Foreign Function Interface (FFI)
Part of: Reef Language Reference Last reviewed on version: 0.10.0
Extern Declarations
Declaring C Functions
extern "C" fn strlen(s: string): size_t
extern "C" fn printf(format: string): int
No function body - implementation in C library.
Type Mapping
Reef → C:
int→int32_tint8→int8_tint64→int64_tuint32→uint32_tstring→char*bool→boolfloat→doublefloat32→floatsize_t→size_tpointer→void*
Strings across the FFI boundary (since 0.8.2)
Reef strings carry a heap-block header with an O(1) logical length; raw C strings do not. The compiler bridges the gap automatically in one direction:
- C returning
stringto Reef: the result of every user-declaredextern "C" fn ...: stringcall is boundary-copied into a Reef heap string (reef_string_dup_c). Your C function may return a static buffer, a pointer intoenviron, a string literal, ormalloc'd memory — Reef takes a value snapshot at the call site. Two consequences: the returned Reef string does not change if your C code later rewrites its buffer, and each call costs one copy (keep chatty FFI onint/pointerreturns if that matters). - C filling a Reef
stringargument (agetcwd-style out-buffer): the buffer must not be a string literal — literals live in.rodataand a write through them faults (or, for some syscalls, fails withEFAULTand a silent wrong answer). Allocate a writable heap buffer withreef_string_alloc(n)(or equivalent) and pass that. After the fill, the buffer's logical length is stale: callstr.sync_length(buf)(orstr.set_length(buf, n)when the C side reports a length) before reading it in safe context. Runtime-owned fillers that already touch the heap header (reef_getcwd_safe,reef_readdir_name,reef_tls_recv,reef_string_copy_range,reef_fgets, plusstr.set_length/str.sync_length) refuse aREEF_FLAG_STATIC_LITERALbuffer with a clean panic (reef-lang#71). Raw libc writers such asstrcpyare not guarded — do not pass a literal as their destination. - Freestanding targets (
--target *-baremetal): no boundary copy is emitted; your allocator/FFI owns the header contract (seereef_baremetal.h's allocator notes).
Example
extern "C" fn strlen(s: string): size_t
proc main()
let len: size_t = strlen("Hello")
println("String length calculated")
end main
Linking: Reef automatically links with the C standard library.
Known libc signatures (since reef-lang#72)
For a fixed set of libc names (string/stdio/stdlib/math/ctype, including
ctype macros such as isxdigit), reefc keeps a name→signature table.
A mismatching extern "C" is rejected at --check with both the declared
and expected Reef signatures. Codegen still suppresses the emitted C
extern for those names (system headers / macros / const·restrict) —
do not re-emit prototypes for table names.
size_t parameters and returns must be spelled size_t (not int).
char* may be declared as string or pointer. Names not in the
table still rely on the C compiler's conflicting-types check when an
extern is emitted.
Safety Considerations
- Type mismatches between Reef and C can cause crashes
- For tabled libc names, reefc checks the declaration; for other names, ensure extern declarations match actual C signatures
- String lifetimes managed by Reef GC
Previous: 100_UNSAFE.md Next: 110_INLINE_ASSEMBLY.md Index: 000_INDEX.md