├── .npmrc ├── .gitignore ├── scripts └── update-clib-json ├── deps └── ok │ ├── clib.json │ ├── ok.h │ ├── ok.c │ └── Makefile ├── def.h ├── clib.json ├── Makefile ├── example ├── example.h ├── logger.h ├── logger.c ├── main.c └── example.c ├── package.json ├── LICENSE ├── test.c ├── require.h ├── module.h └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" 2 | message="chore(release): %s" 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | example/main 2 | example/logger 3 | test 4 | *.o 5 | *.a 6 | -------------------------------------------------------------------------------- /scripts/update-clib-json: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const clib = require('../clib.json') 4 | const pkg = require('../package.json') 5 | const fs = require('fs') 6 | 7 | fs.writeFileSync(require.resolve('../clib.json'), 8 | JSON.stringify(Object.assign(clib, { version: pkg.version }), null, ' ')) 9 | -------------------------------------------------------------------------------- /deps/ok/clib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ok", 3 | "version": "0.3.1", 4 | "author": "Joseph Werle", 5 | "description": "Super tiny tap output library", 6 | "repo": "jwerle/libok", 7 | "src": [ 8 | "ok.h", 9 | "ok.c", 10 | "Makefile" 11 | ], 12 | "keywords": [ 13 | "tap", 14 | "test" 15 | ] 16 | } -------------------------------------------------------------------------------- /def.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIB_MODULE_DEF_H 2 | #define CLIB_MODULE_DEF_H 3 | 4 | #define defaults(...) clib_module_defaults(__VA_ARGS__) 5 | #define require(...) clib_module_require(__VA_ARGS__) 6 | #define exports(...) clib_module_exports(__VA_ARGS__) 7 | #define module(...) clib_module(__VA_ARGS__) 8 | #define define(...) clib_module_define(__VA_ARGS__) 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /clib.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "clibs/module", 3 | "repo": "clibs/module", 4 | "version": "0.1.1", 5 | "description": "A set of macros and functions to make defining a C module easier", 6 | "license": "MIT", 7 | "makefile": "Makefile", 8 | "src": [ 9 | "require.h", 10 | "module.h", 11 | "LICENSE", 12 | "def.h" 13 | ], 14 | "development": { 15 | "jwerle/libok": "0.3.1" 16 | } 17 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS += -I deps 2 | CFLAGS += -I . 3 | 4 | default: test 5 | 6 | example: example/main example/logger 7 | 8 | example/logger: example/logger.c 9 | $(CC) $(CFLAGS) -o $@ $^ 10 | ./$@ 11 | 12 | example/main: example/example.c example/main.c 13 | $(CC) $(CFLAGS) -o $@ $^ 14 | ./$@ 15 | 16 | clean: 17 | rm -f example/logger example/main test 18 | 19 | test: test.c $(wildcard deps/ok/*.c) 20 | $(CC) $(CFLAGS) -o $@ $^ 21 | ./$@ 22 | 23 | -------------------------------------------------------------------------------- /example/example.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIB_MODULE_EXAMPLE_H 2 | #define CLIB_MODULE_EXAMPLE_H 3 | 4 | #include "../module.h" 5 | 6 | /** 7 | * An example module definition with the `CLIB_MODULE` 8 | * module prototype. 9 | */ 10 | clib_module(example) { 11 | clib_module_define(example, CLIB_MODULE); 12 | void (*set)(void *); 13 | void *(*get)(); 14 | }; 15 | 16 | /** 17 | * An example module initializer. 18 | */ 19 | int 20 | example_module_init(clib_module(example) *exports); 21 | 22 | /** 23 | * An example module symbol exports initial state. 24 | */ 25 | clib_module_exports(example) { 26 | .init = example_module_init, 27 | .deinit = 0, 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /example/logger.h: -------------------------------------------------------------------------------- 1 | #include "../require.h" 2 | #include "../module.h" 3 | #include "../def.h" 4 | 5 | enum logger_mode { 6 | LOGGER_NONE, 7 | LOGGER_INFO, 8 | LOGGER_ERROR, 9 | LOGGER_DEBUG, 10 | }; 11 | 12 | // Module Type Interface 13 | module(logger) { 14 | define(logger, CLIB_MODULE); 15 | 16 | enum logger_mode mode; 17 | void (*info)(char *); 18 | void (*debug)(char *); 19 | void (*error)(char *); 20 | }; 21 | 22 | int 23 | logger_init(module(logger) *exports); 24 | 25 | void 26 | logger_deinit(module(logger) *exports); 27 | 28 | // Default Module Exports 29 | exports(logger) { 30 | .mode = LOGGER_NONE, 31 | .init = logger_init, 32 | .deinit = logger_deinit, 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@clibs/module", 3 | "version": "0.1.1", 4 | "description": "A set of macros and functions to make defining a C module easier", 5 | "main": "module.h", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "version": "node scripts/update-clib-json && git add clib.json", 11 | "test": "make clean test" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/clibs/module.git" 16 | }, 17 | "keywords": [ 18 | "clibs", 19 | "clib", 20 | "module" 21 | ], 22 | "author": "Joseph Werle ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/clibs/module/issues" 26 | }, 27 | "homepage": "https://github.com/clibs/module#readme" 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 clibs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example/logger.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "logger.h" 3 | 4 | static inline void 5 | logger_info(char *message) { 6 | if (require(logger)->mode >= LOGGER_INFO) { 7 | fprintf(stdout, " info: %s\n", message); 8 | } 9 | } 10 | 11 | static inline void 12 | logger_error(char *message) { 13 | if (require(logger)->mode >= LOGGER_ERROR) { 14 | fprintf(stderr, "error: %s\n", message); 15 | } 16 | } 17 | 18 | static inline void 19 | logger_debug(char *message) { 20 | if (require(logger)->mode >= LOGGER_DEBUG) { 21 | fprintf(stderr, "debug: %s\n", message); 22 | } 23 | } 24 | 25 | int 26 | logger_init(module(logger) *exports) { 27 | clib_module_init(logger, exports); 28 | exports->mode = LOGGER_NONE; 29 | exports->info = logger_info; 30 | exports->error = logger_error; 31 | exports->debug = logger_debug; 32 | return 0; 33 | } 34 | 35 | void 36 | logger_deinit(module(logger) *exports) { 37 | clib_module_deinit(logger); 38 | } 39 | 40 | int 41 | main(void) { 42 | module(logger) *logger = require(logger); 43 | logger->mode = LOGGER_DEBUG; 44 | logger->info("hello"); 45 | logger->error("oops"); 46 | logger->debug("help"); 47 | clib_module_free(logger); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /example/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "example.h" 3 | #include "../require.h" 4 | 5 | #define require(...) clib_module_require(__VA_ARGS__) 6 | #define exports(...) clib_module_exports(__VA_ARGS__) 7 | #define module(...) clib_module(__VA_ARGS__) 8 | #define define(...) clib_module_define(__VA_ARGS__) 9 | 10 | module(test) { 11 | define(test, CLIB_MODULE); 12 | example_t *example; 13 | }; 14 | 15 | static int 16 | init(module(test) *exports); 17 | 18 | static void 19 | deinit(module(test) *exports); 20 | 21 | exports(test) { 22 | .init = init, 23 | .deinit = deinit, 24 | }; 25 | 26 | static int 27 | init(module(test) *exports) { 28 | clib_module_init(test, exports); 29 | exports->example = require(example); 30 | printf("init module(test)\n"); 31 | return 0; 32 | } 33 | 34 | static void 35 | deinit(module(test) *exports) { 36 | clib_module_deinit(test); 37 | clib_module_free(exports->example); 38 | printf("deinit module(test)\n"); 39 | } 40 | 41 | int 42 | main(void) { 43 | example_t *example = require(example); 44 | test_t *test = require(test); 45 | 46 | example->set("hello"); 47 | printf("%s\n", (char *) example->get()); 48 | 49 | clib_module_free(test); 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /deps/ok/ok.h: -------------------------------------------------------------------------------- 1 | /** 2 | * `ok.h` - libok 3 | * 4 | * Copyright (C) 2014 Joseph Werle 5 | */ 6 | 7 | #ifndef OK_H 8 | #define OK_H 1 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | /** 15 | * libok version 16 | */ 17 | 18 | #ifndef OK_VERSION 19 | #define OK_VERSION "0.3.0" 20 | #endif 21 | 22 | /** 23 | * No-op/void `ok()` function 24 | */ 25 | #ifndef okx 26 | #define okx(...) (void) (0); 27 | #endif 28 | 29 | /** 30 | * Increments ok count and 31 | * outputs a message to stdout 32 | */ 33 | 34 | void 35 | ok (const char *, ...); 36 | 37 | /** 38 | * Completes tests and asserts that 39 | * the expected test count matches the 40 | * actual test count if the expected 41 | * count is greater than 0 42 | */ 43 | 44 | void 45 | ok_done (void); 46 | 47 | /** 48 | * Sets the expectation count 49 | */ 50 | 51 | void 52 | ok_expect (int); 53 | 54 | /** 55 | * Returns the expected count 56 | */ 57 | 58 | int 59 | ok_expected (); 60 | 61 | /** 62 | * Returns the ok count 63 | */ 64 | 65 | int 66 | ok_count (); 67 | 68 | /** 69 | * Resets count and expected counters 70 | */ 71 | 72 | void 73 | ok_reset (); 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /example/example.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "example.h" 4 | #include "../require.h" 5 | 6 | #define defaults(...) clib_module_defaults(__VA_ARGS__) 7 | #define require(...) clib_module_require(__VA_ARGS__) 8 | #define exports(...) clib_module_exports(__VA_ARGS__) 9 | #define module(...) clib_module(__VA_ARGS__) 10 | #define define(...) clib_module_define(__VA_ARGS__) 11 | 12 | module(internal) { 13 | define(internal, CLIB_MODULE); 14 | char *value; 15 | }; 16 | 17 | exports(internal) { 18 | defaults(internal, CLIB_MODULE_DEFAULT), 19 | .value = 0, 20 | }; 21 | 22 | static inline void * 23 | get() { 24 | return require(internal)->value; 25 | } 26 | 27 | static inline void 28 | set(void *val) { 29 | require(internal)->value = val; 30 | } 31 | 32 | static inline void 33 | deinit(module(example) *exports) { 34 | printf("deinit module(example)\n"); 35 | clib_module_free(require(internal)); 36 | clib_module_deinit(internal); 37 | } 38 | 39 | static int 40 | init(module(example) *exports) { 41 | clib_module_init(example, exports); 42 | exports->deinit = deinit; 43 | exports->set = set; 44 | exports->get = get; 45 | return 0; 46 | } 47 | 48 | int 49 | example_module_init(module(example) *exports) { 50 | return init(exports); 51 | } 52 | -------------------------------------------------------------------------------- /deps/ok/ok.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "ok.h" 5 | 6 | /** 7 | * Represents the ok count 8 | */ 9 | 10 | static int ok_count_; 11 | 12 | /** 13 | * Represents an optional 14 | * expected test count 15 | */ 16 | 17 | static int ok_expected_; 18 | 19 | void 20 | ok(const char *format, ...) { 21 | va_list args; 22 | 23 | va_start(args, format); 24 | 25 | if (NULL == format) { 26 | format = (const char *) ""; 27 | } 28 | 29 | printf("ok %d ", ++ok_count_); 30 | vprintf(format, args); 31 | printf("\n"); 32 | va_end(args); 33 | } 34 | 35 | void 36 | ok_done(void) { 37 | if (0 != ok_expected_ && ok_count_ != ok_expected_) { 38 | if (ok_expected_ > ok_count_) { 39 | fprintf(stderr, "expected number of success conditions not met.\n"); 40 | } else { 41 | fprintf(stderr, 42 | "expected number of success conditions is less than the " 43 | "number of given success conditions.\n"); 44 | } 45 | exit(1); 46 | } 47 | 48 | printf("1..%d\n", ok_count_); 49 | } 50 | 51 | void 52 | ok_expect(int expected) { 53 | ok_expected_ = expected; 54 | } 55 | 56 | int 57 | ok_expected() { 58 | return ok_expected_; 59 | } 60 | 61 | int 62 | ok_count() { 63 | return ok_count_; 64 | } 65 | 66 | void 67 | ok_reset() { 68 | ok_count_ = 0; 69 | ok_expected_ = 0; 70 | } 71 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "require.h" 5 | #include "module.h" 6 | #include "def.h" 7 | 8 | // `test` module definition 9 | module(test) { 10 | defaults(test, CLIB_MODULE); 11 | // a private module pointer 12 | void *private; 13 | int state; 14 | int (*function)(); 15 | }; 16 | 17 | // `test` module prototypes 18 | static int 19 | test_init(module(test) *exports); 20 | 21 | static void 22 | test_deinit(module(test) *exports); 23 | 24 | // `test` module exports 25 | exports(test) { 26 | .init = test_init, 27 | .deinit = test_deinit, 28 | }; 29 | 30 | // `private` module definition 31 | module(private) { 32 | define(private, CLIB_MODULE); 33 | int (*function)(); 34 | }; 35 | 36 | // private `private` module function symbol 37 | static int 38 | test_private_function() { 39 | ok("test_private_function()"); 40 | return 0; 41 | } 42 | 43 | // `private` module exports 44 | exports(private) { 45 | defaults(private, CLIB_MODULE_DEFAULT), 46 | .function = test_private_function 47 | }; 48 | 49 | // private `test` module function symbol 50 | static int 51 | test_function() { 52 | ok("test_function()"); 53 | require(test)->state = 1; 54 | require(private)->function(); 55 | return 0; 56 | } 57 | 58 | // `test` module initializer 59 | static int 60 | test_init(module(test) *exports) { 61 | ok("test_init()"); 62 | exports->function = test_function; 63 | exports->private = require(private); 64 | exports->state = -1; 65 | if (0 != exports->private) { 66 | ok("exports->private"); 67 | } 68 | return 0; 69 | } 70 | 71 | // `test` module deinitializer 72 | static void 73 | test_deinit(module(test) *exports) { 74 | ok("test_deinit()"); 75 | clib_module_free((module(private) *) exports->private); 76 | } 77 | 78 | int 79 | main(void) { 80 | ok_expect(8); 81 | 82 | module(test) *test = require(test); 83 | 84 | if (0 != test) { ok("module(test) *test = require(test)"); } 85 | if (-1 == test->state) { ok("-1 == test->state // before test->function()"); } 86 | 87 | test->function(); 88 | if (1 == test->state) { ok("1 == test->state // after test->function()"); } 89 | 90 | clib_module_free(test); 91 | 92 | ok_done(); 93 | return ok_count() - ok_expected(); 94 | } 95 | -------------------------------------------------------------------------------- /require.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIB_MODULE_REQUIRE_H 2 | #define CLIB_MODULE_REQUIRE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "module.h" 8 | 9 | /** 10 | * Returns an allocated pointer a module that should be 11 | * free'd with `clib_module_free()`. 12 | * 13 | * ``` 14 | * clib_module(module) *mod = clib_module_require(module); 15 | * ``` 16 | */ 17 | #define clib_module_require(name) ({ \ 18 | if (0 == __##name##_clib_module_init || 0 == __##name##_clib_module) { \ 19 | __##name##_clib_module = malloc(sizeof(*__##name##_clib_module)); \ 20 | clib_module_init(name, __##name##_clib_module); \ 21 | \ 22 | memset( \ 23 | __##name##_clib_module, \ 24 | 0, \ 25 | sizeof(__##name##_clib_module_exports)); \ 26 | \ 27 | memcpy( \ 28 | __##name##_clib_module, \ 29 | &__##name##_clib_module_exports, \ 30 | sizeof(__##name##_clib_module_exports)); \ 31 | __##name##_clib_module_init = 1; \ 32 | \ 33 | if (0 != (__##name##_clib_module)->init) { \ 34 | if (0 != (__##name##_clib_module)->init((__##name##_clib_module))) { \ 35 | free(__##name##_clib_module); \ 36 | (__##name##_clib_module) = 0; \ 37 | } \ 38 | } \ 39 | } \ 40 | \ 41 | (__##name##_clib_module); \ 42 | }) 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /deps/ok/Makefile: -------------------------------------------------------------------------------- 1 | PREFIX ?= /usr/local 2 | DESTDIR ?= ok 3 | 4 | OS = $(shell uname) 5 | CC ?= cc 6 | AR = ar 7 | LN = ln 8 | RM = rm 9 | VALGRIND ?= valgrind 10 | STRIP = strip 11 | 12 | LIB_NAME = ok 13 | VERSION_MAJOR = 0 14 | VERSION_MINOR = 1 15 | 16 | TARGET_NAME = lib$(LIB_NAME) 17 | TARGET_STATIC = $(TARGET_NAME).a 18 | TARGET_DSOLIB = $(TARGET_NAME).so.$(VERSION_MAJOR).$(VERSION_MINOR) 19 | TARGET_DYLIB = $(TARGET_NAME).$(VERSION_MAJOR).$(VERSION_MINOR).dylib 20 | TARGET_DSO = $(TARGET_NAME).so 21 | 22 | CFLAGS += -I. \ 23 | -std=c99 -Wall -O2 \ 24 | -fvisibility=hidden \ 25 | -fPIC -pedantic 26 | 27 | LDFLAGS += -shared \ 28 | -soname $(TARGET_DSO).$(VERSION_MAJOR) 29 | 30 | OSX_LDFLAGS += -lc \ 31 | -Wl,-install_name,$(TARGET_DSO) \ 32 | -o $(TARGET_DSOLIB) \ 33 | 34 | SRC = $(wildcard *.c) 35 | OBJS = $(SRC:.c=.o) 36 | 37 | TEST_MAIN = ok-test 38 | 39 | ifneq ("Darwin","$(OS)") 40 | CFLAGS += -lm 41 | endif 42 | 43 | ifdef DEBUG 44 | CFLAGS += -DOK_DEBUG 45 | endif 46 | 47 | all: $(TARGET_STATIC) $(TARGET_DSO) 48 | 49 | $(TARGET_STATIC): $(OBJS) 50 | @echo " LIBTOOL-STATIC $(TARGET_STATIC)" 51 | @$(AR) crus $(TARGET_STATIC) $(OBJS) 52 | 53 | $(TARGET_DSO): $(OBJS) 54 | @echo " LIBTOOL-SHARED $(TARGET_DSOLIB)" 55 | @echo " LIBTOOL-SHARED $(TARGET_DSO)" 56 | @echo " LIBTOOL-SHARED $(TARGET_DSO).$(VERSION_MAJOR)" 57 | ifeq ("Darwin","$(OS)") 58 | @$(CC) -shared $(OBJS) $(OSX_LDFLAGS) -o $(TARGET_DSOLIB) 59 | @$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO) 60 | @$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO).$(VERSION_MAJOR) 61 | else 62 | @$(CC) -shared $(OBJS) -o $(TARGET_DSOLIB) 63 | @$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO) 64 | @$(LN) -s $(TARGET_DSOLIB) $(TARGET_DSO).$(VERSION_MAJOR) 65 | @$(STRIP) --strip-unneeded $(TARGET_DSO) 66 | endif 67 | 68 | $(OBJS): 69 | @echo " CC(target) $@" 70 | @$(CC) $(CFLAGS) -c -o $@ $(@:.o=.c) 71 | 72 | check: test 73 | $(VALGRIND) --leak-check=full ./$(TEST_MAIN) 74 | 75 | test: 76 | @echo " LINK(target) ($(TEST_MAIN))" 77 | @$(CC) test.c ./$(TARGET_STATIC) $(CFLAGS) -o $(TEST_MAIN) 78 | @./$(TEST_MAIN) 79 | 80 | clean: 81 | @for item in \ 82 | $(TEST_MAIN) $(OBJS) $(TARGET_STATIC) \ 83 | $(TARGET_DSOLIB) $(TARGET_DSO).$(VERSION_MAJOR) \ 84 | $(TARGET_DSO) $(TARGET_DYLIB) \ 85 | ; do \ 86 | echo " RM $$item"; \ 87 | $(RM) -rf $$item; \ 88 | done; 89 | 90 | 91 | install: all 92 | @test -d $(PREFIX)/$(DESTDIR) || mkdir $(PREFIX)/$(DESTDIR) 93 | @cp *.h $(PREFIX)/include/$(DESTDIR) 94 | @echo " INSTALL $(LIB_NAME).h"; 95 | @install $(LIB_NAME).h $(PREFIX)/include 96 | @echo " INSTALL $(TARGET_STATIC)"; 97 | @install $(TARGET_STATIC) $(PREFIX)/lib 98 | @echo " INSTALL $(TARGET_DSO)"; 99 | @install $(TARGET_DSO) $(PREFIX)/lib 100 | 101 | uninstall: 102 | rm -rf $(PREFIX)/$(DESTDIR) 103 | rm -f $(PREFIX)/lib/$(TARGET_STATIC) 104 | rm -f $(PREFIX)/lib/$(TARGET_DSO) 105 | 106 | -------------------------------------------------------------------------------- /module.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIB_MODULE_H 2 | #define CLIB_MODULE_H 3 | 4 | /** 5 | * Gets a reference to the module structure type. 6 | * 7 | * ``` 8 | * // as a definition 9 | * clib_module(module) { 10 | * clib_module_define(module, CLIB_MODULE); 11 | * }; 12 | * 13 | * // as type 14 | * clib_module(module) *exports = require(module); 15 | * ``` 16 | */ 17 | #ifndef clib_module 18 | #define clib_module(type) struct __##type##_clib_module 19 | #endif 20 | 21 | /** 22 | * Initializes the exports of a module and scopes its definition. 23 | * 24 | * ``` 25 | * clib_module_exports(module) { 26 | * clib_module_define(module, CLIB_MODULE); 27 | * }; 28 | * ``` 29 | */ 30 | #ifndef clib_module_exports 31 | #define clib_module_exports(type) \ 32 | typedef clib_module(type) type##_t; \ 33 | clib_module(type) *__##type##_clib_module; \ 34 | static unsigned int __##type##_clib_module_init = 0; \ 35 | static clib_module(type) __##type##_clib_module_exports = \ 36 | 37 | #endif 38 | 39 | /** 40 | * Creates clib module fields for a structure definition. 41 | * 42 | * ``` 43 | * clib_module(module) { 44 | * clib_module_fields(module); 45 | * }; 46 | * ``` 47 | */ 48 | #ifndef clib_module_fields 49 | #define clib_module_fields(type) \ 50 | const char *name; \ 51 | int (*init)(clib_module(type) *exports); \ 52 | void (*deinit)(clib_module(type) *exports); \ 53 | 54 | #endif 55 | 56 | /** 57 | * The clib module type definition prototype intended to be used with the 58 | * `clib_module()` macro. 59 | * 60 | * ``` 61 | * clib_module(module) { 62 | * clib_module_define(module, CLIB_MODULE); 63 | * }; 64 | * ``` 65 | */ 66 | #ifndef CLIB_MODULE 67 | #define CLIB_MODULE clib_module_fields 68 | #endif 69 | 70 | /** 71 | * The default module field initialization. 72 | */ 73 | #ifndef CLIB_MODULE_DEFAULT 74 | #define CLIB_MODULE_DEFAULT(type) \ 75 | .name = ""#type, \ 76 | .init = 0, \ 77 | .deinit = 0 \ 78 | 79 | #endif 80 | 81 | /** 82 | * Initializes defaults on a module type with a prototype. 83 | * 84 | * ``` 85 | * clib_module_exports(module) { 86 | * clib_module_defaults(module, CLIB_MODULE_DEFAULT) 87 | * }; 88 | * ``` 89 | */ 90 | #ifndef clib_module_defaults 91 | #define clib_module_defaults(type, prototype) prototype(type) 92 | #endif 93 | 94 | /** 95 | * Defines a module with a prototype's fields intended to be called inside the 96 | * definition of a module structure. 97 | * 98 | * ``` 99 | * clib_module(module) { 100 | * clib_module_define(module, CLIB_MODULE); 101 | * char *value; 102 | * void *(function)(void *); 103 | * }; 104 | * ``` 105 | * 106 | * Custom prototypes can be used by defining a new macro 107 | * 108 | * ``` 109 | * #define CUSTOM_PROTOTYPE \ 110 | * CLIB_MODULE \ 111 | * void *(*function)(void *); \ 112 | * 113 | * clib_module(module) { 114 | * clib_module_define(module, CUSTOM_PROTOTYPE); 115 | * }; 116 | * 117 | * clib_exports(module) { 118 | * .init = init, .deinit = 0, 119 | * }; 120 | * ``` 121 | */ 122 | #ifndef clib_module_define 123 | #define clib_module_define(module, prototype) prototype(module) 124 | #endif 125 | 126 | /** 127 | * Marks module and variables as initialized or used. Should be called in 128 | * a module `init()` function. 129 | */ 130 | #ifndef clib_module_init 131 | #define clib_module_init(type, exports) \ 132 | __##type##_clib_module_init = 1; \ 133 | if (0 != (__##type##_clib_module)) { \ 134 | __##type##_clib_module->name = ""#type; \ 135 | } \ 136 | /** marks the following variables as "used" */ \ 137 | (void) (__##type##_clib_module_exports); \ 138 | (void) (__##type##_clib_module_init); \ 139 | (void) (__##type##_clib_module); \ 140 | (void) (exports); \ 141 | 142 | #endif 143 | 144 | /** 145 | * Marks module as de-initialized and points module pointer 146 | * to `0x0` address, the `NULL` pointer. Should be called in a 147 | * `deinit()` module function. 148 | */ 149 | #ifndef clib_module_deinit 150 | #define clib_module_deinit(type) \ 151 | __##type##_clib_module = 0; \ 152 | __##type##_clib_module_init = 0; \ 153 | 154 | #endif 155 | 156 | /** 157 | * Frees a module pointer and calls `deinit()` right before. 158 | */ 159 | #ifndef clib_module_free 160 | #define clib_module_free(module) \ 161 | if (0 != (module)) { \ 162 | if (0 != (module)->deinit) { \ 163 | (module)->deinit((module)); \ 164 | } \ 165 | free(module); \ 166 | } 167 | 168 | #endif 169 | 170 | #endif 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | clibs/module 2 | ============ 3 | 4 | > Macros to help define static module structure around C APIs. 5 | 6 | ## Installation 7 | 8 | ```sh 9 | $ clib install clibs/module --save 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```c 15 | #include 16 | #include 17 | #include 18 | 19 | // module definition 20 | module(your_module) { 21 | define(your_module, CLIB_MODULE); 22 | }; 23 | 24 | // module initializer 25 | int your_module_init(module(your_module) *exports) { 26 | return 0; 27 | } 28 | 29 | // module de-initializer 30 | void your_module_deinit(module(your_module) *exports) { 31 | } 32 | 33 | // module exports 34 | exports(your_module) { 35 | .init = your_module_init, 36 | .deinit = your_module_deinit, 37 | }; 38 | ``` 39 | 40 | ### Defining A Module 41 | 42 | ```c 43 | // the 'clib_module', 'clib_exports', etc macros 44 | #include 45 | 46 | // defines 'module, exports, defaults, define, require' alias macros 47 | #include 48 | 49 | enum channel_status { 50 | CHANNEL_STATUS_ERROR = -1, 51 | CHANNEL_STATUS_NONE = 0, 52 | CHANNEL_STATUS_OPEN = 1, 53 | CHANNEL_STATUS_CLOSED = 2, 54 | }; 55 | 56 | // creates a module struct with the name 'channel' 57 | // ie: `struct channel_clib_module { };` 58 | module(channel) { 59 | // this initializes a few fields using the 'CLIB_MODULE' 60 | // prototype which is the expected prototype for the 61 | // 'clib_module_require()' function to work 62 | define(channel, CLIB_MODULE); 63 | 64 | // channel functions 65 | enum channel_status state; 66 | int (*open)(); 67 | int (*close)(); 68 | int (*send)(void *buf, size_t size); 69 | int (*recv)(void *buf, size_t size); 70 | }; 71 | ``` 72 | 73 | ### Exporting Module Symbols 74 | 75 | ```c 76 | // creates an exports interface with optional default values 77 | exports(channel) { 78 | // the module initializer function called exactly once 79 | .init = channel_init, 80 | // the module deinitializer function called when free'd with 81 | // 'cilb_module_free' just before the 'free()' call 82 | .deinit = channel_deinit, 83 | }; 84 | ``` 85 | 86 | ### Requiring Module Symbols 87 | 88 | ```c 89 | module(channel) *channel = require(channel); 90 | ``` 91 | 92 | ## Example 93 | 94 | Consider a simple logging module that defines and exports a `mode` property, 95 | and `info()`, `debug()`, and `error()` logging functions. 96 | 97 | Below is a `logger.h` file that defines a `logger` module with a few 98 | exports. 99 | 100 | _**logger.h:**_ 101 | 102 | ```c 103 | #include 104 | #include 105 | #include 106 | 107 | enum logger_mode { 108 | LOGGER_NONE, 109 | LOGGER_INFO, 110 | LOGGER_ERROR, 111 | LOGGER_DEBUG, 112 | }; 113 | 114 | // Module Type Interface 115 | module(logger) { 116 | define(logger, CLIB_MODULE); 117 | 118 | enum logger_mode mode; 119 | void (*info)(char *); 120 | void (*debug)(char *); 121 | void (*error)(char *); 122 | }; 123 | 124 | int 125 | logger_init(module(logger) *exports); 126 | 127 | void 128 | logger_deinit(module(logger) *exports); 129 | 130 | // Default Module Exports 131 | exports(logger) { 132 | .mode = LOGGER_NONE, 133 | .init = logger_init, 134 | .deinit = logger_deinit, 135 | }; 136 | ``` 137 | 138 | The following `logger.c` file implements the `logger` module definition 139 | with a few explicit symbols `init()` and `deinit()`. These symbols 140 | can be set to `0` (`NULL`) for default behavior. 141 | 142 | _**logger.c:**_ 143 | 144 | ```c 145 | #include 146 | #include "logger.h" 147 | 148 | int 149 | logger_init(module(logger) *exports) { 150 | clib_module_init(logger, exports); 151 | exports->mode = LOGGER_NONE; 152 | exports->info = logger_info; 153 | exports->error = logger_error; 154 | exports->debug = logger_debug; 155 | return 0; 156 | } 157 | 158 | void 159 | logger_deinit(module(logger) *exports) { 160 | clib_module_deinit(logger); 161 | } 162 | 163 | static inline void 164 | logger_info(char *message) { 165 | if (require(logger)->mode >= LOGGER_INFO) { 166 | fprintf(stdout, " info: %s\n", message); 167 | } 168 | } 169 | 170 | static inline void 171 | logger_error(char *message) { 172 | if (require(logger)->mode >= LOGGER_ERROR) { 173 | fprintf(stderr, "error: %s\n", message); 174 | } 175 | } 176 | 177 | static inline void 178 | logger_debug(char *message) { 179 | if (require(logger)->mode >= LOGGER_DEBUG) { 180 | fprintf(stderr, "debug: %s\n", message); 181 | } 182 | } 183 | ``` 184 | 185 | Below is a program consumes the `logger` module. 186 | 187 | ```c 188 | 189 | #include "logger.h" 190 | 191 | int 192 | main(void) { 193 | module(logger) *logger = require(logger); 194 | 195 | logger->mode = LOGGER_DEBUG; 196 | logger->info("hello"); 197 | logger->error("oops"); 198 | logger->debug("help"); 199 | 200 | clib_module_free(logger); 201 | return 0; 202 | } 203 | ``` 204 | 205 | ## API 206 | 207 | ### `clibs/module/module.h` 208 | 209 | #### `clib_module(type)` 210 | 211 | Gets a reference to the module structure type. 212 | 213 | ```c 214 | // as a definition 215 | clib_module(module) { 216 | clib_module_define(module, CLIB_MODULE); 217 | }; 218 | 219 | // as type 220 | clib_module(module) *exports = require(module); 221 | ``` 222 | 223 | **Source:** 224 | 225 | ```c 226 | #ifndef clib_module 227 | #define clib_module(type) struct __##type##_clib_module 228 | #endif 229 | ``` 230 | 231 | #### `clib_module_exports(type)` 232 | 233 | Initializes the exports of a module and scopes its definition. 234 | 235 | ```c 236 | clib_module_exports(module) { 237 | clib_module_define(module, CLIB_MODULE); 238 | }; 239 | ``` 240 | 241 | **Source:** 242 | 243 | ```c 244 | #ifndef clib_module_exports 245 | #define clib_module_exports(type) \ 246 | typedef clib_module(type) type##_t; \ 247 | static unsigned int __##type##_clib_module_init = 0; \ 248 | static clib_module(type) *__##type##_clib_module; \ 249 | static clib_module(type) __##type##_clib_module_exports = \ 250 | 251 | #endif 252 | ``` 253 | 254 | #### `CLIB_MODULE_DEFAULT` 255 | 256 | The default module field initializer prototype. 257 | 258 | ```c 259 | clib_module_exports(module) { 260 | clib_module_defaults(module, CLIB_MODULE_DEFAULT) 261 | } 262 | ``` 263 | 264 | **Source:** 265 | 266 | ```c 267 | #ifndef CLIB_MODULE_DEFAULT 268 | #define CLIB_MODULE_DEFAULT(type) \ 269 | .name = ""#type, \ 270 | .init = 0, \ 271 | .deinit = 0 \ 272 | 273 | #endif 274 | ``` 275 | 276 | #### `clib_module_defaults(type, prototype)` 277 | 278 | Initializes defaults on a module type with a prototype. 279 | 280 | ``` 281 | clib_module_exports(module) { 282 | clib_module_defaults(module, CLIB_MODULE_DEFAULT) 283 | }; 284 | ``` 285 | 286 | **Source:** 287 | 288 | ```c 289 | #ifndef clib_module_defaults 290 | #define clib_module_defaults(type, prototype) prototype(type) 291 | #endif 292 | ``` 293 | 294 | #### `clib_module_define(module, prototype)` 295 | 296 | Defines a module with a prototype's fields intended to be called inside the 297 | definition of a module structure. 298 | 299 | ```c 300 | clib_module(module) { 301 | clib_module_define(module, CLIB_MODULE); 302 | char *value; 303 | void *(function)(void *); 304 | }; 305 | ``` 306 | 307 | Custom prototypes can be used by defining a new macro 308 | 309 | ```c 310 | #define CUSTOM_PROTOTYPE \ 311 | CLIB_MODULE \ 312 | void *(*function)(void *); \ 313 | 314 | clib_module(module) { 315 | clib_module_define(module, CUSTOM_PROTOTYPE); 316 | }; 317 | 318 | clib_exports(module) { 319 | .init = init, .deinit = 0, 320 | }; 321 | ``` 322 | 323 | **Source:** 324 | 325 | ```c 326 | #ifndef clib_module_define 327 | #define clib_module_define(module, prototype) prototype(module) 328 | #endif 329 | ``` 330 | 331 | #### `clib_module_free(module)` 332 | 333 | Frees a module pointer and calls `deinit()` right before. 334 | 335 | ```c 336 | clib_module_free(module) 337 | ``` 338 | 339 | **Source:** 340 | 341 | ```c 342 | #ifndef clib_module_free 343 | #define clib_module_free(module) \ 344 | if (0 != (module)) { \ 345 | if (0 != (module)->deinit) { \ 346 | (module)->deinit((module)); \ 347 | } \ 348 | free(module); \ 349 | } 350 | 351 | #endif 352 | ``` 353 | 354 | ### `clibs/module/require.h` 355 | 356 | #### `clib_module_require(name)` 357 | 358 | Returns an allocated pointer a module that should be 359 | free'd with `clib_module_free()`. 360 | 361 | ```c 362 | clib_module(module) *mod = clib_module_require(module); 363 | ``` 364 | 365 | **Source:** 366 | 367 | ```c 368 | #define clib_module_require(name) ({ \ 369 | if (0 == __##name##_clib_module_init || 0 == __##name##_clib_module) { \ 370 | __##name##_clib_module = malloc(sizeof(*__##name##_clib_module)); \ 371 | clib_module_init(name, __##name##_clib_module); \ 372 | \ 373 | memset( \ 374 | __##name##_clib_module, \ 375 | 0, \ 376 | sizeof(__##name##_clib_module_exports)); \ 377 | \ 378 | memcpy( \ 379 | __##name##_clib_module, \ 380 | &__##name##_clib_module_exports, \ 381 | sizeof(__##name##_clib_module_exports)); \ 382 | __##name##_clib_module_init = 1; \ 383 | \ 384 | if (0 != (__##name##_clib_module)->init) { \ 385 | if (0 != (__##name##_clib_module)->init((__##name##_clib_module))) { \ 386 | free(__##name##_clib_module); \ 387 | (__##name##_clib_module) = 0; \ 388 | } \ 389 | } \ 390 | } \ 391 | \ 392 | (__##name##_clib_module); \ 393 | }) 394 | ``` 395 | 396 | ### `clibs/module/def.h` 397 | 398 | The `def.h` header file defines a set of alias macros for convenience. 399 | 400 | ```c 401 | module(type) { 402 | define(type, CLIB_MODULE); 403 | char *value; 404 | }; 405 | 406 | exports(type) { 407 | defaults(type, CLIB_MODULE_DEFAULT), 408 | .value = "default value" 409 | }; 410 | 411 | module(type) *exports = require(type); 412 | ``` 413 | 414 | **Source:** 415 | 416 | ```c 417 | #define defaults(...) clib_module_defaults(__VA_ARGS__) 418 | #define require(...) clib_module_require(__VA_ARGS__) 419 | #define exports(...) clib_module_exports(__VA_ARGS__) 420 | #define module(...) clib_module(__VA_ARGS__) 421 | #define define(...) clib_module_define(__VA_ARGS__) 422 | ``` 423 | 424 | ## Tests 425 | 426 | Tests can be built and ran by running `make test`: 427 | 428 | ```sh 429 | $ make test 430 | ``` 431 | 432 | Or with the `clib build` command: 433 | 434 | ```sh 435 | $ clib build --force --test 436 | ``` 437 | 438 | ## Examples 439 | 440 | Examples can be built and ran by running `make example/{EXAMPLE}` 441 | 442 | ```sh 443 | $ make example/logger 444 | $ make example/main 445 | ``` 446 | 447 | Or with the `clib build` command: 448 | 449 | ```sh 450 | $ clib build --force -- example/logger 451 | $ clib build --force -- example/main 452 | ``` 453 | 454 | ## License 455 | 456 | MIT 457 | --------------------------------------------------------------------------------