Documentation channel: v0.0.5 at compiler commit 2c9c7e8. The latest tagged release is v0.0.5; pages identify APIs that are new in v0.0.5.

FFI & embedding

Strut supports two-way interoperability with native code and with host programs. A Strut program can export a stable C ABI surface and be called from native code, and it can embed foreign native code and call it. The same surface lets external programs (C, C++, Python, Go, Node.js, C#) load and drive compiled Strut logic.

The two-way FFI and the embedding API are new in v0.0.5. The one-way native linking described in Linking & FFI remains available and unchanged.

Two directions

Export a C ABI surface

Mark a function export "C" to place it in the generated shared library with an unmangled C symbol. Build the library with --shared and emit a matching header with --emit-c-header.

strut math.p --shared -o libmath.so --emit-c-header math.h
export "C" function add(int a, int b) -> int {
    return a + b;
}

Exported signatures are validated against an ABI-safe type set. Fixed-width integers, string/bytes (as pointer+length with explicit ownership), plain-layout (POD) structs, borrowed primitive ptr<T>/ref<T>, and supported callbacks may cross the boundary. A C++ exception never unwinds into a C caller.

Strings, bytes, and ownership

string and bytes cross the ABI as data pointer plus length. Inputs are borrowed for the duration of the call; outputs are owned by the caller and released with the module's release function. Freeing a null/empty result is safe.

Checked errors across the boundary

An exported function that declares checked errors reports them through a status value plus a module-owned opaque error handle carrying the error type, message, and code. A failed call returns a non-zero status and a concrete error; success returns the value normally.

Callbacks

Two callback forms are supported in both directions:

Embedding API (C)

The embedding surface is an opaque strut_embed_context (#include "strut/embed.h"). A host creates a context, loads Strut source or a file, invokes exported functions by name, converts values, and destroys the context.

#include "strut/embed.h"

strut_embed_context* ctx = strut_embed_context_create();
strut_embed_error* err = NULL;

if (strut_embed_context_load_file(ctx, "app.p", &err) != 0) { /* handle err */ }

strut_embed_value out;
if (strut_embed_invoke(ctx, "add", args, nargs, &out, &err) == 0) { /* use out */ }

strut_embed_value_free(ctx, &out);
strut_embed_context_destroy(ctx);

Host consumers

Executable, independently-tested consumers are provided for C and C++, Python (ctypes), Go (cgo), Node.js (N-API, dependency-free), and C# (P/Invoke). Each builds and runs against the shared embedding library. The Go consumer is not part of the macOS certification path.

Nift interoperability

On Linux, Strut interoperates bidirectionally with the Nift C ABI: a Nift engine can call a Strut export (with dynamic argument marshalling, UTF-8 strings, and byte payloads), and a Strut program can drive a Nift engine. This is Linux-local evidence.

Limitations