|
lexLeo
|
In LexLeo, any API that performs wiring (dependency injection, strategy selection, adapter binding) is owned by the composition root (bootstrap/teardown).
Therefore, lifecycle entry points that consume wiring inputs (typically a *_ctx_t) are internal (non-public API; typically declared under include/internal/) and only exposed to:
Runtime code must not perform wiring (i.e., it must not call entry points that consume a *_ctx_t).
If multiple instances must be created/destroyed during execution, runtime code must use a factory that was wired once at bootstrap time.
Rules:
*_ctx_t.Public headers may expose runtime creation interfaces (ctx-free) when needed. Public headers must not expose wiring-time lifecycle entry points (ctx-consuming).
Factories are typically injected as an ops + userdata pair.
Example:
// include/module_factory.h
typedef struct module module_t;
typedef struct {
module_status_t (*create)(void *userdata, module_t **out);
void (*destroy)(void *userdata, module_t *obj);
} module_factory_ops_t;
typedef struct module_factory {
void *userdata;
const module_factory_ops_t *ops;
} module_factory_t;
Semantics:
module_factory_t is the runtime service object injected into consumers.userdata is an opaque pointer owned by the concrete factory implementation. Consumers must not interpret it; they only pass it back to the ops.ops is the virtual table of ctx-free runtime operations.create / destroy manage instances at runtime; they are distinct from wiring-time lifecycle entry points (which consume a *_ctx_t and remain internal).All *_ops_t tables (including factories, ports, and OSAL operation tables such as osal_mem_ops_t) follow the same lifetime rule:
const *_ops_t *ops must always point to a lifetime-stable object.static const *_ops_t OPS = {...};)Forbidden: passing an ops table allocated on the stack (temporary local variable).
Rationale:
Any runtime object that performs allocations must retain the allocator used to allocate its owned resources.
Rule:
mem_ops, then the runtime object must store const osal_mem_ops_t *mem_ops (or an equivalent allocator handle) so that destruction can be performed later without requiring a *_ctx_t.This applies to:
Therefore:
destroy / close operations must be ctx-free.*_ctx_t).Rationale: