mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-08-02 21:13:17 +00:00
5a: record {.ffiHost.} procs in a compile-time registry (FFIHostMeta /
ffiHostRegistry), populated by the macro, so generators can see host fns.
5b: the Go generator emits an idiomatic wrapper over the host C ABI:
- a single //export cgo trampoline backs every host fn; a cgo.Handle in
userData selects the Go closure;
- the closure runs on a fresh GOROUTINE so the FFI thread is never blocked
(the non-blocking contract), then answers via <lib>_host_complete by token;
- a per-host `Set<Name>(func(string) (string, error))` method registers it.
Validated end to end with `go run` (examples/host_demo): Go UseToken -> Nim
{.ffi.} handler -> await fetchToken {.ffiHost.} -> Go trampoline -> goroutine
runs the closure -> host_complete -> future resolves on the loop thread ->
"token[TOK-session]" back in Go. Timer's Go output is unchanged (no host fns);
its regenerated .h just gains the always-exported host ABI decls.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
32 lines
1.1 KiB
Nim
32 lines
1.1 KiB
Nim
## Minimal example exercising a {.ffiHost.} host callback end-to-end from Go.
|
|
##
|
|
## `fetchToken` is implemented by the *host* (the Go app); `useToken` is a normal
|
|
## {.ffi.} method the host calls, which in turn asks the host for a token via
|
|
## `fetchToken` and awaits it. This proves the inverted call direction across the
|
|
## real FFI boundary with the generated Go wrapper.
|
|
|
|
import ffi, chronos, results
|
|
|
|
type Demo = object
|
|
|
|
declareLibrary("host_demo", Demo)
|
|
|
|
# Ctor first: the {.ffiCtor.} macro declares the per-lib FFI pool that the
|
|
# {.ffi.} method below references.
|
|
proc demoCreate(): Future[Result[Demo, string]] {.ffiCtor.} =
|
|
return ok(Demo())
|
|
|
|
proc demoDestroy(d: Demo) {.ffiDtor.} =
|
|
discard
|
|
|
|
# Host-implemented: the Go app registers this with SetFetchToken.
|
|
proc fetchToken(key: string): Future[Result[string, string]] {.ffiHost.}
|
|
|
|
# A {.ffi.} method the host calls; it asks the host for a token and wraps it.
|
|
proc useToken(d: Demo, key: string): Future[Result[string, string]] {.ffi.} =
|
|
let tok = (await fetchToken(key)).valueOr:
|
|
return err("host error: " & error)
|
|
return ok("token[" & tok & "]")
|
|
|
|
genBindings()
|