Cinder by Example

Cinder is small programming language for low-level development.

FFI: Calling C

extern fn declares a function defined outside the program. It links against

the system C library by default.

use "std/io.cnd";

extern fn abs(n: i32) -> i32;

fn main() -> i32 {
    let v = abs(-42);
    print(&v, .I32);
    putchar(10);
    return 0;
}
$ ./extern
42

The example above links to libc: abs is resolved at link time.

c-strings

The c"..." literal produces a []u8 with a trailing zero byte, the form C

functions expect. c"hello" can be passed directly to C functions that accept

a pointer - the trailing null is part of the string.

// docs: skip
use "std/io.cnd";

extern fn my_strlen(s: *u8) -> usize;

fn main() -> i32 {
    let msg = c"hello, libc!";

    let len: u64 = my_strlen(msg) as u64;
    print(&len, .U64);
    putchar(10);
    return 0;
}
$ ./cstr
13

Pointers and memory

C functions that return allocations come back as raw pointers. Dereferencing

and indexing them is unsafe.

use "std/io.cnd";

extern fn malloc(size: usize) -> *u8;
extern fn free(p: *u8);

fn main() -> i32 {
    let p = malloc(64);
    unsafe {
        p[0] = 65;
        p[1] = 66;
        p[2] = 67;
        p[3] = 0;
        let slice = p[..3];
        print(&slice, .S);
    }
    putchar(10);
    free(p);
    return 0;
}
$ ./malloc
ABC

Layout compatibility

Structs use the C ABI: sizes, alignments, and offsets match C on the same

target. Use sizeof/offsetof to stay in sync (see

Compile-Time Code).

struct timespec {
    tv_sec: i64;
    tv_nsec: i64;
}

Variadic functions

An ellipsis ... marks the variadic part of an extern signature. This is how

printf is declared in the standard library:

// docs: skip
extern fn printf(fmt: *u8, ...) -> i32;

Call it like C:

// docs: skip
printf(c"%s\n", p);

Use fflush(null) after printf when mixing with Cinder's buffered output.