From 7f97410ef5c9078b8e819d9ef1e6a288affad30d Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 15:47:23 +0100 Subject: [PATCH 01/11] chore/feat: refractor and minor fix + utils macro --- public/dynlib/dyn_module.hpp | 30 +++++ public/dynlib/dynlib.hpp | 220 +++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 public/dynlib/dyn_module.hpp create mode 100644 public/dynlib/dynlib.hpp diff --git a/public/dynlib/dyn_module.hpp b/public/dynlib/dyn_module.hpp new file mode 100644 index 0000000..81e79b5 --- /dev/null +++ b/public/dynlib/dyn_module.hpp @@ -0,0 +1,30 @@ +#ifndef __DYN_MODULE_HPP__ +#define __DYN_MODULE_HPP__ + +#ifdef DEFAULT_MODULE +// If DEFAULT_MODULE is defined, EXPORT is defined as 'extern'. +#define EXPORT extern +#define EXPORT_DEFAULT EXPORT Module default_module; +#else +// If DEFAULT_MODULE is not defined, EXPORT is defined as 'extern "C"'. +#define EXPORT extern "C" +#define EXPORT_DEFAULT +#endif + +// Template alias for function pointers. +template using FuncPtr = Func *; + +// Macro to define a type alias for a function pointer. +#define USING_TYPE(func) using func##_ptr = FuncPtr; +#define EXPORT_MODULE(__name__) __attribute__((visibility("default"))) Module __name__ +// Macro to define a pointer to a module item (function or variable). +#define MODULE_ITEM(name) name##_ptr _##name##_m = nullptr; + +// Macro to define a module structure containing provided items. +#define DEFINE_MODULE(...) typedef struct Module {\ + __VA_ARGS__ \ +} *ImportedModule; + + + +#endif diff --git a/public/dynlib/dynlib.hpp b/public/dynlib/dynlib.hpp new file mode 100644 index 0000000..55ff46b --- /dev/null +++ b/public/dynlib/dynlib.hpp @@ -0,0 +1,220 @@ +/** + * @file dynlib.hpp + * @brief Defines the DynamicLibrary class for loading and accessing symbols + * from dynamic libraries. + * + * This file provides the declaration of the DynamicLibrary class, which + * encapsulates functionality for loading dynamic libraries and retrieving + * symbols from them. It also includes necessary headers and type definitions + * required for the class implementation. + * + * @author CASALE Benjamin + * @date 08/03/2024 + */ + +#ifndef __DYNLIB_HPP__ +#define __DYNLIB_HPP__ + +#include +#include +#include + +#include + +/** + * @brief Represents a dynamic library. + */ +class DynamicLibrary { +public: + /** + * @brief Retrieves an item from the dynamic library by its name. + * + * @tparam T The type of the item to retrieve. + * @param fname The name of the item. + * @return T The retrieved item. + */ + template T getItem(std::string_view fname); + + // Destructor + ~DynamicLibrary() = default; + + /** + * @brief Retrieves a dynamic library object given its path. + * + * @param path The path to the dynamic library. + * @return std::shared_ptr A shared pointer to the dynamic + * library object. + */ + static std::shared_ptr getLib(std::string_view path) { + try { + auto lib = new DynamicLibrary(path); + return std::shared_ptr(lib); + } catch (std::exception &e) { + return nullptr; + } + } + + /** + * @brief Retrieves a module from the dynamic library. + * + * @tparam ModuleType The type of the module to retrieve. + * @param lib_handle A shared pointer to the dynamic library. + * @param _def Default value of the module if not found. + * @param modulename The name of the module to retrieve. + * @return ModuleType The retrieved module. + */ + template + static ModuleType getModule(std::shared_ptr lib_handle, + ModuleType *_def = nullptr, + std::string_view modulename = "module"); + +private: + /** + * @brief Private constructor for DynamicLibrary. + * + * @param path The path to the dynamic library. + */ + DynamicLibrary(std::string_view path) { + this->_impl = std::make_unique(path); + } + + // Forward declaration of Impl class + class Impl; + + // Pointer to the implementation object + std::unique_ptr _impl; +}; + +/// LINUX IMPLEMENTATION +#if defined(__linux__) + +#include + +class DynamicLibrary::Impl { +public: + /** + * @brief Constructs the implementation object for the dynamic library. + * + * @param path The path to the dynamic library. + */ + Impl(std::string_view path) { + this->handle = dlopen(std::string(path).c_str(), RTLD_NOW); + + if (!handle) { + throw std::runtime_error("Library cannot be loaded"); + } + } + + /** + * @brief Retrieves a function from the dynamic library by its name. + * + * @tparam T The type of the function to retrieve. + * @param fname The name of the function. + * @return T The retrieved function. + */ + template T getFunction(std::string_view fname) { + void *symbol = dlsym(handle, std::string(fname).c_str()); + T f = static_cast(symbol); + return f; + } + + // Destructor + ~Impl() { + if (handle) { + dlclose(handle); + } + handle = nullptr; + } + +private: + void *handle; // Handle to the dynamic library +}; + +#endif + +#if defined(WIN32) || defined(WIN64) +#include + +class DynamicLibrary::Impl { +public: + /** + * @brief Constructs the implementation object for the dynamic library. + * + * @param path The path to the dynamic library. + */ + Impl(std::string_view path) { + this->handle = LoadLibraryA(path.data()); + + if (!handle) { + throw std::runtime_error("Library cannot be loaded"); + } + } + + /** + * @brief Retrieves a function from the dynamic library by its name. + * + * @tparam T The type of the function to retrieve. + * @param fname The name of the function. + * @return T The retrieved function. + */ + template T getFunction(std::string_view fname) { + FARPROC symbol = GetProcAddress(handle, fname.data()); + return reinterpret_cast(symbol); + } + + // Destructor + ~Impl() { + if (handle) { + FreeLibrary(static_cast(handle)); + } + handle = nullptr; + } + +private: + HMODULE handle; // Handle to the dynamic library +}; +#endif + +// Implementation of getModule for DynamicLibrary class +template +ModuleType DynamicLibrary::getModule(std::shared_ptr lib_handle, + ModuleType *_def, + std::string_view modulename) { + ModuleType _mod; + // Check if the lib is correclty openned + if (!lib_handle) { + // if not + if (!_def) { + // If no default module is provided + throw std::runtime_error( + "Library is not loaded correctly and no default one is provided"); + } + _mod = *_def; // Else we get the default instead of the + } else { + auto sym = lib_handle->getItem(modulename); + if (!sym) { + if (!_def) { + throw std::runtime_error( + "Cannot find required symbol and no default one is provided"); + } else { + _mod = *_def; // IF the lib is opened but we can find the wanted symbol + // we use the defaul;t + std::cerr << "Symbol not found, using default implementation" + << std::endl; + } + } else { + _mod = *sym; + } + } + return _mod; +} + +// Implementation of getItem for DynamicLibrary class +template T DynamicLibrary::getItem(std::string_view fname) { + if (!_impl) { + throw std::runtime_error("DynamicLibrary is not initialized properly"); + } + return _impl->getFunction(fname); +} + +#endif From f095a4dd1b6f825ce54de3a3cf085519a10154b6 Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 15:48:35 +0100 Subject: [PATCH 02/11] feat: new buildsystem + new example --- .gitignore | 8 +++- examples/advanced/main.cpp | 45 +++++++++++++++++++ examples/advanced/meson.build | 23 ++++++++++ examples/advanced/modules/default_lib.cpp | 26 +++++++++++ examples/advanced/modules/external_module.cpp | 27 +++++++++++ examples/advanced/modules/modules.cpp | 0 examples/advanced/modules/modules.hpp | 38 ++++++++++++++++ examples/advanced/modules/wrong.cpp | 26 +++++++++++ examples/basic/main.cpp | 39 ++++++++++++++++ examples/basic/meson.build | 23 ++++++++++ examples/basic/modules/default_lib.cpp | 16 +++++++ examples/basic/modules/external_module.cpp | 17 +++++++ examples/basic/modules/modules.hpp | 17 +++++++ examples/basic/modules/wrong.cpp | 11 +++++ examples/meson.build | 7 +++ examples/modules/module_advanced.cpp | 27 +++++++++++ examples/modules/module_advanced.hpp | 30 +++++++++++++ examples/modules/wrong.cpp | 11 +++++ meson.build | 10 +++++ 19 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 examples/advanced/main.cpp create mode 100644 examples/advanced/meson.build create mode 100644 examples/advanced/modules/default_lib.cpp create mode 100644 examples/advanced/modules/external_module.cpp create mode 100644 examples/advanced/modules/modules.cpp create mode 100644 examples/advanced/modules/modules.hpp create mode 100644 examples/advanced/modules/wrong.cpp create mode 100644 examples/basic/main.cpp create mode 100644 examples/basic/meson.build create mode 100644 examples/basic/modules/default_lib.cpp create mode 100644 examples/basic/modules/external_module.cpp create mode 100644 examples/basic/modules/modules.hpp create mode 100644 examples/basic/modules/wrong.cpp create mode 100644 examples/meson.build create mode 100644 examples/modules/module_advanced.cpp create mode 100644 examples/modules/module_advanced.hpp create mode 100644 examples/modules/wrong.cpp create mode 100644 meson.build diff --git a/.gitignore b/.gitignore index 6f46744..19c02f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,7 @@ -**/build \ No newline at end of file +**/build + +**/builddir + +# LSP +.cache/ +compile_commands.json diff --git a/examples/advanced/main.cpp b/examples/advanced/main.cpp new file mode 100644 index 0000000..6f1237f --- /dev/null +++ b/examples/advanced/main.cpp @@ -0,0 +1,45 @@ +#include "modules/modules.hpp" +#include +#include +#include +#include + +namespace UsafeUDF { +[[nodiscard]] auto init_lib(std::string_view path) { + auto _handle = DynamicLibrary::getLib(path); + Module _mod = DynamicLibrary::getModule(_handle); + make_udf = _mod._make_udf_m; + init_udf = _mod._init_udf_m; + delete_udf = _mod._delete_udf_m; + return _handle; +} + +} // namespace UsafeUDF + +#define LOAD_LIB(name) auto _ = UsafeUDF::init_lib(name); + +int exec() { + + Model m; + + m.init(42); + return 0; +} + +int main(int argc, char **argv) { + std::string_view arg0; + + if (argc == 2) { + arg0 = argv[1]; + try { + + LOAD_LIB(arg0); + return exec(); + } catch (...) { + std::cerr + << "Not default provided and library is not available: early return " + << std::endl; + } + } + return -1; +} diff --git a/examples/advanced/meson.build b/examples/advanced/meson.build new file mode 100644 index 0000000..16008fb --- /dev/null +++ b/examples/advanced/meson.build @@ -0,0 +1,23 @@ +wrong = library('wrong', +'modules/wrong.cpp', +dependencies:dynlib_dep, +pic:true, +gnu_symbol_visibility: 'hidden', +cpp_args:['-DDEFAULT_MODULE']) + +external = library('external','modules/external_module.cpp', +dependencies:dynlib_dep, +pic:true, +gnu_symbol_visibility: 'hidden', +cpp_args:['-DDEFAULT_MODULE']) + + +default = library('default','modules/default_lib.cpp', +dependencies:dynlib_dep,pic:true, +gnu_symbol_visibility: +'hidden',cpp_args:['-DDEFAULT_MODULE']) + +basic_examples = executable('example','main.cpp', +link_with:default, +dependencies:dynlib_dep, +cpp_args:['-DDEFAULT_MODULE']) diff --git a/examples/advanced/modules/default_lib.cpp b/examples/advanced/modules/default_lib.cpp new file mode 100644 index 0000000..1f4dfe1 --- /dev/null +++ b/examples/advanced/modules/default_lib.cpp @@ -0,0 +1,26 @@ +#include "modules.hpp" +#include + +struct impl { + +}; + + +static void _init_udf(impl &, int a) { std::cout << "a=" << a << std::endl; } + +static void _make_udf(Model &model) { model.pimpl = new impl; } + +static void _delete_udf(impl **pimpl) { + if (pimpl != nullptr) { + delete *pimpl; + *pimpl = nullptr; + } +} + + +#ifdef DEFAULT_MODULE +Module default_module = {&_init_udf, &_make_udf, + &_delete_udf}; +#else +static Module module = {&init_udf, &make_udf, &delete_udf}; +#endif diff --git a/examples/advanced/modules/external_module.cpp b/examples/advanced/modules/external_module.cpp new file mode 100644 index 0000000..ce5d3da --- /dev/null +++ b/examples/advanced/modules/external_module.cpp @@ -0,0 +1,27 @@ +#include "modules.hpp" +#include + +struct impl { + int a; +}; + +void _init_udf(impl &pimpl, int a) { + std::cout << "Hello from dynamically loaded object" << std::endl; + std::cout << "internal data: " << pimpl.a << std::endl; +} + +void _make_udf(Model &model) { + model.pimpl = new impl; + model.pimpl->a = 56; +} + +void _delete_udf(impl **pimpl) { + if (pimpl != nullptr) { + delete *pimpl; + *pimpl = nullptr; + } +} + +// Don't forget to export the module. +__attribute__((visibility("default"))) Module module = {&_init_udf, &_make_udf, + &_delete_udf}; diff --git a/examples/advanced/modules/modules.cpp b/examples/advanced/modules/modules.cpp new file mode 100644 index 0000000..e69de29 diff --git a/examples/advanced/modules/modules.hpp b/examples/advanced/modules/modules.hpp new file mode 100644 index 0000000..874074e --- /dev/null +++ b/examples/advanced/modules/modules.hpp @@ -0,0 +1,38 @@ +#ifndef __MYMODULE_HPP__ +#define __MYMODULE_HPP__ +#include + +struct impl; +struct Model; + +namespace UsafeUDF { +static void (*make_udf)(Model &) = nullptr; +static void (*init_udf)(impl &, int) = nullptr; +static void (*delete_udf)(impl **) = nullptr; +} // namespace UsafeUDF + +struct Model { + Model() { UsafeUDF::make_udf(*this); } + void init(int a) { + if (pimpl != nullptr) { + return UsafeUDF::init_udf(*pimpl, a); + } + } + ~Model() { + if (pimpl != nullptr) { + UsafeUDF::delete_udf(&pimpl); + } + } + impl *pimpl = nullptr; +}; + +using init_udf_ptr = decltype(UsafeUDF::init_udf); +using make_udf_ptr = decltype(UsafeUDF::make_udf); +using delete_udf_ptr = decltype(UsafeUDF::delete_udf); + +DEFINE_MODULE(MODULE_ITEM(init_udf) MODULE_ITEM(make_udf) + MODULE_ITEM(delete_udf)) + +EXPORT_DEFAULT + +#endif diff --git a/examples/advanced/modules/wrong.cpp b/examples/advanced/modules/wrong.cpp new file mode 100644 index 0000000..9f08a71 --- /dev/null +++ b/examples/advanced/modules/wrong.cpp @@ -0,0 +1,26 @@ + +#include "modules.hpp" +#include + +struct impl { + int a; +}; + +void _init_udf(impl &pimpl, int a) { + std::cout << "A=" << pimpl.a << std::endl; +} + +void _make_udf(Model &model) { + model.pimpl = new impl; + model.pimpl->a = 56; +} + +void _delete_udf(impl **pimpl) { + if (pimpl != nullptr) { + delete *pimpl; + *pimpl = nullptr; + } +} + +// Don't forget to export the module. +// Module module = {&_init_udf, &_make_udf, &_delete_udf}; diff --git a/examples/basic/main.cpp b/examples/basic/main.cpp new file mode 100644 index 0000000..82d08f3 --- /dev/null +++ b/examples/basic/main.cpp @@ -0,0 +1,39 @@ +#include "modules/modules.hpp" +#include +#include + +int exec(std::shared_ptr handle) { + // Try to retrieve the module from the loaded library. + // If the module is not found, use the default module provided by + // default_module. + Module _mod = DynamicLibrary::getModule(handle, &default_module); + + // Retrieve the function pointer to the function named "_foo_m" from the + // module. + foo_ptr f = _mod._foo_m; + + // Print the addresses of the current function and the default function. + std::cout << "Current function address " << (void *)f << std::endl; + std::cout << "Default function address " << (void *)default_module._foo_m + << std::endl; + + if (f == default_module._foo_m) { + std::cout << "Using default implementation" << std::endl; + } + // Call the function pointer "f" with arguments 1234 and 1 and print the + // result. + std::cout << f(1234, 1) << std::endl; + return (f(1, 1) == 20) ? 0 : -1; +} + +int main(int argc, char **argv) { + + std::shared_ptr _handle; + + if (argc == 2) { + std::cout << "Loading " << argv[1] << std::endl; + _handle = DynamicLibrary::getLib(argv[1]); + } + + return exec(_handle); +} diff --git a/examples/basic/meson.build b/examples/basic/meson.build new file mode 100644 index 0000000..16008fb --- /dev/null +++ b/examples/basic/meson.build @@ -0,0 +1,23 @@ +wrong = library('wrong', +'modules/wrong.cpp', +dependencies:dynlib_dep, +pic:true, +gnu_symbol_visibility: 'hidden', +cpp_args:['-DDEFAULT_MODULE']) + +external = library('external','modules/external_module.cpp', +dependencies:dynlib_dep, +pic:true, +gnu_symbol_visibility: 'hidden', +cpp_args:['-DDEFAULT_MODULE']) + + +default = library('default','modules/default_lib.cpp', +dependencies:dynlib_dep,pic:true, +gnu_symbol_visibility: +'hidden',cpp_args:['-DDEFAULT_MODULE']) + +basic_examples = executable('example','main.cpp', +link_with:default, +dependencies:dynlib_dep, +cpp_args:['-DDEFAULT_MODULE']) diff --git a/examples/basic/modules/default_lib.cpp b/examples/basic/modules/default_lib.cpp new file mode 100644 index 0000000..5b25a3a --- /dev/null +++ b/examples/basic/modules/default_lib.cpp @@ -0,0 +1,16 @@ +#include "modules.hpp" +#include + +// Declaration and implementation of default_foo function which returns the sum of its two arguments. +int default_foo(int a, int b) { return a + b; } + +// Declaration and implementation of default_bar function which returns the product of its argument and 16. +int default_bar(int a) { return a * 16; } + +#ifdef DEFAULT_MODULE +// If DEFAULT_MODULE is defined, create a Module object named default_module +// containing pointers to default_foo and default_bar functions. +EXPORT_MODULE(default_module) = {&default_foo, &default_bar}; +#else +EXPORT_MODULE(module) = {._foo_m = &default_foo, ._bar_m = &default_bar}; +#endif diff --git a/examples/basic/modules/external_module.cpp b/examples/basic/modules/external_module.cpp new file mode 100644 index 0000000..fc61699 --- /dev/null +++ b/examples/basic/modules/external_module.cpp @@ -0,0 +1,17 @@ +#include "dynlib/dyn_module.hpp" +#include "modules.hpp" + +// It's possible to declare "private" functions. +int baz() { return 10; } + +// Declaration and implementation of the function to be exported. +int foo(int a, int b) { return baz() * (a + b); } + +int bar(int a) { return a * 16 * 2; } + +// Don't forget to export the module. +EXPORT_MODULE(module) = {._foo_m = &foo, ._bar_m = &bar}; + +/* We could define the default module as described in modules.hpp, +but in our case, this external module will never be used as a default module, +so the definition doesn't exist. */ diff --git a/examples/basic/modules/modules.hpp b/examples/basic/modules/modules.hpp new file mode 100644 index 0000000..6e581de --- /dev/null +++ b/examples/basic/modules/modules.hpp @@ -0,0 +1,17 @@ +#ifndef __MYMODULE_HPP__ +#define __MYMODULE_HPP__ + +#include + +using foo_ptr = int (*)(int, int); +using bar_ptr = int (*)(int); + + +DEFINE_MODULE( + MODULE_ITEM(foo) + MODULE_ITEM(bar) +) + +EXPORT_DEFAULT + +#endif diff --git a/examples/basic/modules/wrong.cpp b/examples/basic/modules/wrong.cpp new file mode 100644 index 0000000..ebebd4a --- /dev/null +++ b/examples/basic/modules/wrong.cpp @@ -0,0 +1,11 @@ +#include "modules.hpp" + +// It's possible to declare "private" functions. +int baz() { return 10; } + +// Declaration and implementation of the function to be exported. +int foo(int a, int b) { return baz() * (a + b); } + +int bar(int a) { return a * 16 * 2; } + +//function defined but not exported \ No newline at end of file diff --git a/examples/meson.build b/examples/meson.build new file mode 100644 index 0000000..1df504d --- /dev/null +++ b/examples/meson.build @@ -0,0 +1,7 @@ + + + +subdir('basic') + + +subdir('advanced') diff --git a/examples/modules/module_advanced.cpp b/examples/modules/module_advanced.cpp new file mode 100644 index 0000000..7aa52b6 --- /dev/null +++ b/examples/modules/module_advanced.cpp @@ -0,0 +1,27 @@ +#include "module_advanced.hpp" +#include "dynlib/dyn_module.hpp" +#include + +struct impl { + int a; +}; + +void _init_udf(impl &pimpl, int a) { + std::cout << "internal.a=" << pimpl.a << std::endl; + std::cout << "arg=" << a << std::endl; +} + +void _make_udf(Model &model) { + model.pimpl = new impl; + model.pimpl->a = 11212024; +} + +void _delete_udf(impl **pimpl) { + if (pimpl != nullptr) { + delete *pimpl; + *pimpl = nullptr; + } +} + +// Don't forget to export the module. +EXPORT_MODULE(module) = {&_init_udf, &_make_udf, &_delete_udf}; diff --git a/examples/modules/module_advanced.hpp b/examples/modules/module_advanced.hpp new file mode 100644 index 0000000..0b4e1c9 --- /dev/null +++ b/examples/modules/module_advanced.hpp @@ -0,0 +1,30 @@ +#ifndef __MODULE_2_HPP__ +#define __MODULE_2_HPP__ + +#include + +struct impl; +struct Model; + +namespace UsafeUDF { +extern void (*make_udf)(Model &); +extern void (*init_udf)(impl &, int); +extern void (*delete_udf)(impl **); +} // namespace UsafeUDF + +struct Model { + Model(); + void init(int a); + ~Model(); + + impl *pimpl = nullptr; +}; + +using init_udf_ptr = decltype(UsafeUDF::init_udf); +using make_udf_ptr = decltype(UsafeUDF::make_udf); +using delete_udf_ptr = decltype(UsafeUDF::delete_udf); + +DEFINE_MODULE(MODULE_ITEM(init_udf) MODULE_ITEM(make_udf) + MODULE_ITEM(delete_udf)) + +#endif diff --git a/examples/modules/wrong.cpp b/examples/modules/wrong.cpp new file mode 100644 index 0000000..ebebd4a --- /dev/null +++ b/examples/modules/wrong.cpp @@ -0,0 +1,11 @@ +#include "modules.hpp" + +// It's possible to declare "private" functions. +int baz() { return 10; } + +// Declaration and implementation of the function to be exported. +int foo(int a, int b) { return baz() * (a + b); } + +int bar(int a) { return a * 16 * 2; } + +//function defined but not exported \ No newline at end of file diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..39e896b --- /dev/null +++ b/meson.build @@ -0,0 +1,10 @@ +project('dynamic-library', 'cpp', version: '1.0', default_options: ['warning_level=3','cpp_std=gnu++20']) + +shared_include = include_directories('../public') + + + +dynlib_dep = declare_dependency(include_directories: shared_include) + +subdir('examples') + From 32ee332b1eacec768c6e724fac7710b9aea366ca Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 15:54:16 +0100 Subject: [PATCH 03/11] chore: clean and fix make for basic --- examples/makefile | 11 +- examples/modules/default_lib.cpp | 15 -- examples/modules/external_module.cpp | 16 -- examples/modules/module_advanced.cpp | 27 ---- examples/modules/module_advanced.hpp | 30 ---- examples/modules/modules.hpp | 17 -- examples/modules/wrong.cpp | 11 -- lib/dyn_module.hpp | 30 ---- lib/dynlib.hpp | 225 --------------------------- 9 files changed, 7 insertions(+), 375 deletions(-) delete mode 100644 examples/modules/default_lib.cpp delete mode 100644 examples/modules/external_module.cpp delete mode 100644 examples/modules/module_advanced.cpp delete mode 100644 examples/modules/module_advanced.hpp delete mode 100644 examples/modules/modules.hpp delete mode 100644 examples/modules/wrong.cpp delete mode 100644 lib/dyn_module.hpp delete mode 100644 lib/dynlib.hpp diff --git a/examples/makefile b/examples/makefile index 77cac51..a2bc1cc 100644 --- a/examples/makefile +++ b/examples/makefile @@ -1,7 +1,7 @@ .PHONY: all clean external_module BUILD_DIR=./build -INCLUDE_DIR=../lib +INCLUDE_DIR=../public all: dir main @@ -9,13 +9,16 @@ dir: mkdir -p ${BUILD_DIR} default: - g++ -shared -I${INCLUDE_DIR} -fPIC ./modules/default_lib.cpp -o ${BUILD_DIR}/libmodule1.so -DDEFAULT_MODULE + g++ -shared -I${INCLUDE_DIR} -fPIC ./basic/modules/default_lib.cpp -o ${BUILD_DIR}/libmodule1.so -DDEFAULT_MODULE external_module: - g++ -shared -fPIC ./modules/external_module.cpp -o ${BUILD_DIR}/external_module.so -I${INCLUDE_DIR} + g++ -shared -fPIC ./basic/modules/external_module.cpp -o ${BUILD_DIR}/external_module.so -I${INCLUDE_DIR} main: default - g++ main.cpp -I${INCLUDE_DIR} -fPIC -L${BUILD_DIR} -lmodule1 -o ${BUILD_DIR}/main -Wl,-rpath=$(PWD)/${BUILD_DIR} -DDEFAULT_MODULE + g++ basic/main.cpp -I${INCLUDE_DIR} -fPIC -L${BUILD_DIR} -lmodule1 -o ${BUILD_DIR}/main -Wl,-rpath=$(PWD)/${BUILD_DIR} -DDEFAULT_MODULE clean: rm -rf ${BUILD_DIR} + +run: + ${BUILD_DIR}/main diff --git a/examples/modules/default_lib.cpp b/examples/modules/default_lib.cpp deleted file mode 100644 index 15ded34..0000000 --- a/examples/modules/default_lib.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "modules.hpp" - -// Declaration and implementation of default_foo function which returns the sum of its two arguments. -int default_foo(int a, int b) { return a + b; } - -// Declaration and implementation of default_bar function which returns the product of its argument and 16. -int default_bar(int a) { return a * 16; } - -#ifdef DEFAULT_MODULE -// If DEFAULT_MODULE is defined, create a Module object named default_module -// containing pointers to default_foo and default_bar functions. -Module default_module = {&default_foo, &default_bar}; -#else -Module module = {._foo_m = &default_foo, ._bar_m = &default_bar}; -#endif diff --git a/examples/modules/external_module.cpp b/examples/modules/external_module.cpp deleted file mode 100644 index 92d3a70..0000000 --- a/examples/modules/external_module.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "modules.hpp" - -// It's possible to declare "private" functions. -int baz() { return 10; } - -// Declaration and implementation of the function to be exported. -int foo(int a, int b) { return baz() * (a + b); } - -int bar(int a) { return a * 16 * 2; } - -// Don't forget to export the module. -Module module = {._foo_m = &foo, ._bar_m = &bar}; - -/* We could define the default module as described in modules.hpp, -but in our case, this external module will never be used as a default module, -so the definition doesn't exist. */ \ No newline at end of file diff --git a/examples/modules/module_advanced.cpp b/examples/modules/module_advanced.cpp deleted file mode 100644 index 7aa52b6..0000000 --- a/examples/modules/module_advanced.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "module_advanced.hpp" -#include "dynlib/dyn_module.hpp" -#include - -struct impl { - int a; -}; - -void _init_udf(impl &pimpl, int a) { - std::cout << "internal.a=" << pimpl.a << std::endl; - std::cout << "arg=" << a << std::endl; -} - -void _make_udf(Model &model) { - model.pimpl = new impl; - model.pimpl->a = 11212024; -} - -void _delete_udf(impl **pimpl) { - if (pimpl != nullptr) { - delete *pimpl; - *pimpl = nullptr; - } -} - -// Don't forget to export the module. -EXPORT_MODULE(module) = {&_init_udf, &_make_udf, &_delete_udf}; diff --git a/examples/modules/module_advanced.hpp b/examples/modules/module_advanced.hpp deleted file mode 100644 index 0b4e1c9..0000000 --- a/examples/modules/module_advanced.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef __MODULE_2_HPP__ -#define __MODULE_2_HPP__ - -#include - -struct impl; -struct Model; - -namespace UsafeUDF { -extern void (*make_udf)(Model &); -extern void (*init_udf)(impl &, int); -extern void (*delete_udf)(impl **); -} // namespace UsafeUDF - -struct Model { - Model(); - void init(int a); - ~Model(); - - impl *pimpl = nullptr; -}; - -using init_udf_ptr = decltype(UsafeUDF::init_udf); -using make_udf_ptr = decltype(UsafeUDF::make_udf); -using delete_udf_ptr = decltype(UsafeUDF::delete_udf); - -DEFINE_MODULE(MODULE_ITEM(init_udf) MODULE_ITEM(make_udf) - MODULE_ITEM(delete_udf)) - -#endif diff --git a/examples/modules/modules.hpp b/examples/modules/modules.hpp deleted file mode 100644 index cde4d08..0000000 --- a/examples/modules/modules.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __MYMODULE_HPP__ -#define __MYMODULE_HPP__ - -#include "../../lib/dyn_module.hpp" - -using foo_ptr = int (*)(int, int); -using bar_ptr = int (*)(int); - - -DEFINE_MODULE( - MODULE_ITEM(foo) - MODULE_ITEM(bar) -) - -EXPORT_DEFAULT - -#endif diff --git a/examples/modules/wrong.cpp b/examples/modules/wrong.cpp deleted file mode 100644 index ebebd4a..0000000 --- a/examples/modules/wrong.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "modules.hpp" - -// It's possible to declare "private" functions. -int baz() { return 10; } - -// Declaration and implementation of the function to be exported. -int foo(int a, int b) { return baz() * (a + b); } - -int bar(int a) { return a * 16 * 2; } - -//function defined but not exported \ No newline at end of file diff --git a/lib/dyn_module.hpp b/lib/dyn_module.hpp deleted file mode 100644 index 6942165..0000000 --- a/lib/dyn_module.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef __DYN_MODULE_HPP__ -#define __DYN_MODULE_HPP__ - -#ifdef DEFAULT_MODULE -// If DEFAULT_MODULE is defined, EXPORT is defined as 'extern'. -#define EXPORT extern -#define EXPORT_DEFAULT EXPORT Module default_module; -#else -// If DEFAULT_MODULE is not defined, EXPORT is defined as 'extern "C"'. -#define EXPORT extern "C" -#define EXPORT_DEFAULT -#endif - -// Template alias for function pointers. -template using FuncPtr = Func *; - -// Macro to define a type alias for a function pointer. -#define USING_TYPE(func) using func##_ptr = FuncPtr; - -// Macro to define a pointer to a module item (function or variable). -#define MODULE_ITEM(name) name##_ptr _##name##_m = nullptr; - -// Macro to define a module structure containing provided items. -#define DEFINE_MODULE(...) typedef struct Module {\ - __VA_ARGS__ \ -} *ImportedModule; - - - -#endif diff --git a/lib/dynlib.hpp b/lib/dynlib.hpp deleted file mode 100644 index e6f5576..0000000 --- a/lib/dynlib.hpp +++ /dev/null @@ -1,225 +0,0 @@ -/** - * @file dynlib.hpp - * @brief Defines the DynamicLibrary class for loading and accessing symbols from dynamic libraries. - * - * This file provides the declaration of the DynamicLibrary class, which encapsulates functionality - * for loading dynamic libraries and retrieving symbols from them. It also includes necessary headers - * and type definitions required for the class implementation. - * - * @author CASALE Benjamin - * @date 08/03/2024 - */ - -#ifndef __DYNLIB_HPP__ -#define __DYNLIB_HPP__ - -#include -#include -#include - -#include - -/** - * @brief Represents a dynamic library. - */ -class DynamicLibrary { -public: - /** - * @brief Retrieves an item from the dynamic library by its name. - * - * @tparam T The type of the item to retrieve. - * @param fname The name of the item. - * @return T The retrieved item. - */ - template - T getItem(std::string_view fname); - - // Destructor - ~DynamicLibrary() = default; - - /** - * @brief Retrieves a dynamic library object given its path. - * - * @param path The path to the dynamic library. - * @return std::shared_ptr A shared pointer to the dynamic library object. - */ - static std::shared_ptr getLib(std::string_view path) { - try { - auto lib = new DynamicLibrary(path); - return std::shared_ptr(lib); - } catch (std::exception &e) { - std::cout< - static ModuleType getModule(std::shared_ptr lib_handle, - ModuleType *_def = nullptr, - std::string_view modulename = "module"); - -private: - /** - * @brief Private constructor for DynamicLibrary. - * - * @param path The path to the dynamic library. - */ - DynamicLibrary(std::string_view path) { - this->_impl = std::make_unique(path); - } - - // Forward declaration of Impl class - class Impl; - - // Pointer to the implementation object - std::unique_ptr _impl; -}; - - -///LINUX IMPLEMENTATION -#if defined(__linux__) - -#include - -class DynamicLibrary::Impl { -public: - /** - * @brief Constructs the implementation object for the dynamic library. - * - * @param path The path to the dynamic library. - */ - Impl(std::string_view path) { - this->handle = dlopen(std::string(path).c_str(), RTLD_NOW); - - if (!handle) { - throw std::runtime_error("Library cannot be loaded"); - } - } - - /** - * @brief Retrieves a function from the dynamic library by its name. - * - * @tparam T The type of the function to retrieve. - * @param fname The name of the function. - * @return T The retrieved function. - */ - template - T getFunction(std::string_view fname) { - void *symbol = dlsym(handle, std::string(fname).c_str()); - T f = static_cast(symbol); - return f; - } - - // Destructor - ~Impl() { - if (handle) { - dlclose(handle); - } - handle = nullptr; - } - -private: - void *handle; // Handle to the dynamic library -}; - -#endif - - -#if defined(WIN32) || defined(WIN64) -#include - -class DynamicLibrary::Impl { -public: - /** - * @brief Constructs the implementation object for the dynamic library. - * - * @param path The path to the dynamic library. - */ - Impl(std::string_view path) { - this->handle = LoadLibraryA(path.data()); - - if (!handle) { - throw std::runtime_error("Library cannot be loaded"); - } - } - - /** - * @brief Retrieves a function from the dynamic library by its name. - * - * @tparam T The type of the function to retrieve. - * @param fname The name of the function. - * @return T The retrieved function. - */ - template - T getFunction(std::string_view fname) { - FARPROC symbol = GetProcAddress(handle, fname.data()); - return reinterpret_cast(symbol); - } - - // Destructor - ~Impl() { - if (handle) { - FreeLibrary(static_cast(handle)); - } - handle = nullptr; - } - -private: - HMODULE handle; // Handle to the dynamic library -}; -#endif - - -// Implementation of getModule for DynamicLibrary class -template -ModuleType DynamicLibrary::getModule(std::shared_ptr lib_handle, - ModuleType *_def, - std::string_view modulename) { - ModuleType _mod; - //Check if the lib is correclty openned - if (!lib_handle) { - // if not - if (!_def) { - // If no default module is provided - throw std::runtime_error( - "Library is not loaded correctly and no default one is provided"); - } - _mod = *_def; // Else we get the default instead of the - } - else - { - auto sym = lib_handle->getItem(modulename); - if (!sym) { - if (!_def) { - throw std::runtime_error( - "Cannot find required symbol and no default one is provided"); - } else { - _mod = *_def; // IF the lib is opened but we can find the wanted symbol we use the defaul;t - std::cerr<<"Symbol not found, using default implementation"< -T DynamicLibrary::getItem(std::string_view fname) { - if (!_impl) { - throw std::runtime_error("DynamicLibrary is not initialized properly"); - } - return _impl->getFunction(fname); -} - -#endif From 4fa3e87cfe72e80ede5b48024d0cedafd6eec101 Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 15:56:27 +0100 Subject: [PATCH 04/11] ci: update for new basic example --- .github/workflows/cpp.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index df761b2..fc018c5 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -41,4 +41,5 @@ jobs: make make external_module cd build - ./main + ./main external_module.so + From 009f919ecadd462f51d41a93e9cf83c346fc4569 Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 16:20:17 +0100 Subject: [PATCH 05/11] minor fix --- README.md | 45 +++++++++++++------ examples/advanced/modules/external_module.cpp | 16 +++---- examples/makefile | 6 +-- meson.build | 10 +++-- public/dynlib/dyn_module.hpp | 20 ++++++--- 5 files changed, 60 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 8014d7e..8eab38d 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,46 @@ # Dynamic Library Loader +
- [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) +
This repository provides a dynamic library loader, serving as a header-only library. It offers a wrapper around OS-specific API shared object loading, enabling seamless integration into your projects. Additionally, it includes a header file to establish a standardized method for defining modules using macros. -# Features + + +- [Dynamic Library Loader](#dynamic-library-loader) +- [Features](#features) +- [Usage](#usage) + - [For Module Definition](#for-module-definition) + - [For the External Library](#for-the-external-library) + - [Example](#example) +- [License](#license) + + +# Features - OS-Specific API Wrapper: Simplifies the process of loading shared objects by providing a unified interface across different operating systems. - Standardized Module Definition: The included header file enables developers to define modules using macros, ensuring consistency and ease of use. -- Cross-Platform Compatibility: Works seamlessly across various operating systems, allowing developers to write code without worrying about platform-specific nuances. - +- Cross-Platform Compatibility: Works seamlessly across various operating systems, + allowing developers to write code without worrying about platform-specific nuances. # Usage -An example is provided in "../examples" +An example is provided in "./examples" -## For Module Definition: +## For Module Definition -1. **Include Header:** Include the `` header file to access macro definitions. +1. **Include Header:** + Include the `` header file to access macro definitions. -2. **Define Module Header:** Define a header for your module, outlining the signature of the functions that need to be exported to the application. Create a "module" structure containing function pointers corresponding to the defined function signatures. +2. **Define Module Header:** + Define a header for your module, outlining the signature of the functions that need to be exported to the application. + Create a "module" structure containing function pointers corresponding to the defined function signatures. -## For the External Library: +## For the External Library 1. **Include Module Header:** Include the header file of your module. @@ -32,7 +48,10 @@ An example is provided in "../examples" 3. **Create Global Variable:** Create a global variable named `module` with function pointers pointing to the functions you've implemented and need to export. You can include "private" functions within the module that are not included in the module definition. -## Example: +IMPORTANT: External modules should, by default, be compiled with the -fvisibility=hidden flag (or its equivalent). This flag prevents symbols from being exported from the object file, reducing the risk of name conflicts—an issue that can be challenging to debug. +Only the Module itself is exported, and this is achieved via a specific macro. + +## Example An example is provided to demonstrate the usage. From the application's perspective (Loader): @@ -44,10 +63,8 @@ An example is provided to demonstrate the usage. From the application's perspect 4. **Retrieve Imported Module:** Retrieve the imported module. -*Note: If the module's name isn't specified, it is assumed that the external library developer followed the naming conventions given in the header. If not, you can use a custom module name and typename to define the struct manually.* - - +_Note: If the module's name isn't specified, it is assumed that the external library developer followed the naming conventions given in the header. If not, you can use a custom module name and type name to define the structure manually._ # License -This project is licensed under the MIT License, allowing for unrestricted use, modification, and distribution. Please review the LICENSE file for detailed licensing terms. \ No newline at end of file +This project is licensed under the MIT License, allowing for unrestricted use, modification, and distribution. Please review the LICENSE file for detailed licensing terms. diff --git a/examples/advanced/modules/external_module.cpp b/examples/advanced/modules/external_module.cpp index ce5d3da..48a5b3b 100644 --- a/examples/advanced/modules/external_module.cpp +++ b/examples/advanced/modules/external_module.cpp @@ -12,16 +12,12 @@ void _init_udf(impl &pimpl, int a) { void _make_udf(Model &model) { model.pimpl = new impl; - model.pimpl->a = 56; + model.pimpl->a = 11272024; } -void _delete_udf(impl **pimpl) { - if (pimpl != nullptr) { - delete *pimpl; - *pimpl = nullptr; - } -} +// This macro can be used for trivial desctruction +// This declare de _delete_udf function +DECLARE_DELETER -// Don't forget to export the module. -__attribute__((visibility("default"))) Module module = {&_init_udf, &_make_udf, - &_delete_udf}; +// Don't forget to export the module (and deleter). +EXPORT_MODULE(module) = {&_init_udf, &_make_udf, &_delete_udf}; diff --git a/examples/makefile b/examples/makefile index a2bc1cc..61a7e5e 100644 --- a/examples/makefile +++ b/examples/makefile @@ -9,13 +9,13 @@ dir: mkdir -p ${BUILD_DIR} default: - g++ -shared -I${INCLUDE_DIR} -fPIC ./basic/modules/default_lib.cpp -o ${BUILD_DIR}/libmodule1.so -DDEFAULT_MODULE + g++ -shared -fvisibility=hidden -I${INCLUDE_DIR} -fPIC ./basic/modules/default_lib.cpp -o ${BUILD_DIR}/libmodule1.so -DDEFAULT_MODULE external_module: - g++ -shared -fPIC ./basic/modules/external_module.cpp -o ${BUILD_DIR}/external_module.so -I${INCLUDE_DIR} + g++ -shared -fvisibility=hidden -fPIC ./basic/modules/external_module.cpp -o ${BUILD_DIR}/external_module.so -I${INCLUDE_DIR} main: default - g++ basic/main.cpp -I${INCLUDE_DIR} -fPIC -L${BUILD_DIR} -lmodule1 -o ${BUILD_DIR}/main -Wl,-rpath=$(PWD)/${BUILD_DIR} -DDEFAULT_MODULE + g++ basic/main.cpp -fvisibility=hidden -I${INCLUDE_DIR} -fPIC -L${BUILD_DIR} -lmodule1 -o ${BUILD_DIR}/main -Wl,-rpath=$(PWD)/${BUILD_DIR} -DDEFAULT_MODULE clean: rm -rf ${BUILD_DIR} diff --git a/meson.build b/meson.build index 39e896b..43d6d80 100644 --- a/meson.build +++ b/meson.build @@ -1,10 +1,12 @@ -project('dynamic-library', 'cpp', version: '1.0', default_options: ['warning_level=3','cpp_std=gnu++20']) +project( + 'dynamic-library', + 'cpp', + version: '1.0', + default_options: ['warning_level=3', 'cpp_std=gnu++20'], +) shared_include = include_directories('../public') - - dynlib_dep = declare_dependency(include_directories: shared_include) subdir('examples') - diff --git a/public/dynlib/dyn_module.hpp b/public/dynlib/dyn_module.hpp index 81e79b5..6ea6d94 100644 --- a/public/dynlib/dyn_module.hpp +++ b/public/dynlib/dyn_module.hpp @@ -16,15 +16,23 @@ template using FuncPtr = Func *; // Macro to define a type alias for a function pointer. #define USING_TYPE(func) using func##_ptr = FuncPtr; -#define EXPORT_MODULE(__name__) __attribute__((visibility("default"))) Module __name__ +#define EXPORT_MODULE(__name__) \ + __attribute__((visibility("default"))) Module __name__ // Macro to define a pointer to a module item (function or variable). #define MODULE_ITEM(name) name##_ptr _##name##_m = nullptr; -// Macro to define a module structure containing provided items. -#define DEFINE_MODULE(...) typedef struct Module {\ - __VA_ARGS__ \ -} *ImportedModule; - +#define DECLARE_DELETER \ + void _delete_udf(impl **pimpl) { \ + if (pimpl != nullptr) { \ + delete *pimpl; \ + *pimpl = nullptr; \ + } \ + } +// Macro to define a module structure containing provided items. +#define DEFINE_MODULE(...) \ + typedef struct Module { \ + __VA_ARGS__ \ + } *ImportedModule; #endif From 98b936730d7d69d5ec427152db29c5d57fd703a5 Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 17:12:38 +0100 Subject: [PATCH 06/11] feat: improve advanced example --- examples/advanced/loader.cpp | 18 ++++++++++ examples/advanced/loader.hpp | 12 +++++++ examples/advanced/main.cpp | 13 +------ examples/advanced/meson.build | 52 ++++++++++++++++----------- examples/advanced/modules/modules.hpp | 6 ++-- 5 files changed, 66 insertions(+), 35 deletions(-) create mode 100644 examples/advanced/loader.cpp create mode 100644 examples/advanced/loader.hpp diff --git a/examples/advanced/loader.cpp b/examples/advanced/loader.cpp new file mode 100644 index 0000000..ee60a89 --- /dev/null +++ b/examples/advanced/loader.cpp @@ -0,0 +1,18 @@ +#include "loader.hpp" +#include "modules/modules.hpp" +#include + +namespace UsafeUDF { +void (*make_udf)(Model &) = nullptr; +void (*init_udf)(impl &, int) = nullptr; +void (*delete_udf)(impl **) = nullptr; +[[nodiscard]] std::shared_ptr init_lib(std::string_view path) { + auto _handle = DynamicLibrary::getLib(path); + auto _mod = DynamicLibrary::getModule(_handle); + + make_udf = _mod._make_udf_m; + init_udf = _mod._init_udf_m; + delete_udf = _mod._delete_udf_m; + return _handle; +} +} // namespace UsafeUDF diff --git a/examples/advanced/loader.hpp b/examples/advanced/loader.hpp new file mode 100644 index 0000000..7004f9a --- /dev/null +++ b/examples/advanced/loader.hpp @@ -0,0 +1,12 @@ +#ifndef LOADER_HPP +#define LOADER_HPP +#include +#include + +#include + +namespace UsafeUDF { + [[nodiscard]] std::shared_ptr init_lib(std::string_view path); +} + +#endif diff --git a/examples/advanced/main.cpp b/examples/advanced/main.cpp index 6f1237f..b522d1c 100644 --- a/examples/advanced/main.cpp +++ b/examples/advanced/main.cpp @@ -3,18 +3,7 @@ #include #include #include - -namespace UsafeUDF { -[[nodiscard]] auto init_lib(std::string_view path) { - auto _handle = DynamicLibrary::getLib(path); - Module _mod = DynamicLibrary::getModule(_handle); - make_udf = _mod._make_udf_m; - init_udf = _mod._init_udf_m; - delete_udf = _mod._delete_udf_m; - return _handle; -} - -} // namespace UsafeUDF +#include "loader.hpp" #define LOAD_LIB(name) auto _ = UsafeUDF::init_lib(name); diff --git a/examples/advanced/meson.build b/examples/advanced/meson.build index 16008fb..1e6a400 100644 --- a/examples/advanced/meson.build +++ b/examples/advanced/meson.build @@ -1,23 +1,35 @@ -wrong = library('wrong', -'modules/wrong.cpp', -dependencies:dynlib_dep, -pic:true, -gnu_symbol_visibility: 'hidden', -cpp_args:['-DDEFAULT_MODULE']) +library( + 'wrong', + 'modules/wrong.cpp', + dependencies: dynlib_dep, + pic: true, + gnu_symbol_visibility: 'hidden', + cpp_args: ['-DDEFAULT_MODULE'], +) -external = library('external','modules/external_module.cpp', -dependencies:dynlib_dep, -pic:true, -gnu_symbol_visibility: 'hidden', -cpp_args:['-DDEFAULT_MODULE']) +library( + 'external', + 'modules/external_module.cpp', + dependencies: dynlib_dep, + pic: true, + gnu_symbol_visibility: 'hidden', + cpp_args: ['-DDEFAULT_MODULE'], +) +default = library( + 'default', + 'modules/default_lib.cpp', + dependencies: dynlib_dep, + pic: true, + gnu_symbol_visibility: 'hidden', + cpp_args: ['-DDEFAULT_MODULE'], +) -default = library('default','modules/default_lib.cpp', -dependencies:dynlib_dep,pic:true, -gnu_symbol_visibility: -'hidden',cpp_args:['-DDEFAULT_MODULE']) - -basic_examples = executable('example','main.cpp', -link_with:default, -dependencies:dynlib_dep, -cpp_args:['-DDEFAULT_MODULE']) +executable( + 'example', + 'main.cpp', + 'loader.cpp', + link_with: default, + dependencies: dynlib_dep, + cpp_args: ['-DDEFAULT_MODULE'], +) diff --git a/examples/advanced/modules/modules.hpp b/examples/advanced/modules/modules.hpp index 874074e..d55132e 100644 --- a/examples/advanced/modules/modules.hpp +++ b/examples/advanced/modules/modules.hpp @@ -6,9 +6,9 @@ struct impl; struct Model; namespace UsafeUDF { -static void (*make_udf)(Model &) = nullptr; -static void (*init_udf)(impl &, int) = nullptr; -static void (*delete_udf)(impl **) = nullptr; +extern void (*make_udf)(Model &); +extern void (*init_udf)(impl &, int); +extern void (*delete_udf)(impl **); } // namespace UsafeUDF struct Model { From ac63d2ca242068e4bc95057e9e21642e03fe8640 Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 20:43:08 +0100 Subject: [PATCH 07/11] refractor/fix: fix internal signature and branching --- public/dynlib/dynlib.hpp | 55 ++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/public/dynlib/dynlib.hpp b/public/dynlib/dynlib.hpp index 55ff46b..31be15c 100644 --- a/public/dynlib/dynlib.hpp +++ b/public/dynlib/dynlib.hpp @@ -33,7 +33,7 @@ class DynamicLibrary { * @param fname The name of the item. * @return T The retrieved item. */ - template T getItem(std::string_view fname); + template T* getItem(std::string_view fname); // Destructor ~DynamicLibrary() = default; @@ -112,9 +112,9 @@ class DynamicLibrary::Impl { * @param fname The name of the function. * @return T The retrieved function. */ - template T getFunction(std::string_view fname) { + template T* getFunction(std::string_view fname) { void *symbol = dlsym(handle, std::string(fname).c_str()); - T f = static_cast(symbol); + T* f = static_cast(symbol); return f; } @@ -157,9 +157,9 @@ class DynamicLibrary::Impl { * @param fname The name of the function. * @return T The retrieved function. */ - template T getFunction(std::string_view fname) { + template T* getFunction(std::string_view fname) { FARPROC symbol = GetProcAddress(handle, fname.data()); - return reinterpret_cast(symbol); + return reinterpret_cast(symbol); } // Destructor @@ -178,39 +178,34 @@ class DynamicLibrary::Impl { // Implementation of getModule for DynamicLibrary class template ModuleType DynamicLibrary::getModule(std::shared_ptr lib_handle, - ModuleType *_def, + ModuleType* _def, std::string_view modulename) { - ModuleType _mod; - // Check if the lib is correclty openned if (!lib_handle) { - // if not if (!_def) { - // If no default module is provided - throw std::runtime_error( - "Library is not loaded correctly and no default one is provided"); - } - _mod = *_def; // Else we get the default instead of the - } else { - auto sym = lib_handle->getItem(modulename); - if (!sym) { - if (!_def) { - throw std::runtime_error( - "Cannot find required symbol and no default one is provided"); - } else { - _mod = *_def; // IF the lib is opened but we can find the wanted symbol - // we use the defaul;t - std::cerr << "Symbol not found, using default implementation" - << std::endl; - } - } else { - _mod = *sym; + throw std::runtime_error("Library is not loaded correctly and no default one is provided"); } + std::cerr << "Library not loaded, using default module" << std::endl; + return *_def; // Return default module + } + + auto sym = lib_handle->getItem(modulename); + + + if (sym) { + return *sym; // Return the module found in the library + } + + // Handle case where symbol is not found + if (!_def) { + throw std::runtime_error("Cannot find required symbol and no default one is provided"); } - return _mod; + + std::cerr << "Symbol not found, using default implementation" << std::endl; + return *_def; // Return default module if symbol is not found } // Implementation of getItem for DynamicLibrary class -template T DynamicLibrary::getItem(std::string_view fname) { +template T* DynamicLibrary::getItem(std::string_view fname) { if (!_impl) { throw std::runtime_error("DynamicLibrary is not initialized properly"); } From 572893ab271a9171782a173f3ec1c3874814ae24 Mon Sep 17 00:00:00 2001 From: Casale Date: Wed, 27 Nov 2024 20:43:17 +0100 Subject: [PATCH 08/11] fix: update examples --- examples/advanced/loader.cpp | 7 ++----- examples/advanced/loader.hpp | 2 +- examples/advanced/main.cpp | 2 +- examples/advanced/meson.build | 13 +++++++++++-- examples/advanced/modules/modules.cpp | 20 ++++++++++++++++++++ examples/advanced/modules/modules.hpp | 22 +++++++--------------- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/examples/advanced/loader.cpp b/examples/advanced/loader.cpp index ee60a89..fb416c2 100644 --- a/examples/advanced/loader.cpp +++ b/examples/advanced/loader.cpp @@ -2,10 +2,7 @@ #include "modules/modules.hpp" #include -namespace UsafeUDF { -void (*make_udf)(Model &) = nullptr; -void (*init_udf)(impl &, int) = nullptr; -void (*delete_udf)(impl **) = nullptr; +namespace UnsafeUDF { [[nodiscard]] std::shared_ptr init_lib(std::string_view path) { auto _handle = DynamicLibrary::getLib(path); auto _mod = DynamicLibrary::getModule(_handle); @@ -15,4 +12,4 @@ void (*delete_udf)(impl **) = nullptr; delete_udf = _mod._delete_udf_m; return _handle; } -} // namespace UsafeUDF +} // namespace UnsafeUDF diff --git a/examples/advanced/loader.hpp b/examples/advanced/loader.hpp index 7004f9a..51134b6 100644 --- a/examples/advanced/loader.hpp +++ b/examples/advanced/loader.hpp @@ -5,7 +5,7 @@ #include -namespace UsafeUDF { +namespace UnsafeUDF { [[nodiscard]] std::shared_ptr init_lib(std::string_view path); } diff --git a/examples/advanced/main.cpp b/examples/advanced/main.cpp index b522d1c..a8dda0e 100644 --- a/examples/advanced/main.cpp +++ b/examples/advanced/main.cpp @@ -5,7 +5,7 @@ #include #include "loader.hpp" -#define LOAD_LIB(name) auto _ = UsafeUDF::init_lib(name); +#define LOAD_LIB(name) auto _ = UnsafeUDF::init_lib(name); int exec() { diff --git a/examples/advanced/meson.build b/examples/advanced/meson.build index 1e6a400..09da369 100644 --- a/examples/advanced/meson.build +++ b/examples/advanced/meson.build @@ -25,11 +25,20 @@ default = library( cpp_args: ['-DDEFAULT_MODULE'], ) +model = library( + 'model', + 'modules/modules.cpp', + dependencies: dynlib_dep, + pic: true, + gnu_symbol_visibility: 'hidden', + cpp_args: ['-DDEFAULT_MODULE'], +) + executable( 'example', 'main.cpp', - 'loader.cpp', - link_with: default, + 'loader.cpp','modules/modules.cpp', + link_with: [default,model], dependencies: dynlib_dep, cpp_args: ['-DDEFAULT_MODULE'], ) diff --git a/examples/advanced/modules/modules.cpp b/examples/advanced/modules/modules.cpp index e69de29..280e6f9 100644 --- a/examples/advanced/modules/modules.cpp +++ b/examples/advanced/modules/modules.cpp @@ -0,0 +1,20 @@ +#include "modules.hpp" + +namespace UnsafeUDF { +void (*make_udf)(Model &) = nullptr; // Define the pointers +void (*init_udf)(impl &, int) = nullptr; +void (*delete_udf)(impl **) = nullptr; +} // namespace UnsafeUDF + +Model::Model() { UnsafeUDF::make_udf(*this); } + +void Model::init(int a) { + if (pimpl != nullptr) { + return UnsafeUDF::init_udf(*pimpl, a); + } +} +Model::~Model() { + if (pimpl != nullptr) { + UnsafeUDF::delete_udf(&pimpl); + } +} diff --git a/examples/advanced/modules/modules.hpp b/examples/advanced/modules/modules.hpp index d55132e..e5aa554 100644 --- a/examples/advanced/modules/modules.hpp +++ b/examples/advanced/modules/modules.hpp @@ -5,30 +5,22 @@ struct impl; struct Model; -namespace UsafeUDF { +namespace UnsafeUDF { extern void (*make_udf)(Model &); extern void (*init_udf)(impl &, int); extern void (*delete_udf)(impl **); } // namespace UsafeUDF struct Model { - Model() { UsafeUDF::make_udf(*this); } - void init(int a) { - if (pimpl != nullptr) { - return UsafeUDF::init_udf(*pimpl, a); - } - } - ~Model() { - if (pimpl != nullptr) { - UsafeUDF::delete_udf(&pimpl); - } - } + Model() ; + void init(int a); + ~Model() ; impl *pimpl = nullptr; }; -using init_udf_ptr = decltype(UsafeUDF::init_udf); -using make_udf_ptr = decltype(UsafeUDF::make_udf); -using delete_udf_ptr = decltype(UsafeUDF::delete_udf); +using init_udf_ptr = decltype(UnsafeUDF::init_udf); +using make_udf_ptr = decltype(UnsafeUDF::make_udf); +using delete_udf_ptr = decltype(UnsafeUDF::delete_udf); DEFINE_MODULE(MODULE_ITEM(init_udf) MODULE_ITEM(make_udf) MODULE_ITEM(delete_udf)) From 2976859a5bcf4e709f17ca1741548d97c66c0877 Mon Sep 17 00:00:00 2001 From: Casale Date: Thu, 28 Nov 2024 11:59:59 +0100 Subject: [PATCH 09/11] feat: improve macro declaration --- examples/advanced/modules/default_lib.cpp | 7 ++-- examples/advanced/modules/external_module.cpp | 4 +-- examples/advanced/modules/modules.hpp | 1 + examples/basic/modules/default_lib.cpp | 4 +-- examples/basic/modules/external_module.cpp | 3 +- examples/main.cpp | 32 ----------------- public/dynlib/dyn_module.hpp | 36 ++++++++++--------- 7 files changed, 29 insertions(+), 58 deletions(-) delete mode 100644 examples/main.cpp diff --git a/examples/advanced/modules/default_lib.cpp b/examples/advanced/modules/default_lib.cpp index 1f4dfe1..74e4a38 100644 --- a/examples/advanced/modules/default_lib.cpp +++ b/examples/advanced/modules/default_lib.cpp @@ -19,8 +19,9 @@ static void _delete_udf(impl **pimpl) { #ifdef DEFAULT_MODULE -Module default_module = {&_init_udf, &_make_udf, - &_delete_udf}; +EXPORT_MODULE(default_module,&_init_udf, &_make_udf, + &_delete_udf); #else -static Module module = {&init_udf, &make_udf, &delete_udf}; +EXPORT_MODULE(module,&_init_udf, &_make_udf, + &_delete_udf); #endif diff --git a/examples/advanced/modules/external_module.cpp b/examples/advanced/modules/external_module.cpp index 48a5b3b..b75a64b 100644 --- a/examples/advanced/modules/external_module.cpp +++ b/examples/advanced/modules/external_module.cpp @@ -17,7 +17,7 @@ void _make_udf(Model &model) { // This macro can be used for trivial desctruction // This declare de _delete_udf function -DECLARE_DELETER +DECLARE_DELETER(impl) // Don't forget to export the module (and deleter). -EXPORT_MODULE(module) = {&_init_udf, &_make_udf, &_delete_udf}; +EXPORT_MODULE(module,&_init_udf, &_make_udf, &_delete_udf); diff --git a/examples/advanced/modules/modules.hpp b/examples/advanced/modules/modules.hpp index e5aa554..98085e2 100644 --- a/examples/advanced/modules/modules.hpp +++ b/examples/advanced/modules/modules.hpp @@ -27,4 +27,5 @@ DEFINE_MODULE(MODULE_ITEM(init_udf) MODULE_ITEM(make_udf) EXPORT_DEFAULT + #endif diff --git a/examples/basic/modules/default_lib.cpp b/examples/basic/modules/default_lib.cpp index 5b25a3a..2dd3b92 100644 --- a/examples/basic/modules/default_lib.cpp +++ b/examples/basic/modules/default_lib.cpp @@ -10,7 +10,7 @@ int default_bar(int a) { return a * 16; } #ifdef DEFAULT_MODULE // If DEFAULT_MODULE is defined, create a Module object named default_module // containing pointers to default_foo and default_bar functions. -EXPORT_MODULE(default_module) = {&default_foo, &default_bar}; +EXPORT_MODULE(default_module,&default_foo, &default_bar); #else -EXPORT_MODULE(module) = {._foo_m = &default_foo, ._bar_m = &default_bar}; +EXPORT_MODULE(module,&default_foo, &default_bar); #endif diff --git a/examples/basic/modules/external_module.cpp b/examples/basic/modules/external_module.cpp index fc61699..be30587 100644 --- a/examples/basic/modules/external_module.cpp +++ b/examples/basic/modules/external_module.cpp @@ -10,8 +10,7 @@ int foo(int a, int b) { return baz() * (a + b); } int bar(int a) { return a * 16 * 2; } // Don't forget to export the module. -EXPORT_MODULE(module) = {._foo_m = &foo, ._bar_m = &bar}; - +EXPORT_MODULE(module, ._foo_m = &foo, ._bar_m = &bar); /* We could define the default module as described in modules.hpp, but in our case, this external module will never be used as a default module, so the definition doesn't exist. */ diff --git a/examples/main.cpp b/examples/main.cpp deleted file mode 100644 index 193b744..0000000 --- a/examples/main.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "modules/modules.hpp" -#include -#include - -int main() { - // Attempt to load the dynamic library "./external_module.so" and get a handle - // to it. - auto _handle = DynamicLibrary::getLib("./external_module.so"); - - // Try to retrieve the module from the loaded library. - // If the module is not found, use the default module provided by - // default_module. - Module _mod = DynamicLibrary::getModule(_handle, &default_module); - - // Retrieve the function pointer to the function named "_foo_m" from the - // module. - foo_ptr f = _mod._foo_m; - - // Obtain the addresses of the retrieved function and the default function - // "_bar_m". - int *addr1 = (int *)f; - int *addr2 = (int *)default_module._bar_m; - - // Print the addresses of the current function and the default function. - printf("Current function address %p\n", f); - printf("Default function address %p\n", default_module._bar_m); - - // Call the function pointer "f" with arguments 1234 and 1 and print the - // result. - std::cout << f(1234, 1) << std::endl; - return (f(1, 1) == 20) ? 0 : -1; -} diff --git a/public/dynlib/dyn_module.hpp b/public/dynlib/dyn_module.hpp index 6ea6d94..e58eb47 100644 --- a/public/dynlib/dyn_module.hpp +++ b/public/dynlib/dyn_module.hpp @@ -1,33 +1,35 @@ #ifndef __DYN_MODULE_HPP__ #define __DYN_MODULE_HPP__ -#ifdef DEFAULT_MODULE -// If DEFAULT_MODULE is defined, EXPORT is defined as 'extern'. -#define EXPORT extern -#define EXPORT_DEFAULT EXPORT Module default_module; -#else -// If DEFAULT_MODULE is not defined, EXPORT is defined as 'extern "C"'. -#define EXPORT extern "C" -#define EXPORT_DEFAULT -#endif - // Template alias for function pointers. template using FuncPtr = Func *; // Macro to define a type alias for a function pointer. #define USING_TYPE(func) using func##_ptr = FuncPtr; -#define EXPORT_MODULE(__name__) \ - __attribute__((visibility("default"))) Module __name__ -// Macro to define a pointer to a module item (function or variable). -#define MODULE_ITEM(name) name##_ptr _##name##_m = nullptr; -#define DECLARE_DELETER \ - void _delete_udf(impl **pimpl) { \ +#define DECLARE_DELETER(__typename__) \ + void _delete_udf(__typename__ **pimpl) { \ if (pimpl != nullptr) { \ - delete *pimpl; \ + delete *pimpl; /*NOLINT*/ \ *pimpl = nullptr; \ } \ } +#define _EXPOSE_CST_SYMBOL_FROM_SO \ + extern const __attribute__((visibility("default"))) + +#define EXPORT_MODULE(__name__, ...) \ + _EXPOSE_CST_SYMBOL_FROM_SO Module __name__ = {__VA_ARGS__} + +// Macro to define a pointer to a module item (function or variable). +#define MODULE_ITEM(name) name##_ptr _##name##_m = nullptr; + +#ifdef DEFAULT_MODULE +#define EXPORT_DEFAULT _EXPOSE_CST_SYMBOL_FROM_SO Module default_module; +#else +#define EXPORT_DEFAULT +#endif + +#define DECLARE_MODULE(__name__) _EXPOSE_CST_SYMBOL_FROM_SO Module __name__ // Macro to define a module structure containing provided items. #define DEFINE_MODULE(...) \ From 71a3265f1c5ccbfcce7035e78ee9220c00318ec1 Mon Sep 17 00:00:00 2001 From: Casale Date: Thu, 28 Nov 2024 12:56:10 +0100 Subject: [PATCH 10/11] feat: update macro export for msvc --- public/dynlib/dyn_module.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/public/dynlib/dyn_module.hpp b/public/dynlib/dyn_module.hpp index e58eb47..b192c6c 100644 --- a/public/dynlib/dyn_module.hpp +++ b/public/dynlib/dyn_module.hpp @@ -14,8 +14,15 @@ template using FuncPtr = Func *; *pimpl = nullptr; \ } \ } -#define _EXPOSE_CST_SYMBOL_FROM_SO \ + +#if defined(WIN32) || defined(WIN64) + #define _EXPOSE_CST_SYMBOL_FROM_SO \ + extern const __declspec(dllexport) +#else + #define _EXPOSE_CST_SYMBOL_FROM_SO \ extern const __attribute__((visibility("default"))) +#endif + #define EXPORT_MODULE(__name__, ...) \ _EXPOSE_CST_SYMBOL_FROM_SO Module __name__ = {__VA_ARGS__} From 23ae483ef7169b4c2a754f3a18aeb1a512e82862 Mon Sep 17 00:00:00 2001 From: Casale Date: Fri, 29 Nov 2024 21:53:21 +0100 Subject: [PATCH 11/11] refractor: clean --- public/dynlib/dynlib.hpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/public/dynlib/dynlib.hpp b/public/dynlib/dynlib.hpp index 31be15c..72e66b7 100644 --- a/public/dynlib/dynlib.hpp +++ b/public/dynlib/dynlib.hpp @@ -15,6 +15,7 @@ #ifndef __DYNLIB_HPP__ #define __DYNLIB_HPP__ +#include #include #include #include @@ -37,6 +38,10 @@ class DynamicLibrary { // Destructor ~DynamicLibrary() = default; + DynamicLibrary(DynamicLibrary&&) = delete; + DynamicLibrary(const DynamicLibrary&) = delete; + DynamicLibrary& operator=(const DynamicLibrary&)=delete; + DynamicLibrary& operator=(DynamicLibrary&&)=delete; /** * @brief Retrieves a dynamic library object given its path. @@ -47,7 +52,7 @@ class DynamicLibrary { */ static std::shared_ptr getLib(std::string_view path) { try { - auto lib = new DynamicLibrary(path); + auto *lib = new DynamicLibrary(path); return std::shared_ptr(lib); } catch (std::exception &e) { return nullptr; @@ -74,7 +79,7 @@ class DynamicLibrary { * * @param path The path to the dynamic library. */ - DynamicLibrary(std::string_view path) { + explicit DynamicLibrary(std::string_view path) { this->_impl = std::make_unique(path); } @@ -97,10 +102,10 @@ class DynamicLibrary::Impl { * * @param path The path to the dynamic library. */ - Impl(std::string_view path) { + explicit Impl(std::string_view path) { this->handle = dlopen(std::string(path).c_str(), RTLD_NOW); - if (!handle) { + if (handle == nullptr) { throw std::runtime_error("Library cannot be loaded"); } } @@ -120,7 +125,7 @@ class DynamicLibrary::Impl { // Destructor ~Impl() { - if (handle) { + if (handle != nullptr) { dlclose(handle); } handle = nullptr;