C FFI — Calling C from Wyn
Wyn compiles to C, which means the entire C ecosystem is within reach. Declare a C function with extern fn, and Wyn will call it directly — no wrappers, no glue code.
Declaring an external function
An extern fn is a declaration only — no body, ended with a semicolon. It names a C function and its signature:
extern fn sqrt(x: float) -> float;
extern fn pow(base: float, exp: float) -> float;
fn main() {
println("${sqrt(16.0)}") // 4.0
println("${pow(2.0, 10.0)}") // 1024.0
}The C math library is linked by default, so these run without any extra setup.
Type mapping
Wyn types map to C types as follows:
| Wyn | C |
|---|---|
int | long long |
float | double |
bool | bool |
string | const char* |
void / omitted | void |
A function with no -> T returns void. Variadic C functions are supported with ...:
extern fn my_log_init(level: int); // void return
extern fn my_logf(fmt: string, ...) -> int; // variadicTIP
Use extern fn for functions the standard C headers don't already declare — your own libraries and third-party ones. Don't re-declare printf, puts, malloc, and friends: they're already in scope through the runtime's standard includes, and a second declaration is a C conflict. Math functions such as sqrt and pow are the exception — their double-based signatures match Wyn's float mapping exactly, so they can be declared and called directly (and libm is linked by default).
Linking a C library
To call into a library other than libc, tell the compiler what to link in your project's wyn.toml:
[ffi]
libs = "curl, z"
lib_dirs = "/usr/local/lib"
include_dirs = "/usr/local/include"libs— comma- or space-separated library names; each becomes-l<name>.lib_dirs— extra library search paths (-L). Optional.include_dirs— extra header search paths (-I). Optional.
The compiler passes these flags to the C compiler and links your program against the named libraries automatically. wyn.toml is only consulted when your program actually declares an extern fn.
Example: a custom C library
Given a compiled libmylib.a in your project directory:
// mylib.c → compiled to libmylib.a
int triple(int x) { return x * 3; }# wyn.toml
[ffi]
libs = "mylib"
lib_dirs = "."extern fn triple(x: int) -> int;
fn main() {
println("${triple(7)}") // 21
}Safety notes
- The FFI boundary is unchecked. A foreign call escapes Wyn's type system and memory guarantees — Wyn trusts the signature you declare. A wrong signature is undefined behavior, exactly as in C. Wrap foreign calls in a thin, well-tested Wyn layer rather than scattering them through your code.
[ffi]values are validated. The compiler rejects anylibs/lib_dirs/include_dirsvalue containing shell metacharacters, so awyn.tomlfrom an untrusted source cannot inject a shell command into the build.
Roadmap
This is phase one of Wyn's C interop: correct scalar/string type mapping and manual library linking. Coming next: automatic binding generation from C headers and a package-manager experience for discovering and pulling in C libraries.