Next: , Previous: , Up: C and Smalltalk  


5.2 Using the C callout mechanism

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 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.

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 typeObject typeC parameter type used
booleanBoolean (True, False)int
byteArrayByteArraychar *
cObjectCObjectvoid *
cObjectByteArray, etc.void *
cObjectPtrCObjectvoid **
charBoolean (True, False)int
charCharacterint (C promotion rule)
charIntegerint
doubleFloatdouble (C promotion)
longDoubleFloatlong double
intBoolean (True, False)int
intIntegerint
uIntBoolean (True, False)unsigned int
uIntIntegerunsigned int
longBoolean (True, False)long
longIntegerlong
uLongBoolean (True, False)unsigned long
uLongIntegerunsigned long
smalltalk, selfSmalltalkanythingOOP
stringStringchar *
stringSymbolchar *
stringOutStringchar *
symbolSymbolchar *
unknown, selfBoolean (True, False)int
unknown, selfByteArraychar *
unknown, selfCObjectvoid *
unknown, selfCharacterint
unknown, selfFloatdouble
unknown, selfIntegerlong
unknown, selfStringchar *
unknown, selfSymbolchar *
unknown, selfanything elseOOP
variadicArrayeach element is passed according to "unknown"
variadicSmalltalkArrayeach element is passed as an OOP
wcharCharacterwchar_t
wstringUnicodeStringwchar_t *
wstringOutUnicodeStringwchar_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: , Previous: , Up: C and Smalltalk