|
lexLeo
|
This document defines the standard structure and responsibilities of configuration, context, state, and wiring-related types used across the project.
The purpose is to clearly distinguish:
and to prevent accidental mixing of these concerns.
Suffix: <module>_t
The <module>_t type is the primary runtime handle of a module.
It represents a constructed, executable instance of the module, after all wiring decisions have been resolved.
A module handle:
A module handle:
Typical shapes (illustrative, not normative):
A) Core module handle
B) Port handle (port/adapter boundary)
C) Adapter handle (backend implementation)
Mental model:
_ctx_t builds the module; <module>_t is the module.
Suffix: *_cfg_t
Configuration types define static, value-only parameters that describe how a module behaves from a functional or policy point of view.
A configuration:
const.Configuration expresses what the module is allowed or configured to do, not what it depends on.
Typical use cases:
A module may have no configuration at all if it does not need it.
Suffix: *_state_t
State types define the mutable execution state of a module instance.
A state:
State objects typically hold:
State types:
src/internal/;module_t).Suffix: *_ops_t
Dependency interfaces define abstract capability contracts that a module relies on to interact with other modules or services.
They are expressed as operation tables (function pointer vtables).
Important distinction:
Dependency interfaces (
*_ops_t) describe capabilities
Port virtual tables (*_vtbl_t) describe executable backends
Use *_ops_t when the dependency:
Use *_vtbl_t instead when the dependency is a runtime execution backend selected via a port.
Rules:
Suffix: *_deps_t
Dependency aggregates group all external dependencies required by a module into a single structure.
Example:
This structure represents what the module depends on, explicitly.
Rules:
Suffix: *_callbacks_t
Callbacks define outbound notifications emitted by a module toward its embedding environment.
Example:
typedef struct <module>_callbacks_t {
void (*on_error)(void *user_data, const char *msg);
} <module>_callbacks_t;
Rules:
user_data;Callbacks represent observation, not control.
Suffix: *_ctx_t
The context defines the wiring contract of a module.
It aggregates everything that is injected at construction time only, and expresses how the module is assembled, not how it executes.
It may aggregate:
*_deps_t);*_ops_t);*_callbacks_t);*_vtbl_t) selecting adapter implementations (including teardown semantics)**;user_data pointer.The context represents the upper bound of what can be injected. A real module is expected to include only the fields it actually needs.
Example (maximal form):
Key properties:
Architectural rule:
The context exists only for wiring. It expresses how the module is connected, not how it behaves internally.
In the project architecture:
teardown.c) and is responsible for releasing all resources created or wired during bootstrap (module instances, backends, adapters, OSAL resources);destroy operations;For this reason, ctx types typically live in:
include/internal/<module>_ctx.hand are exposed via CMake only to the bootstrap and test targets.
Modules may expose default providers to reduce boilerplate.
Examples:
Defaults:
Suffix: *_vtbl_t
Virtual tables define runtime callable interfaces used to connect a port abstraction to a concrete backend implementation.
They are the primary mechanism used to implement ports / adapters in a low-level, explicit, and testable way.
A virtual table:
*_ctx_t).In this model:
The port:
void *backend);const lexer_vtbl_t *vtbl);const osal_mem_ops_t *mem).Example:
Although both _ops_t and _vtbl_t are tables of function pointers, they serve distinct architectural roles.
| Type | Purpose | Typical usage |
|---|---|---|
_ops_t | Dependency interface (capabilities) | Services, policies, dependencies |
_vtbl_t | Port / adapter call contract | Ports with interchangeable backends |
*_ops_t when:struct foo *);*_vtbl_t when:void *);destroy) is part of the contract.In short:
_ops_texpresses replaceable operations on a known (possibly opaque) handle
_vtbl_texpresses callable behavior bound to an unknown backend instance
Some modules require dynamic runtime instance management (create/destroy multiple instances during execution). In LexLeo, this must be done via a factory object that is wired once at bootstrap time and then used at runtime.
module_factory_t is an opaque handle representing a VM-owned runtime service that creates and destroys instances of a given family of objects.
Rules:
module_factory_t is typically forward-declared in public headers as an opaque type: typedef struct module_factory_t module_factory_t;src/internal/)Factories only expose the two following runtime operations:
createdestroyThese operations are runtime-safe and must not require a *_ctx_t.
A factory may expose its runtime API through an ops table:
module_factory_ops_t (function pointers)module_factory_handle_t (self + ops)This follows the standard ops/self pattern used across LexLeo modules and supports easy substitution in tests.
| Suffix | Role | Mutability | Scope |
|---|---|---|---|
| <module>_t | Runtime module handle (executable instance) | mutable | public / internal |
| *_cfg_t | Value-only configuration | immutable | public |
| *_state_t | Runtime execution state | mutable | internal only |
| *_ops_t | Replaceable operations (services or internal strategies) | immutable | public / internal |
| *_vtbl_t | Port / adapter call contract (opaque backend) | immutable | internal / wiring |
| *_deps_t | Aggregated injected dependencies | immutable | wiring |
| *_callbacks_t | Outbound notifications (observability only) | immutable | wiring only |
| *_ctx_t | Wiring / dependency injection object | immutable | bootstrap / tests only |
This type model enforces a strict separation between:
*_cfg_t);*_ctx_t, *_deps_t, *_vtbl_t);*_state_t, concrete implementations).It enables explicit dependency injection, controlled substitution, and clear architectural boundaries, while avoiding hidden dependencies and over-virtualization.