Skip to content
FutureBlitz

    Install

    Guide

    C libraries and bindgen

    Extern blocks, the C types, @CLayout Structs, strings, callbacks, Unsafe, and fb bindgen.

    A C library’s functions are declared in an Extern block in your code and called like your own Functions. There are no .decls files and no wrapper to compile: fb links the library, and fb bindgen writes the block from a header.

    Extern

    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)
    Output
    42 11
    48 is 0.75 * 2^6
    1235
    • = "symbol" calls a C function by another name.
    • A String passed for a CString parameter is copied with a NUL after it for the call alone.
    • A ByRef parameter 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 only Unsafe blocks call it.

    Types

    FutureBlitzC
    Int8 to Int64, UInt8 to UInt64int8_t to uint64_t
    CLong, CULong, CWCharlong, unsigned long, wchar_t, the target’s sizes
    Float32, Float64float, double
    Boolbool
    Ptr<T>, Ptr<Void>T*, void*
    CStringconst char*
    Callback(A, B): RR (*)(A, B)
    @CLayout Structa 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}")
    Output
    17 / 5 is 3 remainder 2

    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)
    Output
    [3, 7, 19, 42]

    Pointers are read and written only in Unsafe code, and the rest of the program stays checked.

    fb bindgen

    fb bindgen reads a C header with libclang and writes a 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 unions and varargs, is a // skipped: comment saying why.

    fb bindgen vecmath.h --lib vecmath -o vecmath.fb

    Then [native] in project.toml says where the library is, vecmath = { static = "libs/vecmath" }, and Import "./vecmath" uses it. A static library links into the executable, so the program is still one file. The spec’s FFI chapter has every rule, CData and Buffers too.

    Report an issue with this page or with FutureBlitz on GitHub.