A python script which generates a C++ dynamic loader for shared objects, given a C header file. Useful when you don't want to link at compile-time, and if you want to have a beginning of C++ API for your C code.
pip install clang inflection
$ echo 'void mylib_foo(int x, const char* bar);\nvoid mylib_bar(float** z);' > mylib.h
$ python dynalizer.py header.h
will produce a class akin to the following :
class mylib { public: // Pass the path to your .so / .dll / .dylib here explicit mylib(const char* filepath) : m_lib{filepath} { m_sym_mylib_foo = m_lib.symbol<decltype(::mylib_foo)*>("mylib_foo"); if(!m_sym_mylib_foo) throw std::runtime_error("Invalid symbol lookup: mylib_foo"); m_sym_mylib_bar = m_lib.symbol<decltype(::mylib_bar)*>("mylib_bar"); if(!m_sym_mylib_bar) throw std::runtime_error("Invalid symbol lookup: mylib_bar"); } static auto& instance() { static mylib c; return c; } template<typename... Args> auto mylib_foo(Args&&... args) -> decltype(auto) { return m_sym_mylib_foo(std::forward<Args>(args)...); } template<typename... Args> auto mylib_bar(Args&&... args) -> decltype(auto) { return m_sym_mylib_bar(std::forward<Args>(args)...); } private: decltype(::mylib_foo)* m_sym_mylib_foo{}; decltype(::mylib_bar)* m_sym_mylib_bar{}; dynalizer::library m_lib; };
Multiple codegen aspects can be changed:
-
Lazy (
--lazy) and eager (--eager) loading: in the eager case, all the symbols are loaded upon instantiation of the library. In the lazy case, symbols are not loaded until they are actually used. -
Error reporting :
--throwand--abort. These options define what happens when a symbol is missing: an exception, or an assertion. -
Prettification :
--pretty. If possible, will remove common suffixes of the API functions, and will port to standard C++snake_case.That is, the earlier example would become instead:
template<typename... Args> auto foo(Args&&... args) -> decltype(auto) { return m_sym_mylib_foo(std::forward<Args>(args)...); } template<typename... Args> auto bar(Args&&... args) -> decltype(auto) { return m_sym_mylib_bar(std::forward<Args>(args)...); }
so that usage patterns can be akin to :
int main() { auto& pa = portaudio::instance(); pa.open_stream(...); pa.close_stream(...); }
instead of the much heavier
int main() { auto& pa = portaudio::instance(); pa.Pa_OpenStream(...); pa.Pa_CloseStream(...); }