Skip to content
FutureBlitz

    Install

    C interop, 1 of 7

    Calling C

    Declare C functions in an Extern block and call them like your own, with C's types and out-parameters.

    What you’ll build

    A program that calls functions from the C runtime, which every FutureBlitz program links: abs, strlen, atoi under a name of your own, frexp with an out-parameter, and getenv, which gives back text. It’s the whole of what calling a C library takes: an Extern block that says what each function takes and gives.

    An Extern block

    An Extern "C" block declares C functions, one per line, with their parameters and results in FutureBlitz’s types. There’s no header to include and no wrapper to compile: the program calls the C symbol directly, through C’s own calling convention.

    Import std.core
    
    Extern "C"
        Function abs(n: Int32): Int32
        Function strlen(text: CString): UInt64
    End Extern
    
    Print(abs(-42))
    Print(strlen("FutureBlitz"))
    Output
    42
    11

    An Extern Function is called from ordinary code, like any function. strlen takes a C string, const char*, which is a CString; a String passed for it is copied with a NUL after it for the call. Strings and buffers has more.

    C’s types

    Each C type has a FutureBlitz type of the same size and meaning:

    FutureBlitzC
    Int8 to Int64, UInt8 to UInt64int8_t to int64_t, uint8_t to uint64_t
    CLong, CULong, CWCharlong, unsigned long, wchar_t, at the target’s sizes
    Float32, Float64float, double
    Boolbool
    Ptr<T>, Ptr<Void>T*, void*
    CStringconst char*
    Callback(A, B): Ra function pointer, R (*)(A, B)
    a @CLayout Structa struct

    C’s int is an Int32, and size_t a UInt64, on every target FutureBlitz builds for, which is why strlen gives a UInt64. long differs by OS, so it has a type of its own, CLong:

    Import std.core
    
    Extern "C"
        Function labs(n: CLong): CLong
    End Extern
    
    Print(labs(-1234567))
    Output
    1234567

    A name of your own

    = "symbol" after a declaration calls the C symbol by a different name in your code.

    Import std.core
    
    Extern "C"
        Function ParseInt(text: CString): Int32 = "atoi"
    End Extern
    
    Print(ParseInt("1234") + 1)
    Output
    1235

    Out-parameters

    Many C functions return a second result through a pointer. A ByRef parameter passes the variable’s address, so the call needs no pointers and no Unsafe code. frexp splits a number into a fraction and a power of two, and writes the power through its int*.

    Import std.core
    
    Extern "C"
        Function frexp(x: Float64, ByRef exponent: Int32): Float64
    End Extern
    
    Local exponent: Int32 = 0
    Local fraction = frexp(48.0, ByRef exponent)
    Print("48 is {fraction} * 2^{exponent}")
    Output
    48 is 0.75 * 2^6

    Text from C

    A C function that gives back a const char* can be declared to give a String. The text is copied into a new String right after the call. String? is for one that can give NULL, which becomes Null, as getenv does for a variable that isn’t set.

    Import std.core
    
    Extern "C"
        Function getenv(name: CString): String?
    End Extern
    
    Print(getenv("FB_TUTORIAL_NOT_SET") ?? "not set")
    Output
    not set

    All of it

    Import std.core
    
    Extern "C"
        Function abs(n: Int32): Int32
        Function strlen(text: CString): UInt64
        Function ParseInt(text: CString): Int32 = "atoi"
        Function frexp(x: Float64, ByRef exponent: Int32): Float64
        Function getenv(name: CString): String?
    End Extern
    
    Print(abs(-42), strlen("FutureBlitz"), ParseInt("1234") + 1)
    
    Local exponent: Int32 = 0
    Local fraction = frexp(48.0, ByRef exponent)
    Print("48 is {fraction} * 2^{exponent}")
    Print(getenv("FB_TUTORIAL_NOT_SET") ?? "not set")
    Output
    42 11 1235
    48 is 0.75 * 2^6
    not set

    These functions are in the C runtime. A function from another library names it with Lib, as Native libraries in project.toml shows. A C function that breaks memory when it’s given the wrong arguments is declared Unsafe Function, and only Unsafe code can call it.

    What you learned

    • An Extern "C" block declares C functions, which ordinary code calls like its own.
    • Each C type maps to a FutureBlitz type, with CLong for C’s long.
    • = "symbol" gives a C function a name of your own.
    • A ByRef parameter is an out-parameter, with no pointers needed.
    • A function that gives text can give a String, or a String? when it can give NULL.

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