Guide
Using a C library
Declaring a C library's functions, linking it from project.toml, its types and callbacks, and fb bindgen.
A C library’s functions are declared in an Extern block and called like your own Functions.
There’s no wrapper to compile: fb links the library, and fb bindgen writes the block from its
header.
1. Declare its functions
Extern "C" Lib "name" names one of the package’s [native] libraries; without Lib, the
symbols come from the C runtime and the system libraries every program links, as these do:
Import std.core
Extern "C"
Function abs(n: Int32): Int32
Function strlen(text: CString): UInt64
Function frexp(x: Float64, ByRef exponent: Int32): Float64
Function CAtoi(text: CString): Int32 = "atoi"
End Extern
Print(abs(-42), strlen("FutureBlitz"))
Local exponent: Int32 = 0
Local mantissa = frexp(48.0, ByRef exponent)
Print("48 is {mantissa} * 2^{exponent}")
Print(CAtoi("1234") + 1)42 11 48 is 0.75 * 2^6 1235
= "symbol"calls a C function by another name.- A
Stringpassed for aCStringparameter is copied with a NUL after it for the call alone. - A
ByRefparameter passes the variable’s address, so out-parameters need no Unsafe code. - An Extern Function is called from safe code; one whose misuse could break memory whatever it’s
given is declared
Unsafe Function, and onlyUnsafeblocks call it.
2. Link the library
Any other library is named in project.toml’s [native], and its Extern block says Lib and
that name. Here it’s C’s math library, which every system has:
[project]
name = "roots"
version = "0.1.0"
[native]
m = { system = "m" }Import std.core
Extern "C" Lib "m"
Function cbrt(x: Float64): Float64
Function hypot(x: Float64, y: Float64): Float64
End Extern
Print(cbrt(27.0))
Print(hypot(3.0, 4.0))3 5
A library comes in one of three forms:
static, assqlite3 = { static = "libs/sqlite3" }, links an archive into the executable, so the program is still one file:libs/libsqlite3.a,libs/sqlite3.a, orlibs/sqlite3.lib.system, asm = { system = "m" }, is a library the system has, found by its name.dynamic, ascodec = { dynamic = "native/{target}/codec" }, is a shared library that fb copies beside the executable, one for each target.
project.toml has every form.
3. Match C’s types
| FutureBlitz | C |
|---|---|
Int8 to Int64, UInt8 to UInt64 | int8_t to uint64_t |
CLong, CULong, CWChar | long, unsigned long, wchar_t, the target’s sizes |
Float32, Float64 | float, double |
Bool | bool |
Ptr<T>, Ptr<Void> | T*, void* |
CString | const char* |
Callback(A, B): R | R (*)(A, B) |
@CLayout Struct | a struct, by value or through a Ptr |
A @CLayout Struct has its Fields where C puts them, so it passes by value as the target’s C ABI
does, as clang would.
Import std.core
@CLayout Struct DivResult
Field quot: Int32
Field rem: Int32
End Struct
Extern "C"
Function div(numerator: Int32, denominator: Int32): DivResult
End Extern
Local r = div(17, 5)
Print("17 / 5 is {r.quot} remainder {r.rem}")17 / 5 is 3 remainder 2
4. Pass callbacks
A Callback parameter takes a top-level Function whose types match, which C can call back.
Import std.core
Extern "C"
Function qsort(items: Ptr<Void>, count: UInt64, size: UInt64,
compare: Callback(Ptr<Void>, Ptr<Void>): Int32)
End Extern
Function Ascending(a: Ptr<Void>, b: Ptr<Void>): Int32
Unsafe
Local x = (a As Ptr<Int32>).Value
Local y = (b As Ptr<Int32>).Value
Return x < y ? -1 : (x > y ? 1 : 0)
End Unsafe
End Function
Local numbers: Int32[] = [42, 7, 19, 3]
Unsafe
qsort(AddressOf(numbers[0]), 4, 4, Ascending)
End Unsafe
Print(numbers)[3, 7, 19, 42]
Pointers are read and written only in Unsafe code, and the rest of the program stays checked.
5. Let fb bindgen write it
For a library of many functions, fb bindgen reads its header and
writes the module: a @CLayout Struct for each struct, an Enum or Consts for each enum, a Const
for each macro that’s a number, and one Public Extern "C" Lib block of its functions. What
can’t map, like a union or varargs, is a // skipped: comment that says why.
fb bindgen libs/sqlite3.h --lib sqlite3 -o sqlite3.fbImport "./sqlite3" then uses it. Read through what it wrote: a function whose misuse could break
memory whatever it’s given, like one that writes through a pointer, is better declared
Unsafe Function, so only Unsafe code calls it.
The language reference’s C interop chapter has every rule, with std.ffi’s CData and Buffer for memory C owns.
Report an issue with this page or with FutureBlitz on GitHub.