Skip to content

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:

wyn
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:

WynC
intlong long
floatdouble
boolbool
stringconst char*
void / omittedvoid

A function with no -> T returns void. Variadic C functions are supported with ...:

wyn
extern fn my_log_init(level: int);              // void return
extern fn my_logf(fmt: string, ...) -> int;     // variadic

TIP

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:

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:

c
// mylib.c  →  compiled to libmylib.a
int triple(int x) { return x * 3; }
toml
# wyn.toml
[ffi]
libs = "mylib"
lib_dirs = "."
wyn
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 any libs/lib_dirs/ include_dirs value containing shell metacharacters, so a wyn.toml from 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.

See also

MIT License — v1.16