Skip to content
FutureBlitz

    Install

    C interop, 7 of 7

    bindgen

    Turn a C header into a FutureBlitz module with fb bindgen, and use the library through it.

    What you’ll build

    A C header with a struct, an enum, two macros, and two functions from the C math library, turned into a FutureBlitz module by fb bindgen, and a program that uses the module. For a real library, whose headers declare hundreds of functions, this is how its Extern block gets written.

    A header

    geom.h
    /* Geometry helpers over C's math library. */
    #define GEOM_VERSION 3
    #define GEOM_EPSILON 1e-9
    
    typedef struct geom_point {
        double x;
        double y;
    } geom_point;
    
    typedef enum geom_unit {
        GEOM_METERS,
        GEOM_FEET,
    } geom_unit;
    
    double hypot(double x, double y);
    double cbrt(double x);

    Generating the module

    fb bindgen geom.h --lib m -o geom.fb

    fb bindgen reads the header with libclang, as the C compiler that links programs would for the target, and writes a module of its declarations. --lib names the [native] library the functions are in, and -o the file to write; without them, both are named after the header.

    geom.fb
    // Generated by fb-bindgen from geom.h for x86_64-w64-mingw32.
    // Run fb-bindgen again instead of editing it.
    
    Public Const GEOM_VERSION: Int32 = 3
    Public Const GEOM_EPSILON: Float64 = 1e-09
    
    @CLayout Public Struct geom_point
        Public Field x: Float64
        Public Field y: Float64
    End Struct
    
    Public Enum geom_unit: UInt32
        GEOM_METERS = 0
        GEOM_FEET = 1
    End Enum
    
    Public Extern "C" Lib "m"
        Function hypot(x: Float64, y: Float64): Float64
        Function cbrt(x: Float64): Float64
    End Extern

    Each struct is a @CLayout Struct, each enum an Enum of its integer type, each macro that’s a number a Const of C’s type for it, and the functions one Public Extern "C" Lib block. Everything is Public, and C’s names are kept. Don’t edit the file: run bindgen again when the header changes.

    Using it

    The module is a file of the package like any other, and [native] names the library its block says.

    project.toml
    [project]
    name = "measure"
    version = "0.1.0"
    
    [native]
    m = { system = "m" }
    main.fb
    Import std.core
    Import measure.geom
    
    Local a = geom_point(0, 0)
    Local b = geom_point(3, 4)
    Print(hypot(b.x - a.x, b.y - a.y))
    Print(cbrt(27.0))
    Print(geom_unit.GEOM_FEET, GEOM_VERSION)
    Output
    5
    3
    GEOM_FEET 3

    A library’s own package usually wraps the generated module in functions and Classes with FutureBlitz names and types, which free what the C code allocates, check its errors, and throw. Its users import the wrapper, and never see the C names.

    What bindgen leaves out

    What can’t map to FutureBlitz is a // skipped: comment where it would be, with the reason, and a warning. A header with a union:

    value.h
    typedef union geom_value {
        int as_int;
        float as_float;
    } geom_value;
    
    double geom_scale(double x, double by);

    made into a module with fb bindgen value.h --lib m -o value.fb, gives one without the union, which a library package can check:

    project.toml
    [project]
    name = "values"
    version = "0.1.0"
    type = "library"
    
    [native]
    m = { system = "m" }
    value.fb
    // Generated by fb-bindgen from value.h for x86_64-w64-mingw32.
    // Run fb-bindgen again instead of editing it.
    
    // skipped: union geom_value, a union, which FutureBlitz hasn't
    
    Public Extern "C" Lib "m"
        Function geom_scale(x: Float64, by: Float64): Float64
    End Extern

    What it skips: unions, bit fields, packed structs, varargs functions like printf, function-like macros, and C globals, among others. A function that takes one of these is skipped too.

    --target reads the header as it would be compiled for another target, and -I and -D pass include folders and macros, as a C compiler takes them.

    What you learned

    • fb bindgen header.h --lib name -o module.fb writes a module of a header’s declarations.
    • Structs become @CLayout Structs, enums Enums, number macros Consts, and functions an Extern block.
    • The generated module is used like any other, with its library in [native].
    • What can’t map is skipped, with a comment saying why.

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