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 help-smalltalk@gnu.org' !
The various keyword arguments are described below.
cCall: 'system'
This says that we are defining the C function 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
This defines the C data type that will be returned. It is converted to the corresponding Smalltalk data type. The set of valid return types is:
char
Single C character value
string
A C char *, converted to a Smalltalk string
stringOut
A C char *, converted to a Smalltalk string and then freed.
symbol
A C char *, converted to a Smalltalk symbol
symbolOut
A C char *, converted to a Smalltalk symbol and then freed.
int
A C int value
uInt
A C unsigned int value
long
A C long value
uLong
A C unsigned long value
double
A C double, converted to an instance of FloatD
longDouble
A C long double, converted to an instance of FloatQ
void
No returned value (self
returned from Smalltalk)
wchar
Single C wide character (wchar_t
) value
wstring
Wide C string (wchar_t *
), converted to a UnicodeString
wstringOut
Wide C string (wchar_t *
), converted to a UnicodeString and then freed
cObject
An anonymous C pointer; useful to pass back to some C function later
smalltalk
An anonymous (to C) Smalltalk object pointer; should have been passed to C at some point in the past or created by the program by calling other public GNU Smalltalk functions (see Smalltalk types).
ctype
You can pass an instance of CType or one of its subclasses (see C data types). In this case the object will be sent #narrow
before being returned: an example of this feature is given in the
experimental Gtk+ bindings.
args: #(#string)
This is an array of symbols that describes the types of the arguments in order. For example, to specify a call to open(2), the arguments might look something like:
args: #(#string #int #int)
The following argument types are supported; see above for details.
unknown
Smalltalk will make the best conversion that it can guess for this object; see the mapping table below
boolean
passed as char
, which is promoted to int
char
passed as char
, which is promoted to int
wchar
passed as wchar_t
string
passed as char *
byteArrayOut
passed as char *
. The contents are expected to be overwritten
with a new C string, and copied back to the object that was passed
on return from the C function
stringOut
passed as 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
passed as wchar_t *
wstringOut
passed as 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
passed as char *
byteArray
passed as char *
, even though may contain NUL’s
int
passed as int
uInt
passed as unsigned int
long
passed as long
uLong
passed as unsigned long
double
passed as double
longDouble
passed as long double
cObject
C object value passed as 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
Pointer to C object value passed as void **
. The CObject
is modified on output to reflect the value stored into the passed object.
smalltalk
Pass the object pointer to C. The C routine should treat the value as a pointer to anonymous storage. This pointer can be returned to Smalltalk at some later point in time.
variadic
variadicSmalltalk
an Array is expected, each of the elements of the array will be
converted like an unknown
parameter if variadic
is used,
or passed as a raw object pointer for variadicSmalltalk
.
self
selfSmalltalk
Pass the receiver, converting it to C like an 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
.
Next: C data types, Previous: External modules, Up: C and Smalltalk