mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-26 19:29:49 +00:00
docs: ABI format selection decision (raw vs cbor)
Records the user-chosen direction for telling the compiler a proc's wire
format, applicable across the whole pragma family ({.ffi.}, {.ffiHost.}, …):
- per-proc pragma argument: {.ffi: raw.} / {.ffi: cbor.} (A1), default native;
- library-wide override declareLibrary(defaultAbiFormat = raw) (C2);
- the global -d:ffiMode compile flag is discarded.
'raw' (not 'native') names the zero-serialization C-POD format. Design only;
captures open questions (symbol emission, resolution order, codegen impact) for
further discussion before implementation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c90200de2b
commit
50b56c0cad
75
docs/design-abi-format.md
Normal file
75
docs/design-abi-format.md
Normal file
@ -0,0 +1,75 @@
|
||||
# Design: ABI format selection (`raw` vs `cbor`)
|
||||
|
||||
Status: **direction decided, open for refinement.** How a library author tells the
|
||||
compiler which wire format a boundary-crossing proc uses. Applies to the whole
|
||||
pragma family (`{.ffi.}`, `{.ffiHost.}`, `{.ffiCtor.}`, `{.ffiDtor.}`).
|
||||
|
||||
## The two formats
|
||||
|
||||
- **`raw`** — native, zero-serialization C-POD ABI. Same-process; the request/
|
||||
result is a C struct passed/cast directly under the deep-copy +
|
||||
callback-lifetime ownership rule. The common path.
|
||||
- **`cbor`** — CBOR-encoded buffer. For IPC / generic / cross-language callers
|
||||
where serialization is required anyway.
|
||||
|
||||
## Decision
|
||||
|
||||
**A1 — per-proc pragma argument** (chosen). The format is an argument on the
|
||||
pragma, read by the macro at compile time:
|
||||
|
||||
```nim
|
||||
proc echo(req: EchoReq): Future[Result[EchoResp, string]] {.ffi: raw.}
|
||||
proc echo(req: EchoReq): Future[Result[EchoResp, string]] {.ffi: cbor.}
|
||||
|
||||
proc fetchProfile(id: string): Future[Result[Profile, string]] {.ffiHost: cbor.}
|
||||
```
|
||||
|
||||
Matches the existing `{.ffiEvent: "wire_name".}` precedent (pragmas already take
|
||||
args in this codebase). Local, granular, lets one library mix both formats.
|
||||
|
||||
**C2 — library-wide default** (chosen, layered on top). Set the default once at
|
||||
`declareLibrary`; a per-proc pragma arg overrides it:
|
||||
|
||||
```nim
|
||||
declareLibrary("my_app", MyApp, defaultAbiFormat = raw)
|
||||
# every {.ffi.}/{.ffiHost.}/… is `raw` unless it says otherwise
|
||||
```
|
||||
|
||||
So the common case stays terse, with per-proc control when needed.
|
||||
|
||||
**Rejected:**
|
||||
- **Global compile flag** (`-d:ffiFormat=…`) — discarded. All-or-nothing,
|
||||
action-at-a-distance, can't mix formats in one library. `defaultAbiFormat`
|
||||
replaces its only virtue (a single default) without the downsides.
|
||||
- Distinct pragma names (`{.ffiCbor.}`, `{.ffiHostCbor.}`) — combinatorial
|
||||
explosion across the pragma family.
|
||||
- Format on the data type (`type T {.ffi: cbor.}`) — misattributes a transport
|
||||
property to the data; breaks when one type is used by both a raw and a cbor
|
||||
proc.
|
||||
|
||||
## Sketch
|
||||
|
||||
```nim
|
||||
type AbiFormat* = enum
|
||||
raw # native zero-serialization C-POD (same-process)
|
||||
cbor # CBOR buffer (IPC / generic)
|
||||
|
||||
# two overloads, like ffiEvent: no-arg form defaults to the library default
|
||||
macro ffi*(prc: untyped): untyped = ... # uses defaultAbiFormat
|
||||
macro ffi*(fmt: static[AbiFormat], prc: untyped): untyped = ... # explicit override
|
||||
```
|
||||
|
||||
## Open questions (for further discussion)
|
||||
|
||||
- **Enum name / spelling.** `AbiFormat` with values `raw` / `cbor`? (`raw` over
|
||||
`native` per current preference.)
|
||||
- **Resolution order.** proc pragma arg → `defaultAbiFormat` → built-in fallback
|
||||
(`raw`?). Confirm the fallback when `declareLibrary` sets no default.
|
||||
- **Symbol emission.** Today `{.ffi.}` emits BOTH `<name>` and `<name>_cbor`. Does
|
||||
a per-proc format mean we emit only the chosen symbol, or keep emitting both
|
||||
and let the format arg pick the *primary*? (Leaning: emit only the chosen one;
|
||||
`<name>_cbor` suffix stays only when both are explicitly wanted.)
|
||||
- **Codegen impact.** Each generator (c/go/cpp/rust/swift/kotlin) must honour the
|
||||
per-proc format when emitting wrappers + registration symbols.
|
||||
- **Future A3.** Whether to later expose an orthogonal `{.ffi, wire: cbor.}`
|
||||
transport pragma that composes across the family (deferred; A1+C2 first).
|
||||
@ -46,6 +46,13 @@ Export `<lib>_abi_descriptor()` returning the schema (CBOR/JSON) so a host can
|
||||
validate compatibility at load time. Addresses the deferred CBOR wire-versioning
|
||||
concern.
|
||||
|
||||
## Cross-cutting decisions
|
||||
|
||||
- **ABI format selection** ([design-abi-format.md](design-abi-format.md)) — per-proc
|
||||
pragma arg `{.ffi: raw.}` / `{.ffi: cbor.}` (default native/`raw`), with a
|
||||
library-wide `declareLibrary(defaultAbiFormat = …)` override. Global compile
|
||||
flag discarded. Applies to `{.ffiHost.}` too. Direction decided, design only.
|
||||
|
||||
## Adjacent / parallel tracks (already discussed elsewhere)
|
||||
|
||||
- **seq/Option + multi-struct param marshaling parity** for the Swift (#59) and
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user