Macros to help define static module structure around C APIs.
$ clib install clibs/module --save
#include <clibs/module/require.h> #include <clibs/module/module.h> #include <clibs/module/def.h> // module definition module(your_module) { define(your_module, CLIB_MODULE); }; // module initializer int your_module_init(module(your_module) *exports) { return 0; } // module de-initializer void your_module_deinit(module(your_module) *exports) { } // module exports exports(your_module) { .init = your_module_init, .deinit = your_module_deinit, };
// the 'clib_module', 'clib_exports', etc macros #include <clibs/module/module.h> // defines 'module, exports, defaults, define, require' alias macros #include <clibs/module/def.h> enum channel_status { CHANNEL_STATUS_ERROR = -1, CHANNEL_STATUS_NONE = 0, CHANNEL_STATUS_OPEN = 1, CHANNEL_STATUS_CLOSED = 2, }; // creates a module struct with the name 'channel' // ie: `struct channel_clib_module { };` module(channel) { // this initializes a few fields using the 'CLIB_MODULE' // prototype which is the expected prototype for the // 'clib_module_require()' function to work define(channel, CLIB_MODULE); // channel functions enum channel_status state; int (*open)(); int (*close)(); int (*send)(void *buf, size_t size); int (*recv)(void *buf, size_t size); };
// creates an exports interface with optional default values exports(channel) { // the module initializer function called exactly once .init = channel_init, // the module deinitializer function called when free'd with // 'cilb_module_free' just before the 'free()' call .deinit = channel_deinit, };
module(channel) *channel = require(channel);
Consider a simple logging module that defines and exports a mode property,
and info(), debug(), and error() logging functions.
Below is a logger.h file that defines a logger module with a few
exports.
logger.h:
#include <clibs/module/require.h> #include <clibs/module/module.h> #include <clibs/module/def.h> enum logger_mode { LOGGER_NONE, LOGGER_INFO, LOGGER_ERROR, LOGGER_DEBUG, }; // Module Type Interface module(logger) { define(logger, CLIB_MODULE); enum logger_mode mode; void (*info)(char *); void (*debug)(char *); void (*error)(char *); }; int logger_init(module(logger) *exports); void logger_deinit(module(logger) *exports); // Default Module Exports exports(logger) { .mode = LOGGER_NONE, .init = logger_init, .deinit = logger_deinit, };
The following logger.c file implements the logger module definition
with a few explicit symbols init() and deinit(). These symbols
can be set to 0 (NULL) for default behavior.
logger.c:
#include <stdio.h> #include "logger.h" int logger_init(module(logger) *exports) { clib_module_init(logger, exports); exports->mode = LOGGER_NONE; exports->info = logger_info; exports->error = logger_error; exports->debug = logger_debug; return 0; } void logger_deinit(module(logger) *exports) { clib_module_deinit(logger); } static inline void logger_info(char *message) { if (require(logger)->mode >= LOGGER_INFO) { fprintf(stdout, " info: %s\n", message); } } static inline void logger_error(char *message) { if (require(logger)->mode >= LOGGER_ERROR) { fprintf(stderr, "error: %s\n", message); } } static inline void logger_debug(char *message) { if (require(logger)->mode >= LOGGER_DEBUG) { fprintf(stderr, "debug: %s\n", message); } }
Below is a program consumes the logger module.
#include "logger.h" int main(void) { module(logger) *logger = require(logger); logger->mode = LOGGER_DEBUG; logger->info("hello"); logger->error("oops"); logger->debug("help"); clib_module_free(logger); return 0; }
Gets a reference to the module structure type.
// as a definition clib_module(module) { clib_module_define(module, CLIB_MODULE); }; // as type clib_module(module) *exports = require(module);
Source:
#ifndef clib_module #define clib_module(type) struct __##type##_clib_module #endif
Initializes the exports of a module and scopes its definition.
clib_module_exports(module) { clib_module_define(module, CLIB_MODULE); };
Source:
#ifndef clib_module_exports #define clib_module_exports(type) \ typedef clib_module(type) type##_t; \ static unsigned int __##type##_clib_module_init = 0; \ static clib_module(type) *__##type##_clib_module; \ static clib_module(type) __##type##_clib_module_exports = \ #endif
The default module field initializer prototype.
clib_module_exports(module) { clib_module_defaults(module, CLIB_MODULE_DEFAULT) }
Source:
#ifndef CLIB_MODULE_DEFAULT #define CLIB_MODULE_DEFAULT(type) \ .name = ""#type, \ .init = 0, \ .deinit = 0 \ #endif
Initializes defaults on a module type with a prototype.
clib_module_exports(module) {
clib_module_defaults(module, CLIB_MODULE_DEFAULT)
};
Source:
#ifndef clib_module_defaults #define clib_module_defaults(type, prototype) prototype(type) #endif
Defines a module with a prototype's fields intended to be called inside the definition of a module structure.
clib_module(module) { clib_module_define(module, CLIB_MODULE); char *value; void *(function)(void *); };
Custom prototypes can be used by defining a new macro
#define CUSTOM_PROTOTYPE \ CLIB_MODULE \ void *(*function)(void *); \ clib_module(module) { clib_module_define(module, CUSTOM_PROTOTYPE); }; clib_exports(module) { .init = init, .deinit = 0, };
Source:
#ifndef clib_module_define #define clib_module_define(module, prototype) prototype(module) #endif
Frees a module pointer and calls deinit() right before.
clib_module_free(module)
Source:
#ifndef clib_module_free #define clib_module_free(module) \ if (0 != (module)) { \ if (0 != (module)->deinit) { \ (module)->deinit((module)); \ } \ free(module); \ } #endif
Returns an allocated pointer a module that should be
free'd with clib_module_free().
clib_module(module) *mod = clib_module_require(module);
Source:
#define clib_module_require(name) ({ \ if (0 == __##name##_clib_module_init || 0 == __##name##_clib_module) { \ __##name##_clib_module = malloc(sizeof(*__##name##_clib_module)); \ clib_module_init(name, __##name##_clib_module); \ \ memset( \ __##name##_clib_module, \ 0, \ sizeof(__##name##_clib_module_exports)); \ \ memcpy( \ __##name##_clib_module, \ &__##name##_clib_module_exports, \ sizeof(__##name##_clib_module_exports)); \ __##name##_clib_module_init = 1; \ \ if (0 != (__##name##_clib_module)->init) { \ if (0 != (__##name##_clib_module)->init((__##name##_clib_module))) { \ free(__##name##_clib_module); \ (__##name##_clib_module) = 0; \ } \ } \ } \ \ (__##name##_clib_module); \ })
The def.h header file defines a set of alias macros for convenience.
module(type) { define(type, CLIB_MODULE); char *value; }; exports(type) { defaults(type, CLIB_MODULE_DEFAULT), .value = "default value" }; module(type) *exports = require(type);
Source:
#define defaults(...) clib_module_defaults(__VA_ARGS__) #define require(...) clib_module_require(__VA_ARGS__) #define exports(...) clib_module_exports(__VA_ARGS__) #define module(...) clib_module(__VA_ARGS__) #define define(...) clib_module_define(__VA_ARGS__)
Tests can be built and ran by running make test:
$ make testOr with the clib build command:
$ clib build --force --test
Examples can be built and ran by running make example/{EXAMPLE}
$ make example/logger $ make example/main
Or with the clib build command:
$ clib build --force -- example/logger $ clib build --force -- example/main
MIT