mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-20 16:29:31 +00:00
feat(codegen): C native event payloads + -d:ffiMode (native/cbor/both) + tasks
- Add the `-d:ffiMode=native|cbor|both` strdefine (default both) with `ffiEmitNative`/`ffiEmitCbor` helpers; the C generator now emits only the selected header(s) (`<lib>.h` and/or `<lib>_cbor.h`). - Native C events: the native header documents each event's payload type (`"on_echo_fired" -> const EchoEvent *`) so consumers cast the callback's msg to the typed struct — the bare native listener already delivers it. - nimble tasks: `genbindings_c` (both), `genbindings_c_native`, `genbindings_c_cbor`. Verified: native mode emits only my_timer.h, cbor only my_timer_cbor.h, both emits both. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ceccc7bef3
commit
f08cb7971d
@ -111,6 +111,8 @@ int my_timer_schedule(void *ctx, FFICallBack callback, void *userData, JobSpec j
|
||||
|
||||
int my_timer_destroy(void *ctx);
|
||||
|
||||
// Native event payloads — cast the callback's msg accordingly:
|
||||
// "on_echo_fired" -> const EchoEvent *
|
||||
uint64_t my_timer_add_event_listener(void *ctx, const char *eventName, FFICallBack callback, void *userData);
|
||||
int my_timer_remove_event_listener(void *ctx, uint64_t listenerId);
|
||||
|
||||
|
||||
29
ffi.nimble
29
ffi.nimble
@ -146,19 +146,22 @@ task genbindings_rust, "Generate Rust bindings for the timer example":
|
||||
" -d:ffiSrcPath=../timer.nim" &
|
||||
" -o:/dev/null examples/timer/timer.nim"
|
||||
|
||||
task genbindings_c, "Generate C bindings for the timer example":
|
||||
exec "nim c " & nimFlagsOrc &
|
||||
" --app:lib --noMain --nimMainPrefix:libmy_timer" &
|
||||
" -d:ffiGenBindings -d:targetLang=c" &
|
||||
" -d:ffiOutputDir=examples/timer/c_bindings" &
|
||||
" -d:ffiSrcPath=../timer.nim" &
|
||||
" -o:/dev/null examples/timer/timer.nim"
|
||||
exec "nim c " & nimFlagsRefc &
|
||||
" --app:lib --noMain --nimMainPrefix:libmy_timer" &
|
||||
" -d:ffiGenBindings -d:targetLang=c" &
|
||||
" -d:ffiOutputDir=examples/timer/c_bindings" &
|
||||
" -d:ffiSrcPath=../timer.nim" &
|
||||
" -o:/dev/null examples/timer/timer.nim"
|
||||
# `mode` selects the ABI to emit: "native", "cbor", or "both" (-d:ffiMode).
|
||||
proc genC(mode: string) =
|
||||
for flags in [nimFlagsOrc, nimFlagsRefc]:
|
||||
exec "nim c " & flags & " --app:lib --noMain --nimMainPrefix:libmy_timer" &
|
||||
" -d:ffiGenBindings -d:targetLang=c -d:ffiMode=" & mode &
|
||||
" -d:ffiOutputDir=examples/timer/c_bindings -d:ffiSrcPath=../timer.nim" &
|
||||
" -o:/dev/null examples/timer/timer.nim"
|
||||
|
||||
task genbindings_c, "Generate C bindings (native + CBOR) for the timer example":
|
||||
genC("both")
|
||||
|
||||
task genbindings_c_native, "Generate only the native C bindings (<lib>.h)":
|
||||
genC("native")
|
||||
|
||||
task genbindings_c_cbor, "Generate only the CBOR C bindings (<lib>_cbor.h)":
|
||||
genC("cbor")
|
||||
|
||||
task genbindings_go, "Generate Go (cgo) bindings for the timer example":
|
||||
exec "nim c " & nimFlagsOrc &
|
||||
|
||||
@ -167,8 +167,13 @@ proc generateCHeader*(
|
||||
lines.add("")
|
||||
|
||||
# `declareLibrary` always exports the listener-registration ABI. The native
|
||||
# header advertises the native listener: the callback's msg is a typed
|
||||
# `const <Event>*` (cast it), not CBOR.
|
||||
# listener delivers the payload as a typed struct: on RET_OK the callback's
|
||||
# `msg` is a `const <Event>*` (cast it; valid only for the callback), keyed by
|
||||
# the registered event name below.
|
||||
if events.len > 0:
|
||||
lines.add("// Native event payloads — cast the callback's msg accordingly:")
|
||||
for e in events:
|
||||
lines.add("// \"" & e.wireName & "\" -> const " & e.payloadTypeName & " *")
|
||||
lines.add(
|
||||
"uint64_t " & libName &
|
||||
"_add_event_listener(void *ctx, const char *eventName, FFICallBack callback, void *userData);"
|
||||
@ -401,12 +406,14 @@ proc generateCBindings*(
|
||||
nimSrcRelPath: string,
|
||||
events: seq[FFIEventMeta] = @[],
|
||||
) =
|
||||
# Emit both ABIs so consumers can choose per call site: the native (zero-copy,
|
||||
# same-process) one and the CBOR (boundary-crossing / generic) one.
|
||||
writeFile(
|
||||
outputDir / (libName & ".h"), generateCHeader(procs, types, libName, events)
|
||||
)
|
||||
writeFile(
|
||||
outputDir / (libName & "_cbor.h"),
|
||||
generateCborCHeader(procs, types, libName, events),
|
||||
)
|
||||
# Emit the ABI(s) selected by -d:ffiMode (default both): the native (zero-copy,
|
||||
# same-process) header and/or the CBOR (boundary-crossing / generic) one.
|
||||
if ffiEmitNative():
|
||||
writeFile(
|
||||
outputDir / (libName & ".h"), generateCHeader(procs, types, libName, events)
|
||||
)
|
||||
if ffiEmitCbor():
|
||||
writeFile(
|
||||
outputDir / (libName & "_cbor.h"),
|
||||
generateCborCHeader(procs, types, libName, events),
|
||||
)
|
||||
|
||||
@ -49,6 +49,16 @@ var currentLibName* {.compileTime.}: string
|
||||
# Target language for binding generation; override with -d:targetLang=cpp
|
||||
const targetLang* {.strdefine.} = "rust"
|
||||
|
||||
# Which ABI(s) to emit: "native" (zero-serialization C structs), "cbor"
|
||||
# (inter-process), or "both" (default). Override with -d:ffiMode=native.
|
||||
const ffiMode* {.strdefine.} = "both"
|
||||
|
||||
func ffiEmitNative*(): bool =
|
||||
ffiMode == "native" or ffiMode == "both"
|
||||
|
||||
func ffiEmitCbor*(): bool =
|
||||
ffiMode == "cbor" or ffiMode == "both"
|
||||
|
||||
# Output directory for generated bindings; set with -d:ffiOutputDir=path/to/dir
|
||||
const ffiOutputDir* {.strdefine.} = ""
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user