Run-time dynamic loader generator for C APIs
Generates run-time dynamic loaders from C header files. Useful for when you don't want to have a hard dependency on a particular C library or where dynamic loading is a requirement, like with OpenGL 2.0+ on Windows.
It works in the following way:
- You provide the generator with a header file, output Jinja2 templates and some additional options
- The header file is preprocessed and parsed into an AST
- Output templates are instantiated with access to the AST and then rendered.
With the default templates you get a struct full of function pointers, *_functions
along with a function to fill that struct, *_load_functions
.
The default templates generate loaders which can be used with any OS-level dynamic loader by passing the *_load_functions
function a loader function pointer of type PFN_rbg_get_sym
and a user-defined argument void *user_arg
.
A simple dynamic loader is provided in loaders/c
. You might need to use something more complicated, like combining wglGetProcAddress
and GetProcAddress
.
Get Python 3.7+, install pipenv and run pipenv install
in rubigen's directory. Run the generator using pipenv run python app.py
.
Example:
pipenv run python app.py tests/data/simple_decl.h test -oheader=test_binding.h=default_c_binding.h -osource=test_binding.c=default_c_binding.c.j2
Output:
[header] default_c_binding.h.j2 -> test_binding.h
[source] default_c_binding.c.j2 -> test_binding.c
Header file:
#pragma once
/* generated by rubigen, do not edit.
*
* parameters:
* defines: none
* force includes: none
*/
#include "simple_decl.h"
#ifdef __cplusplus
extern "C" {
#endif
/* typedefs */
typedef int (*PFN_test_compute)(int x, int y);
/* functions struct */
typedef struct test_functions_t
{
PFN_test_compute compute;
} test_functions;
typedef void* (*PFN_rbg_get_sym)(void *user_arg, const char *name);
void test_load_functions(test_functions *f, PFN_rbg_get_sym get_sym, void *user_arg);
#ifdef __cplusplus
} /* extern "C" */
#endif
Source file:
/* generated by rubigen, do not edit. */
#include "test_binding.h"
void test_load_functions(test_functions *f, PFN_rbg_get_sym get_sym, void *user_arg)
{
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4055)
#endif
f->compute = (PFN_test_compute)get_sym(user_arg, "compute");
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
}
pipenv run python app.py --help
:
usage: app.py [-h] [-I PATH] [-include FORCE_INCLUDES] [-D MACRO[=VAL]] [-T TEMPLATE_DIRS] [-o NAME=FILE=TEMPLATE]
[--out-prefix OUT_PREFIX] [--show-ast] [--show-preprocessed-source]
input namespace
positional arguments:
input the path to the input file
namespace the namespace the generated bindings should be put in
optional arguments:
-h, --help show this help message and exit
-I PATH add an include path
-include FORCE_INCLUDES
force inclusion of the specified file at the top of the input file
-D MACRO[=VAL] add a preprocessor definition
-T TEMPLATE_DIRS add a template directory
-o NAME=FILE=TEMPLATE
generate an output file at FILE using TEMPLATE with the name NAME
--out-prefix OUT_PREFIX
output path prefix
--show-ast
--show-preprocessed-source