Foreign Function Interface (FFI) for the Ring programming language
Built on libffi - A portable Foreign Function Interface library
- π Dynamic Library Loading: Load shared libraries (.so, .dll, .dylib) at runtime
- π C Function Calls: Call C functions directly from Ring with automatic type conversion
- π’ Pointer Operations: Pointer arithmetic, dereferencing, read/write access
- ποΈ Struct Support: Define and manipulate C structs with named field access
- π Union Support: Define and use C unions
- π’ Enum Support: Define and access C enumerations
- π Callbacks: Pass Ring functions as C callbacks
- π§ Memory Management: Allocate and manage C memory directly
- π C String Handling: Seamless conversion between Ring and C strings
- π Cross-Platform: Works on Windows, Linux, macOS, and FreeBSD
- β‘ High-Level OOP API: Clean
FFIclass with intuitive method names
ringpm install ring-cffi from ysdragonload "cffi.ring"
# Load libc and call printf
ffi = new FFI {
loadLib("libc.so.6") # or msvcrt.dll on Windows
oPrintf = varFunc("printf", "int", ["string"])
varcall(oPrintf, ["Hello from Ring! Value: %d\n", 42])
}load "cffi.ring"
ffi = new FFI
# Load a shared library
ffi.loadLib("libm.so.6") # Math library on Linux
# ffi.loadLib("msvcrt.dll") # C runtime on Windows
# ffi.loadLib("libSystem.B.dylib") # macOS system library
# Call a simple function (sqrt)
oSqrt = ffi.cFunc("sqrt", "double", ["double"])
result = ffi.invoke(oSqrt, [25.0])
? "sqrt(25) = " + result # 5.0
# Call printf (variadic function)
oPrintf = ffi.varFunc("printf", "int", ["string"])
ffi.varcall(oPrintf, ["Hello, %s! Number: %d\n", "World", 123])load "cffi.ring"
ffi = new FFI
# Allocate memory for an integer
pInt = ffi.alloc("int")
# Write a value to the pointer
ffi.ptrSet(pInt, "int", 42)
# Read the value back
? ffi.ptrGet(pInt, "int") # 42
# Pointer arithmetic
pNext = ffi.offset(pInt, 4) # Move 4 bytes forward
# Check for NULL
pNull = ffi.nullptr()
? ffi.isNullPtr(pNull) # 1 (true)
# Get size of a type
? ffi.sizeof("double") # 8load "cffi.ring"
ffi = new FFI("libc.so.6")
# Create a C string
cStr = ffi.string("Hello from Ring!")
# Read it back
? ffi.toString(cStr) # "Hello from Ring!"load "cffi.ring"
ffi = new FFI
# Define a Point struct
point = ffi.defineStruct("Point", [
["x", "int"],
["y", "int"],
["name", "string"]
])
# Allocate a new struct instance
pPoint = ffi.structNew(point)
# Set field values
ffi.ptrSet(ffi.fieldPtr(pPoint, point, "x"), "int", 10)
ffi.ptrSet(ffi.fieldPtr(pPoint, point, "y"), "int", 20)
ffi.ptrSet(ffi.fieldPtr(pPoint, point, "name"), "string", "MyPoint")
# Read field values
? "x = " + ffi.field(pPoint, point, "x") # 10
? "y = " + ffi.field(pPoint, point, "y") # 20
? "name = " + ffi.field(pPoint, point, "name") # "MyPoint"
# Get struct info
? "Size: " + ffi.structSize(point) + " bytes"
? "Offset of y: " + ffi.fieldOffset(point, "y")load "cffi.ring"
ffi = new FFI
# Define a union
data = ffi.defineUnion("Data", [
["i", "int"],
["f", "float"],
["c", "char"]
])
# Allocate and use
pData = ffi.unionNew(data)
ffi.ptrSet(pData, "int", 42)
? ffi.field(pData, data, "i") # 42
? "Union size: " + ffi.unionSize(data) # Size of largest memberload "cffi.ring"
ffi = new FFI
# Define an enum
colors = ffi.enum("Colors", [
["RED", 0],
["GREEN", 1],
["BLUE", 2]
])
? ffi.enumValue(colors, "RED") # 0
? ffi.enumValue(colors, "GREEN") # 1
? ffi.enumValue(colors, "BLUE") # 2load "cffi.ring"
ffi = new FFI("libc.so.6")
# Create a C callback
cb = ffi.callback("myCompare", "int", ["ptr", "ptr"])
# Pass callback to C function (e.g., qsort)
oQsort = ffi.cFunc("qsort", "void", ["ptr", "size_t", "size_t", "ptr"])
# ... use with array sorting
# Define a Ring function to use as callback
func myCompare(pA, pB)
nA = cffi_get(pA, "int")
nB = cffi_get(pB, "int")
if nA < nB return -1 ok
if nA > nB return 1 ok
return 0load "cffi.ring"
ffi = new FFI("libc.so.6")
# Get a function pointer directly
pFunc = ffi.sym("strlen")
# Create callable from pointer
oFunc = ffi.funcPtr(pFunc, "int", ["string"])
result = ffi.invoke(oFunc, ["Hello"])
? "strlen(Hello) = " + resultload "cffi.ring"
ffi = new FFI
# Get error information (errno is set by previous C calls)
? "Errno: " + ffi.errno()
? "Error: " + ffi.strError()| Method | Taken Arguments | Description |
|---|---|---|
new FFI[(cPath]] |
cPath: string (optional) β library path |
Create FFI instance, optionally loading a library |
loadLib(cPath) |
cPath: string β library path |
Load a shared library |
library() |
(none) | Get the raw library handle |
cFunc(cName, cRetType, [aArgTypes]) |
cName: string β function name, cRetType: string β return type, aArgTypes: list (optional) β argument type strings |
Create a C function wrapper |
funcPtr(pPtr, cRetType, [aArgTypes]) |
pPtr: pointer β function pointer, cRetType: string β return type, aArgTypes: list (optional) β argument type strings |
Create wrapper from function pointer |
varFunc(cName, cRetType, [aArgTypes]) |
cName: string β function name, cRetType: string β return type, aArgTypes: list (optional) β fixed argument type strings (fixed count inferred from list length) |
Create variadic function wrapper |
alloc(cType) |
cType: string β type name |
Allocate memory for a single C type |
allocArray(cType, nCount) |
cType: string β type name, nCount: number β array element count |
Allocate memory for an array of C types |
sizeof(cType) |
cType: string β type name |
Get size of a C type in bytes |
nullptr() |
(none) | Create a NULL pointer |
isNullPtr(pPtr) |
pPtr: pointer β pointer to check |
Check if pointer is NULL |
ptrGet(pPtr, cType) |
pPtr: pointer β memory pointer, cType: string β type name |
Read value from pointer |
ptrSet(pPtr, cType, value) |
pPtr: pointer β memory pointer, cType: string β type name, value: any β value to write |
Write value to pointer |
deref(pPtr) |
pPtr: pointer β pointer to dereference |
Dereference a pointer, returning the pointed-to pointer |
derefTyped(pPtr, cType) |
pPtr: pointer β pointer to dereference, cType: string β type name |
Dereference a pointer with explicit type |
offset(pPtr, nOffset) |
pPtr: pointer β base pointer, nOffset: number β byte offset |
Offset a pointer by bytes |
invoke(oFunc, [aArgs]) |
oFunc: pointer β function wrapper, aArgs: list (optional) β arguments |
Call a C function wrapper |
varcall(oFunc, [aArgs]) |
oFunc: pointer β variadic function wrapper, aArgs: list (optional) β arguments |
Call a variadic function wrapper |
fieldPtr(pStruct, oStruct, cField) |
pStruct: pointer β struct instance, oStruct: pointer β struct definition, cField: string β field name |
Get pointer to struct field |
toString(pPtr) |
pPtr: pointer β C string pointer |
Read null-terminated C string |
string(cString) |
cString: string β Ring string |
Create C string from Ring string |
sym(cName) |
cName: string β symbol name |
Look up symbol in loaded library |
defineStruct(cName, aFields) |
cName: string β struct name, aFields: list β field definitions [["name", "type"], ...] |
Define a C struct |
structNew(oStruct) |
oStruct: pointer β struct definition |
Allocate struct instance |
field(pStruct, oStruct, cField) |
pStruct: pointer β struct instance, oStruct: pointer β struct definition, cField: string β field name |
Get pointer to struct field |
fieldOffset(oStruct, cField) |
oStruct: pointer β struct definition, cField: string β field name |
Get byte offset of field |
structSize(oStruct) |
oStruct: pointer β struct definition |
Get struct size in bytes |
defineUnion(cName, aFields) |
cName: string β union name, aFields: list β field definitions [["name", "type"], ...] |
Define a C union |
unionNew(oUnion) |
oUnion: pointer β union definition |
Allocate union instance |
unionSize(oUnion) |
oUnion: pointer β union definition |
Get union size in bytes |
enum(cName, aVariants) |
cName: string β enum name, aVariants: list β variant definitions [["NAME", value], ...] |
Define a C enum |
enumValue(oEnum, cVariant) |
oEnum: pointer β enum definition, cVariant: string β variant name |
Get enum variant value |
callback(cRingFunc, cRetType, [aArgTypes]) |
cRingFunc: string β Ring function name, cRetType: string β return type, aArgTypes: list (optional) β argument type strings |
Create C callback |
cdef(cDef) |
cDef: string β C definition source |
Parse C definition string |
errno() |
(none) | Get last C errno value |
strError() |
(none) | Get error string for errno |
These are the underlying native C extension functions exposed to Ring via RING_API_REGISTER:
| Ring Function | Taken Arguments | Description |
|---|---|---|
cffi_load(cPath) |
cPath: string β library path |
Load a shared library |
cffi_new(cType, [nCount]) |
cType: string β type name, nCount: number (optional) β array element count |
Allocate memory for a C type |
cffi_sizeof(cType) |
cType: string β type name |
Get size of a C type in bytes |
cffi_nullptr() |
(none) | Create a NULL pointer |
cffi_isnull(pPtr) |
pPtr: pointer β pointer to check |
Check if pointer is NULL |
cffi_string(cStr) |
cStr: string β Ring string |
Create a C string from Ring string |
cffi_tostring(pPtr) |
pPtr: pointer β C string pointer |
Convert C string to Ring string |
cffi_errno() |
(none) | Get last C errno value |
cffi_strerror([nErr]) |
nErr: number (optional) β error code |
Get error string for errno |
cffi_func(pLib, cName, cRetType, [aArgTypes]) |
pLib: pointer β library handle, cName: string β function name, cRetType: string β return type, aArgTypes: list (optional) β argument type strings |
Create a C function wrapper |
cffi_funcptr(pPtr, cRetType, [aArgTypes]) |
pPtr: pointer β function pointer, cRetType: string β return type, aArgTypes: list (optional) β argument type strings |
Create wrapper from function pointer |
cffi_invoke(oFunc, [aArgs]) |
oFunc: pointer β function handle, aArgs: list (optional) β arguments |
Call a C function wrapper |
cffi_sym(pLib, cName) |
pLib: pointer β library handle, cName: string β symbol name |
Look up symbol in loaded library |
cffi_get(pPtr, cType, [nIndex]) |
pPtr: pointer β memory pointer, cType: string β type name, nIndex: number (optional) β array index |
Read value from pointer |
cffi_set(pPtr, cType, value, [nIndex]) |
pPtr: pointer β memory pointer, cType: string β type name, value: any β value to write, nIndex: number (optional) β array index |
Write value to pointer |
cffi_deref(pPtr, [cType]) |
pPtr: pointer β pointer to dereference, cType: string (optional) β type name |
Dereference a pointer |
cffi_offset(pPtr, nOffset) |
pPtr: pointer β base pointer, nOffset: number β byte offset |
Offset a pointer by bytes |
cffi_struct(cName, [aFields]) |
cName: string β struct name, aFields: list (optional) β field definitions [["name", "type"], ...] |
Define a C struct |
cffi_typeof(cName) |
cName: string β type/struct/union/enum name |
Get type handle by name |
cffi_struct_new(pType) |
pType: pointer β struct definition |
Allocate struct instance |
cffi_field(pPtr, pType, cField) |
pPtr: pointer β struct/union instance, pType: pointer β type definition, cField: string β field name |
Get pointer to struct field |
cffi_field_offset(pType, cField) |
pType: pointer β struct type, cField: string β field name |
Get byte offset of field |
cffi_struct_size(pType) |
pType: pointer β struct type |
Get struct size in bytes |
cffi_callback(cFunc, cRetType, [aArgTypes]) |
cFunc: string β Ring function name, cRetType: string β return type, aArgTypes: list (optional) β argument type strings |
Create C callback |
cffi_enum(cName, aConsts) |
cName: string β enum name, aConsts: list β constant definitions [["NAME", value], ...] |
Define a C enum |
cffi_enum_value(pEnum, cName) |
pEnum: pointer β enum handle, cName: string β constant name |
Get enum variant value |
cffi_union(cName, [aFields]) |
cName: string β union name, aFields: list (optional) β field definitions [["name", "type"], ...] |
Define a C union |
cffi_union_new(pType) |
pType: pointer β union definition |
Allocate union instance |
cffi_union_size(pType) |
pType: pointer β union type |
Get union size in bytes |
cffi_varfunc(pLib, cName, cRetType, [aArgTypes]) |
pLib: pointer β library handle, cName: string β function name, cRetType: string β return type, aArgTypes: list (optional) β fixed argument type strings (fixed count inferred from list length) |
Create variadic function wrapper |
cffi_varcall(oFunc, [aArgs]) |
oFunc: pointer β variadic function handle, aArgs: list (optional) β arguments |
Call a variadic function wrapper |
cffi_cdef(pLib, cDeclarations) |
pLib: pointer β library handle (can be NULL), cDeclarations: string β C definition source |
Parse C definition string |
All type aliases recognized by the parser in parse_type_kind():
| Type(s) | Description | Size |
|---|---|---|
int8, int8_t, Sint8 |
8-bit signed int | 1 byte |
uint8, uint8_t, Uint8, byte |
8-bit unsigned int | 1 byte |
int16, int16_t, Sint16 |
16-bit signed int | 2 bytes |
uint16, uint16_t, Uint16 |
16-bit unsigned int | 2 bytes |
int32, int32_t, Sint32 |
32-bit signed int | 4 bytes |
uint32, uint32_t, Uint32 |
32-bit unsigned int | 4 bytes |
int64, int64_t, Sint64 |
64-bit signed int | 8 bytes |
uint64, uint64_t, Uint64 |
64-bit unsigned int | 8 bytes |
| Type(s) | Description | Size |
|---|---|---|
char |
Character | 1 byte |
signed char, schar |
Signed character | 1 byte |
unsigned char, uchar |
Unsigned character | 1 byte |
short, short int, signed short |
Short integer | 2 bytes |
unsigned short, ushort, unsigned short int |
Unsigned short integer | 2 bytes |
int, signed, signed int |
Signed integer | 4 bytes |
unsigned, unsigned int, uint |
Unsigned integer | 4 bytes |
long, long int, signed long |
Long integer | platform-dependent |
ulong, unsigned long, unsigned long int |
Unsigned long integer | platform-dependent |
long long, long long int, signed long long |
Long long integer | 8 bytes |
unsigned long long, ulonglong, unsigned long long int |
Unsigned long long integer | 8 bytes |
float |
Single precision float | 4 bytes |
double |
Double precision float | 8 bytes |
long double |
Extended precision float | platform-dependent |
| Type(s) | Description | Size |
|---|---|---|
ptr, pointer, void* |
Generic pointer | platform-dependent |
string, char*, cstring |
Null-terminated C string | platform-dependent |
| Type(s) | Description | Size |
|---|---|---|
size_t |
Unsigned size type | platform-dependent |
ssize_t |
Signed size type | platform-dependent |
ptrdiff_t |
Pointer difference type | platform-dependent |
intptr_t |
Signed integer pointer type | platform-dependent |
uintptr_t |
Unsigned integer pointer type | platform-dependent |
| Type(s) | Description | Size |
|---|---|---|
void |
No return value | β |
bool, _Bool |
Boolean type | 1 byte |
wchar_t |
Wide character type | platform-dependent |
Note: The parser also strips
const,volatile, andrestrictqualifiers. Pointer suffixes (e.g.int**) are handled automatically by counting trailing*characters in any type string.
Check the examples/ directory for usage examples covering:
| # | Example | Description |
|---|---|---|
| 01 | π₯ Library Loading | cffi_load β loading shared libraries |
| 02 | π Function Calls | cffi_func β calling C functions |
| 03 | πΎ Memory Allocation | cffi_new β allocating C memory |
| 04 | π’ Pointer Operations | cffi_get, cffi_set, cffi_deref, cffi_offset |
| 05 | π C Strings | cffi_string, cffi_tostring β string handling |
| 06 | π Symbol Lookup | cffi_sym β resolving library symbols |
| 07 | π― Function Pointers | cffi_funcptr β wrapping raw function pointers |
| 08 | ποΈ Structs | cffi_struct, cffi_struct_new, cffi_field β C structs |
| 09 | π Unions | cffi_union, cffi_union_new β C unions |
| 10 | π’ Enums | cffi_enum, cffi_enum_value β C enumerations |
| 11 | π Callbacks | cffi_callback β Ring functions as C callbacks |
| 12 | β‘ Variadic Functions | cffi_varfunc, cffi_varcall β variadic C calls |
| 13 | π C Definition Parser | cffi_cdef β parsing C declarations |
| 14 | π Sizeof | cffi_sizeof β getting type sizes |
| 15 | cffi_errno, cffi_strerror β C errors |
|
| 16 | π Typeof | cffi_typeof β runtime type queries |
| 17β18 | π§© OOP Basics | High-level FFI class β loading and calling |
| 19β20 | π» OOP Memory | High-level FFI class β memory and pointers |
| 21 | ποΈ OOP Structs/Unions | High-level FFI class β structs and unions |
| 22 | π§Ή OOP Misc | High-level FFI class β misc operations |
| 23 | π OOP qsort | Complete qsort callback example |
- CMake: Version 3.16 or higher
- C Compiler: GCC, Clang, or MSVC
- Ring Source Code: Ring language source code
- Git: For cloning submodules
-
Clone the Repository:
git clone --recursive https://github.com/ysdragon/ring-cffi.git
-
Set the
RINGEnvironment Variable:# Linux/macOS/FreeBSD export RING=/path/to/ring # Windows set RING=X:\path\to\ring
-
Build:
mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release cmake --build .
The compiled library will be in lib/<os>/<arch>/.
This project is licensed under the MIT License - see the LICENSE file for details.