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
/* 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.fbfb 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.
// 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 ExternEach 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]
name = "measure"
version = "0.1.0"
[native]
m = { system = "m" }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)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:
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]
name = "values"
version = "0.1.0"
type = "library"
[native]
m = { system = "m" }// 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 ExternWhat 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.fbwrites a module of a header’s declarations.- Structs become
@CLayoutStructs, 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.
The rules are in the language reference.
Report an issue with this page or with FutureBlitz on GitHub.