How to Use FFI
Last updated at 6:59 pm UTC on 17 September 2019
Squeak allows calling functions in the underlying platform's libraries. These 'ffi' (Foreign Function Interface) calls are analogous to Primitive Methods, but they can call arbitrary functions.
Installation
The FFI package is available via several tools.
The SqueakMap catalog provides access to the latest version as well as several older Squeak specific version. Simply select the one you need and click the Install button as usual.
The package is also available via Metacello - just 'do it' on
Metacello new
configuration: 'FFI';
load.
Installation Squeak 5.3
Through the PreferenceWizardMorph

This uses the Metacello method shown above.
Installation (Squeak 5.0 and later)
(Installer repository: 'http://source.squeak.org/FFI')
install: 'FFI-Pools';
install: 'FFI-Kernel';
install: 'FFI-Tests';
install: 'FFI-Examples'.
or, if you don't want the examples & tests
(Installer repository: 'http://source.squeak.org/FFI')
install: 'FFI-Pools';
install: 'FFI-Kernel'.
Methods invoking external functions
To call an external function, we need to make a method that includes a suitable pragma that specifies the function to use, the library that provides it and the type signature(s) of the function and parameters. This looks very similar to a named primitive method in the simplest cases
For example
testAbs: anInteger
"Simple example of an ffi call to the stdlib"
"This version uses the name for the linux club; different systems use different names just to be a pain"
<cdecl: long 'abs' (long) module: 'libc.so.6' >
^self externalCallFailed
The first line should be the external function specification pragma. Following the external function specification is normal Smalltalk code that is executed if the external function call fails.
It's important to remember that failure here means something went wrong in linking to and calling the external function, not that the function could not do its internal job. Thus, the most common code is simply '^self externalCallFailed.'
The pragma is always of the form
< calltype: returntype 'function name' (list of parameter types) module: 'library name' >
- Like primitive calls the entire thing is wrapped in < and >
- The calltype is always cdecl except on some Windows calls, where it must be apicall; thank you so very much, Microsoft. See https://en.wikipedia.org/wiki/X86_calling_conventions#stdcall for some information on this. In effect this requires anyone writing methods that uses an ffi call to know if the function is considered a Windows API call or not. On all the other platforms both cdecl and apicall work the same.
- The returntype must be one of the list of known parameter types shown below - long, char or double are probably the most common you will see.
- The function name is a literal string (hence the quotes surrounding it) that tells us which function to call.
- The list of parameter types must have one type for each argument of the named function, again, chosen from the list of known parameter types shown below.
- The module name is another literal string that tells us the name of the library or DLL etc to look in when searching for the named function.
Parameter Types
Argument and return types must be names of ExternalTypes. These can be one of the atomic types (see ExternalType class>>initializeFFIConstants and ExternalType class>>initializeAtomicTypes):
- void
- bool
- byte (unsigned)
- sbyte (signed)
- ushort (16-bit unsigned)
- short (16-bit signed)
- ulong (32-bit unsigned)
- long (32-bit signed)
- ulonglong (64-bit unsigned)
- longlong (64-bit signed)
- char (unsigned)
- schar (signed)
- float (single-precision float)
- double (double-precision float)
ExternalTypes can also be structure types, referred to by the name of a subclass of ExternalStructure with '*' appended to indicate that the argument or return is a pointer to that structure. See the next section.
Structure Types
If the function you want to call has a return or argument type that is a structure type that is not already present as a subclass of ExternalStructure, you'll need to create one. The most important bit is to write a class-side method 'fields' that returns an array of field descriptions. Each field description is a 2 or 3 element array, the first element of which is the field name and the second is the type. The optional third element is a number of extra bytes to be added in the structure, presumably and some form of alignment correction? Then send the class #defineFields to initialize it with your new implementation of #fields. Be sure to have for each subclass of ExternalStructure a class-side #initialize method that includes "self defineFields" or the class won't work after being loaded into another image.
For example, a pixel color structure might implemented so:
- first, create a new class named PixelColor in the System Browser (subclass of ExternalStructure)
- add class method 'fields' to color
maybe, like this:
fields
^#((red 'byte')
(green 'byte')
(blue 'byte'))
- in the end, just do a "PixelColor defineFields." in the Workspace
Comments