Next: C data types, Previous: External modules, Up: C and Smalltalk
To use the C callout mechanism, you first need to inform Smalltalk about
the C functions that you wish to call. You currently need to do this in
two places: 1) you need to establish the mapping between your C
function's address and the name that you wish to refer to it by, and 2)
define that function along with how the argument objects should be
mapped to C data types to the Smalltalk interpreter. As an example, let
us use the pre-defined (to gnu Smalltalk) functions of system and
getenv.
First, the mapping between these functions and string names for the functions needs to be established in your module. If you are writing an external Smalltalk module (which can look at Smalltalk objects and manipulate them), see Linking your libraries to the virtual machine; if you are using function from a dynamically loaded library, see Dynamic loading.
Second, we need to define a method that will invoke these C functions and describe its arguments to the Smalltalk runtime system. Such a method is defined with a primitive-like syntax, similar to the following example (taken from kernel/CFuncs.st)
system: aString
<cCall: 'system' returning: #int args: #(#string)>
getenv: aString
<cCall: 'getenv' returning: #string args: #(#string)>
These methods were defined on class SystemDictionary, so
that we would invoke it thus:
Smalltalk system: 'lpr README' !
However, there is no special significance to which class receives the method; it could have just as well been Float, but it might look kind of strange to see:
1701.0 system: 'mail [email protected]' !
The various keyword arguments are described below.
cCall: 'system'system. This name
must be exactly the same as the string passed to
defineCFunc.
The name of the method does not have to match the name of the C function;
we could have just as easily defined the selector to be 'rambo:
fooFoo'; it's just good practice to define the method with a similar
name and the argument names to reflect the data types that should be
passed.
returning: #intcharstringstringOutsymbolsymbolOutintuIntlonguLongdoublelongDoublevoidself returned from Smalltalk)
wcharwchar_t) value
wstringwchar_t *), converted to a UnicodeString
wstringOutwchar_t *), converted to a UnicodeString and then freed
cObjectsmalltalk#narrow
before being returned: an example of this feature is given in the
experimental Gtk+ bindings.
args: #(string) args: #(string int int)
The following argument types are supported; see above for details.
unknownbooleanchar, which is promoted to int
charchar, which is promoted to int
wcharwchar_t
stringchar *
stringOutchar *, the contents are expected to be overwritten
with a new C string, and the object that was passed becomes the new
string on return
wstringwchar_t *
wstringOutwchar_t *, the contents are expected to be overwritten
with a new C wide string, and the object that was passed becomes the new
string on return
symbolchar *
byteArraychar *, even though may contain NUL's
intint
uIntunsigned int
longlong
uLongunsigned long
doubledouble
longDoublelong double
cObjectvoid *.
Any class with non-pointer indexed instance variables can be passed as
a #cObject, and GNU Smalltalk will pass the address of the first indexed
instance variable. This however should never be done for functions that
allocate objects, call back into Smalltalk code or otherwise may cause
a garbage collection: after a GC, pointers passed as #cObject may be
invalidated. In this case, it is safer to pass every object as
#smalltalk, or to only pass CObjects that were returned
by a C function previously.
In addition, #cObject can be used for function pointers. These are
instances of CCallable or one of its subclasses. See Smalltalk callbacks for more information on how to create function pointers for
Smalltalk blocks.
cObjectPtrvoid **. The CObject
is modified on output to reflect the value stored into the passed object.
smalltalkvariadicvariadicSmalltalkunknown parameter if variadic is used,
or passed as a raw object pointer for variadicSmalltalk.
selfselfSmalltalkunknown parameter
if self is used or passing the raw object pointer for
selfSmalltalk. Parameters passed this way don't map to the
message's arguments, instead they map to the message's receiver.
Table of parameter conversions:
| Declared param type | Object type | C parameter type used
|
| boolean | Boolean (True, False) | int
|
| byteArray | ByteArray | char *
|
| cObject | CObject | void *
|
| cObject | ByteArray, etc. | void *
|
| cObjectPtr | CObject | void **
|
| char | Boolean (True, False) | int
|
| char | Character | int (C promotion rule)
|
| char | Integer | int
|
| double | Float | double (C promotion)
|
| longDouble | Float | long double
|
| int | Boolean (True, False) | int
|
| int | Integer | int
|
| uInt | Boolean (True, False) | unsigned int
|
| uInt | Integer | unsigned int
|
| long | Boolean (True, False) | long
|
| long | Integer | long
|
| uLong | Boolean (True, False) | unsigned long
|
| uLong | Integer | unsigned long
|
| smalltalk, selfSmalltalk | anything | OOP
|
| string | String | char *
|
| string | Symbol | char *
|
| stringOut | String | char *
|
| symbol | Symbol | char *
|
| unknown, self | Boolean (True, False) | int
|
| unknown, self | ByteArray | char *
|
| unknown, self | CObject | void *
|
| unknown, self | Character | int
|
| unknown, self | Float | double
|
| unknown, self | Integer | long
|
| unknown, self | String | char *
|
| unknown, self | Symbol | char *
|
| unknown, self | anything else | OOP
|
| variadic | Array | each element is passed according to "unknown"
|
| variadicSmalltalk | Array | each element is passed as an OOP
|
| wchar | Character | wchar_t
|
| wstring | UnicodeString | wchar_t *
|
| wstringOut | UnicodeString | wchar_t *
|
When your call-out returns #void, depending on your
application you might consider using asynchronous
call-outs. These are call-outs that do not suspend the process
that initiated them, so the process might be scheduled again,
executing the code that follows the call-out, during the execution
of the call-out itself. This is particularly handy when writing
event loops (the most common place where you call back into Smalltalk)
because then you can handle events that arrive during the
handling of an outer event before the outer event's processing
has ended. Depending on your application this might be correct or
not, of course. In the future, asynchronous call-outs might be
started into a separate thread.
An asynchronous call-out is defined using an alternate primitive-like
syntax, asyncCCall:args:. Note that the returned value parameter
is missing because an asynchronous call-out always returns nil.