Problem
A library is bound to exactly one state type. declareLibrary(name, LibType, defaultABIFormat) records a single currentLibType; FFIContext[T] has one myLib: ptr T; ffiThreadBody instantiates one var ffiReqHandler: T. A node exposing relay + filter + store must cram all of it into one T. Lowest priority / largest design surface — treat as exploratory. {.ffiHandle.} types (FFIHandleRegistry) already provide a secondary id-keyed object mechanism.
Proposal (pick based on how much the single-type limit actually hurts)
7a — lean on handles (recommended first, minimal change). Keep one small root T (a registry/facade); each subsystem is an {.ffiHandle.} object created via a {.ffi.} proc returning a handle id; subsystem methods take the handle as first object param (already supported — ridesAsPtr/isHandle in meta.nim, FFIHandleRegistry in ffi_handles.nim). Deliverable is mostly docs + a codegen nicety: generate a wrapper class per handle type so callers see node.relay().subscribe(...) instead of free functions taking an opaque id. Zero core-architecture risk; likely the right answer.
7b — true multi-state context (larger, only if 7a is insufficient). declareLibrary accepts multiple state types into a keyed registry; FFIContext holds a heterogeneous set (compile-time tuple or Table[TypeId, pointer]); ffiThreadBody instantiates each; processRequest resolves each proc's state type. Ripples into ffi_context_pool.nim, every ptr FFIContext[T] signature, and codegen receiver resolution — the highest-churn change in the set.
Gotchas
- Handles already solve "multiple live objects of the same kind" — confirm the real requirement is "multiple kinds of root state" before paying 7b's cost. Do not start with 7b.
- All subsystems share the one FFI thread / chronos loop. A subsystem needing its own thread is a different feature (separate contexts) — don't conflate.
Tests
- 7a: an
examples/ multi-subsystem library + e2e C++ test exercising two handle types from one context.
- 7b: per-state routing test; pool acquire/release with multi-state contexts; full sanitizer pass (the generic change is leak-prone).
Effort: small for 7a (docs + a codegen grouping pass), large/invasive for 7b. Recommendation: ship 7a; revisit 7b only if a concrete consumer hits a wall handles can't model.
Problem
A library is bound to exactly one state type.
declareLibrary(name, LibType, defaultABIFormat)records a singlecurrentLibType;FFIContext[T]has onemyLib: ptr T;ffiThreadBodyinstantiates onevar ffiReqHandler: T. A node exposing relay + filter + store must cram all of it into oneT. Lowest priority / largest design surface — treat as exploratory.{.ffiHandle.}types (FFIHandleRegistry) already provide a secondary id-keyed object mechanism.Proposal (pick based on how much the single-type limit actually hurts)
7a — lean on handles (recommended first, minimal change). Keep one small root
T(a registry/facade); each subsystem is an{.ffiHandle.}object created via a{.ffi.}proc returning a handle id; subsystem methods take the handle as first object param (already supported —ridesAsPtr/isHandleinmeta.nim,FFIHandleRegistryinffi_handles.nim). Deliverable is mostly docs + a codegen nicety: generate a wrapper class per handle type so callers seenode.relay().subscribe(...)instead of free functions taking an opaque id. Zero core-architecture risk; likely the right answer.7b — true multi-state context (larger, only if 7a is insufficient).
declareLibraryaccepts multiple state types into a keyed registry;FFIContextholds a heterogeneous set (compile-time tuple orTable[TypeId, pointer]);ffiThreadBodyinstantiates each;processRequestresolves each proc's state type. Ripples intoffi_context_pool.nim, everyptr FFIContext[T]signature, and codegen receiver resolution — the highest-churn change in the set.Gotchas
Tests
examples/multi-subsystem library + e2e C++ test exercising two handle types from one context.Effort: small for 7a (docs + a codegen grouping pass), large/invasive for 7b. Recommendation: ship 7a; revisit 7b only if a concrete consumer hits a wall handles can't model.