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: #int
char
string
stringOut
symbol
symbolOut
int
uInt
long
uLong
double
longDouble
void
self
returned from Smalltalk)
wchar
wchar_t
) value
wstring
wchar_t *
), converted to a UnicodeString
wstringOut
wchar_t *
), converted to a UnicodeString and then freed
cObject
smalltalk
#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.
unknown
boolean
char
, which is promoted to int
char
char
, which is promoted to int
wchar
wchar_t
string
char *
stringOut
char *
, the contents are expected to be overwritten
with a new C string, and the object that was passed becomes the new
string on return
wstring
wchar_t *
wstringOut
wchar_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
symbol
char *
byteArray
char *
, even though may contain NUL's
int
int
uInt
unsigned int
long
long
uLong
unsigned long
double
double
longDouble
long double
cObject
void *
.
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 CObject
s 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.
cObjectPtr
void **
. The CObject
is modified on output to reflect the value stored into the passed object.
smalltalk
variadic
variadicSmalltalk
unknown
parameter if variadic
is used,
or passed as a raw object pointer for variadicSmalltalk
.
self
selfSmalltalk
unknown
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
.