"token" was overloaded (auth tokens, cgo handles, lexer tokens) and didn't say
what it is — a per-call correlation id linking an outgoing {.ffiHost.} call to
the answer that arrives later (possibly from another thread). Renamed across the
runtime (ffi_host / ffi_context), the macro, the exported C ABI (FFIHostFn,
<lib>_host_complete), the Go trampoline, and the tests; regenerated bindings.
The unrelated request-path cgo.Handle result-slot (also informally called a
"token" in go.nim comments) is left as-is — different mechanism.
16 host unit tests + the examples/host_demo Go round-trip stay green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Increment 4: the exported C surface for host callbacks, plus an end-to-end
test that the host can answer from a different thread than the FFI loop.
- declareLibrary now emits two exportc/cdecl procs on every library's
FFIContext (like the event ABI):
<lib>_register_host_fn(ctx, name, fn, userData)
<lib>_host_complete(ctx, token, ret, msg, len)
(the `name` param is spelled `hostFnName` to dodge the macros.name capture
under quote, same class as the existing id/ret collisions.)
- c.nim emits the FFIHostFn typedef + both declarations into <lib>.h
(guarded, format-agnostic), and the timer header is regenerated.
- Verified: the built timer lib exports both symbols.
The e2e (test_ffi_host_e2e) drives the real bridge: a {.ffi.} handler awaits a
{.ffiHost.} call; the host fn (invoked on the FFI thread, non-blocking) hands
the work to a worker thread, which answers via the completion path. The result
resolves on the loop thread and round-trips correctly (orc+refc). It calls the
underlying registerHostFn/completeHostCall directly, since the exported shims
need an --app:lib build; those shims are verified by the symbol check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every generated library exports both ABIs side by side; spell out the choice in
the example READMEs: the native (pure-C) ABI is the default for same-process /
local calls (flat C structs, zero serialization), while the CBOR ABI exists
solely for inter-process communication (different process or machine). In a
shared address space CBOR is pure overhead, so prefer native locally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the native C example to cast each struct return's callback msg to its
`const <Type>*` and read it in-callback (EchoResponse, ComplexResponse), instead
of scanning opaque bytes. Regenerate the headers with the new return-shape note.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The C codegen already emitted `my_timer.h` / `my_timer_cbor.h`, but the example
had no runnable driver. Add `example.c` exercising the native ABI end-to-end
(ctor with a struct param, string-returning version, struct-param echo, and a
deeply nested ComplexRequest), plus a Makefile that builds the Nim dylib from
the repo root — where the vendored Nimble deps resolve — and links the driver.
Native is the same-process path; the companion CBOR headers are for crossing a
process/machine boundary (see the forthcoming ipc example).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>