Skip to content
CShift0.32

C interop (FFI)

← Language guide

CShift’s primary external ABI is the C ABI, and calling C code is meant to feel native.

extern "C" int strlen(char* str);
extern "C" void* malloc(uint64 size);
link "m"; // link against a system library (here, libm)

extern "C" declares a C function; link "name" tells the linker to pull in a library (-l<name>).

For a real library, hand-writing every declaration doesn’t scale. using Name from "header.h"; imports a C header’s functions, structs, enums and constants as a namespace, with the types mapped automatically (const char* → string, size_t → nuint, a pointer parameter → ref T, an array in a struct (float m[16]) → Fixed<float32, 16>, and so on):

using Zlib from "zlib.h";
int Main()
{
Console.WriteLine(Zlib.zlibVersion()); // const char* -> string
Console.WriteLine(Zlib.Z_BEST_COMPRESSION); // a macro -> a constant
return 0;
}
cshiftc prog.cshift -lz # still need to link the actual library

Structs passed by value to and from C, and C callbacks, both work — a CShift function can be handed to a C function expecting a callback, using the function pointer types Action/Func:

using Mfb from "MiniFB.h";
void OnKey(Mfb.mfb_window* window, Mfb.mfb_key key, Mfb.mfb_key_mod mod, bool pressed)
{
if (pressed && key == Mfb.mfb_key.KB_KEY_ESCAPE)
Mfb.mfb_close(window);
}
Mfb.mfb_set_keyboard_callback(window, OnKey);

This is a large enough topic to have its own document: the full type mapping table, how pointers and nint/nuint map, ffiApi for umbrella headers, and the current limitations (no C++, no unions/bit fields, no long double, …). The demo-minifb/ and demo-opengl/ projects are complete, runnable examples that import real C libraries this way.

Next: Projects.