C interop (FFI)
CShift’s primary external ABI is the C ABI, and calling C code is meant to feel native.
Hand-written declarations
Section titled “Hand-written declarations”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>).
Importing a whole header
Section titled “Importing a whole header”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 libraryStructs 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.