Skip to content

C interop

Zap can call C libraries without rewriting them. The boundary must describe the C ABI exactly.

Declare a function supplied by C with ext fun:

ext fun qsort(
base: *Void,
count: Int,
elementSize: Int,
compare: *fun(*Void, *Void) Int32
) Void;

Calling functions that use raw pointers belongs in unsafe.

Use @repr("C") for a struct crossing the boundary:

@repr("C")
struct LegacyJob {
id: Int32,
priority: Int32,
}

The field types and order must match the C declaration.

Use @extern("C") when C code will call a Zap function:

@extern("C")
fun compareJobs(left: *Void, right: *Void) Int32 {
unsafe {
var a: LegacyJob = *(left as *LegacyJob);
var b: LegacyJob = *(right as *LegacyJob);
if a.priority < b.priority { return -1; }
if a.priority > b.priority { return 1; }
}
return 0;
}

The function pointer type in Zap must match the callback type expected by C.

fun sortJobs(ref jobs: [3]LegacyJob) {
unsafe {
var compare: *fun(*Void, *Void) Int32 = compareJobs;
qsort(&jobs[0] as *Void, 3, sizeof(LegacyJob), compare);
}
}

Declare a C global with ext var:

ext var errno: Int;

Declare a C variadic function with an untyped trailing ...:

ext fun printf(format: String, ...) Int;

The compiler cannot validate C format strings or variadic argument representations.

Keep libraries and search paths in thor.toml:

flags = "-L ./vendor/lib -l legacy"

Then build normally with thor build. Use -l name for a library and -L path for a library directory. Keep C details behind a small Zap module so the rest of the program can use ordinary safe types.