### `struct tm_api_registry_listener_i`
Listener for receiving information about changes to the API registry. Use `add_listener()`
a listener to the API registry.
void (*add_implementation)(void *ud, const char *name, void *implementation);
Called when an implementation was added for the interface `name`.
#define TM_API_REGISTRY_API_NAME "tm_api_registry_api"
### `struct tm_api_registry_api`
Global registry that keeps track of loaded APIs and interface implementations.
The difference between an API and an interface is that APIs only have a single implementation,
whereas interfaces can have many implementations.
For example the OS API `tm_os_api`
provides the implementation of OS functions to access files,
memory, etc. It only has a single implementation for each supported platform, and it is this
implementation you call upon to perform OS functions.
In contrast, each module that supports unit tests implements the `tm_unit_test_i`
querying for the `tm_unit_test_i`
interface, you can enumerate all these implementations and run
all the unit tests.
void (*set)(const char *name, void *api, uint32_t bytes);
Sets an API in the registry. `name` is the name of the API that is implemented and `api` is a
pointer to the struct of function pointers defining the API. `bytes` is the size of this
APIs can be implemented only once. If you call `set()`
again, it replaces the previous API
pointers. This can be used to implement hot-reload of APIs.
void (*remove)(void *api);
Removes `API` if it is in use.
void *(*get)(const char *name);
Gets a pointer to the API implementing the API `name`.
`get(name)` is guaranteed to always return the same pointer, throughout the lifetime of an
application (whether `set(name)` has been called zero, one or multiple times). It returns a
pointer to internal API registry memory and the actual API pointers are copied to this memory
On hot-reload these function pointers will be overwritten, but this is transparent to users
of the API. They can continue to use the same interface pointer and will call the new methods
automatically. (If they have cached the function pointers in the API locally, they will keep
calling the old methods.)
on an API that hasn't been loaded yet will return a struct full of NULL
function pointers. When the API is loaded (and calls `set()`
), these NULL pointers will be
replaced by the real function pointers of the API.
To test whether an API has been loaded, you can test if it contains NULL function pointers or
void *(*get_optional)(const char *name);
, but indicates that this is an optional API, i.e. we will be able to continue to
run even if this API is not available.
void (*add_implementation)(const char *name, void *implementation);
Adds an implementation of the interface named `name`.
void (*remove_implementation)(const char *name, void *implementation);
Removes the specified implementation of the interface `name`.
void **(*implementations)(const char *name, uint32_t *count);
Returns an array of all the implementations implementing the interface `name`. The size of
the array is returned in `count`.
!!! TODO: API-REVIEW
This function is kind of annoying to call, either improve this interface or provide an
void (*add_listener)(const tm_api_registry_listener_i *listener);
Adds a listener that will be called with changes to the api_registry. Currently, only an
`add_implementation` callback is provided.
void *(*static_variable)(tm_strhash_t id, uint32_t size, const char *file, uint32_t line);
Returns a pointer to a static variable that survives plugin reloads. `id` is a unique
identifier for the variable and `size` its size in bytes. The first time this function is
called, the variable will be zero-initialized.
Use of static variables in DLLs can be problematic, because when the DLL is reloaded, the
new instance of the DLL will get a new freshly initialized static variable, losing whatever
content the variable had before reload. By using `static_variable()`
instead, the variable
data is saved in permanent memory.
Instead of this:
You would do this:
void load(struct tm_api_registry_api *reg)
count_ptr = (uint64_t *)reg->static_variable(TM_STATIC_HASH("my_count", 0xa287d4b3ec9c2109ULL),
sizeof(uint64_t), __FILE__, __LINE__);
Print a log message about APIs that were requested with `get()`
but were never found.
#define tm_set_or_remove_api(reg, load, name, ptr)
Convenience macro that either sets or remove an api based on value of `load` flag.
#define tm_add_or_remove_implementation(reg, load, name, ptr)
Convenience macro that either adds or removes an implementation based on value of `load` flag.
typedef void tm_load_function(struct tm_api_registry_api *reg, bool load);
Function type for loading plugins or subparts of plugins.
Extern variable holding the global plugin registry.
extern struct tm_api_registry_api *tm_global_api_registry;
void tm_init_global_api_registry(struct tm_allocator_i *a);
Inits the global registry. You must call this before using the
void tm_shutdown_global_api_registry(struct tm_allocator_i *a);
Shuts down the global registry. You must call this to free the resources
allocated by `tm_init_global_api_registry()`
void tm_register_all_foundation_apis(struct tm_api_registry_api *pr);
Convenience function to register all foundation APIs in the specified