FFI: Calling C with UFFI

Pharo's UFFI calls C libraries with a declaration written in C-like syntax inside a literal array:

39.1 Unified FFI

Pharo's UFFI calls C libraries with a declaration written in C-like syntax inside a literal array:

Arguments in the signature are bound by name to the method's arguments (n, aName), or can be self or literals.

Object << #LibCDemo slots: {}; package: 'FFI-Demo'.

LibCDemo class >> abs: n
    ^ self ffiCall: #( int abs ( int n ) ) library: LibC

LibCDemo class >> getenv: aName
    ^ self ffiCall: #( char * getenv ( const char * aName ) ) library: LibC

LibCDemo class >> clock
    ^ self ffiCall: #( long clock ( ) ) library: LibC
LibCDemo abs: -42.            "→ 42"
LibCDemo getenv: 'HOME'.      "→ '/home/ada' (a String; nil if unset)"

39.2 Library classes

FFILibrary << #LibM slots: {}; package: 'FFI-Demo'.

LibM >> unixLibraryName   ^ 'libm.so.6'
LibM >> macLibraryName    ^ 'libm.dylib'
LibM >> win32LibraryName  ^ 'msvcrt.dll'

Object << #MathFFI slots: {}; package: 'FFI-Demo'.
MathFFI class >> ffiLibrary
    ^ LibM
MathFFI class >> cos: x
    ^ self ffiCall: #( double cos ( double x ) )

39.3 Types, structures, memory

int, long, double, float, char, bool same names; also int32, uint8, size_t, int64 String (copied) or ExternalAddress ExternalAddress / void * subclass of FFIExternalStructure subclass of FFIExternalEnumeration subclass of FFIOpaqueObject / FFIExternalObject

FFICallback signature:... block: [...] Memory you allocate (allocate:, externalNew) must be freed — or registered for finalization (autoRelease).

FFIExternalStructure << #Point2D slots: {}; package: 'FFI-Demo'.
Point2D class >> fieldsDesc
    ^ #( double x; double y; )
Point2D rebuildFieldAccessors.        "generates x, x:, y, y:"

| p mem |
p := Point2D new.  p x: 3.0; y: 4.0.
mem := ExternalAddress allocate: 64.
[ "use mem" ] ensure: [ mem free ].

39.4 Notes

Pharo 9+ has threaded FFI (TFFI) so long C calls need not block the VM.

Squeak/Cuis FFI uses pragmas: <cdecl: long 'abs' (long) module: 'libc.so.6'>.

GNU Smalltalk: <cCall: 'abs' returning: #int args: #(#int)>.

VisualWorks uses DLL&C Connect; VA Smalltalk uses PlatformFunctions.

Popular bindings built on UFFI: libgit2 (Iceberg), SDL2, Cairo/Athens, SQLite3, OpenGL, TensorFlow/ONNX experiments.