Skip to main content

ffi.callback

Creates and manages native C function pointers (libffi closures) backed by Lua functions. This is the inverse of lje.call — instead of calling a C function from Lua, you expose a Lua function as a callable C address that native code can invoke.

Functions

ffi.callback.create(sig, handler, on_remove?)

Note: Returns nothing (no values) on failure — check that two values were returned before using the address.

Creates a new libffi closure that wraps a Lua function as a callable C function pointer.

The signature string encodes the calling convention in a compact character format. The first character is the return type; every subsequent character is an argument type, in order. Type characters:

CharC typeLua type
vvoid(no return)
iint32_tnumber
uuint32_tnumber
lint64_tnumber
ffloatnumber
ddoublenumber
buint8_tboolean
pvoid*number (address)
sconst char*string (or number 0 if null)

For example, "ipp" means: returns int32_t, takes two pointer arguments.

The returned userdata is a GC-managed handle. Keep it alive (e.g., in a global) for as long as the native code may call the address. When the handle is garbage-collected, the closure is freed and the address becomes invalid. You can also free it early with lje.callback.remove.

The on_remove function, if provided, is called with no arguments immediately before the closure is freed (whether by GC or by an explicit remove call). Use it to clean up any native-side references to the address.

Thread safety: the closure can only invoke the Lua handler when called from the same OS thread that created it. Calls arriving from other threads silently return a zeroed result without executing Lua code.

Returns nothing if the signature is empty, contains an unknown type character, or if libffi fails to prepare the closure.

Parameters

NameTypeDescription
sigstring

Type signature string. First character is the return type; remaining characters are argument types. Maximum 63 characters total. See the type character table above.

handlerfunction

The Lua function to invoke when the C address is called. Receives each C argument marshalled to a Lua value. If the return type is not v, it must return a compatible value.

on_remove?function

Optional callback invoked (with no arguments) just before the closure is freed, either by GC or by lje.callback.remove.

Returns

TypeDescription
userdata

GC-managed closure handle. Must be kept alive as long as the native address is in use. Freed via GC or lje.callback.remove.

number

The native function pointer address as a number. Pass this to any native API that expects a C callback.

ffi.callback.remove(cb)

Explicitly frees a callback closure before it would be garbage-collected.

If the closure has an on_remove handler, it is called first. After this call the native address is invalid and the userdata handle is permanently destroyed. Calling remove on an already-destroyed handle is safe and returns false.

Parameters

NameTypeDescription
cbuserdata

The callback handle returned by lje.callback.create.

Returns

TypeDescription
boolean

true if the closure was alive and has now been freed; false if it was already destroyed.

ffi.callback.address(cb)

Returns the native function pointer address of a callback handle. Useful when you need the address again after create without storing the second return value separately. Returns 0 if the handle has been destroyed.

Parameters

NameTypeDescription
cbuserdata

The callback handle returned by lje.callback.create.

Returns

TypeDescription
number

The native function pointer address, or 0 if the handle has been destroyed.