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 + 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/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/loader.cpp b/examples/advanced/loader.cpp new file mode 100644 index 0000000..fb416c2 --- /dev/null +++ b/examples/advanced/loader.cpp @@ -0,0 +1,15 @@ +#include "loader.hpp" +#include "modules/modules.hpp" +#include + +namespace UnsafeUDF { +[[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 UnsafeUDF diff --git a/examples/advanced/loader.hpp b/examples/advanced/loader.hpp new file mode 100644 index 0000000..51134b6 --- /dev/null +++ b/examples/advanced/loader.hpp @@ -0,0 +1,12 @@ +#ifndef LOADER_HPP +#define LOADER_HPP +#include +#include + +#include + +namespace UnsafeUDF { + [[nodiscard]] std::shared_ptr init_lib(std::string_view path); +} + +#endif diff --git a/examples/advanced/main.cpp b/examples/advanced/main.cpp new file mode 100644 index 0000000..a8dda0e --- /dev/null +++ b/examples/advanced/main.cpp @@ -0,0 +1,34 @@ +#include "modules/modules.hpp" +#include +#include +#include +#include +#include "loader.hpp" + +#define LOAD_LIB(name) auto _ = UnsafeUDF::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..09da369 --- /dev/null +++ b/examples/advanced/meson.build @@ -0,0 +1,44 @@ +library( + 'wrong', + 'modules/wrong.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'], +) + +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','modules/modules.cpp', + link_with: [default,model], + 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..74e4a38 --- /dev/null +++ b/examples/advanced/modules/default_lib.cpp @@ -0,0 +1,27 @@ +#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 +EXPORT_MODULE(default_module,&_init_udf, &_make_udf, + &_delete_udf); +#else +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 new file mode 100644 index 0000000..b75a64b --- /dev/null +++ b/examples/advanced/modules/external_module.cpp @@ -0,0 +1,23 @@ +#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 = 11272024; +} + +// This macro can be used for trivial desctruction +// This declare de _delete_udf function +DECLARE_DELETER(impl) + +// Don't forget to export the module (and deleter). +EXPORT_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..280e6f9 --- /dev/null +++ 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 new file mode 100644 index 0000000..98085e2 --- /dev/null +++ b/examples/advanced/modules/modules.hpp @@ -0,0 +1,31 @@ +#ifndef __MYMODULE_HPP__ +#define __MYMODULE_HPP__ +#include + +struct impl; +struct Model; + +namespace UnsafeUDF { +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(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)) + +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/modules/default_lib.cpp b/examples/basic/modules/default_lib.cpp similarity index 78% rename from examples/modules/default_lib.cpp rename to examples/basic/modules/default_lib.cpp index 15ded34..2dd3b92 100644 --- a/examples/modules/default_lib.cpp +++ b/examples/basic/modules/default_lib.cpp @@ -1,4 +1,5 @@ #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; } @@ -9,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. -Module default_module = {&default_foo, &default_bar}; +EXPORT_MODULE(default_module,&default_foo, &default_bar); #else -Module module = {._foo_m = &default_foo, ._bar_m = &default_bar}; +EXPORT_MODULE(module,&default_foo, &default_bar); #endif diff --git a/examples/modules/external_module.cpp b/examples/basic/modules/external_module.cpp similarity index 77% rename from examples/modules/external_module.cpp rename to examples/basic/modules/external_module.cpp index 92d3a70..be30587 100644 --- a/examples/modules/external_module.cpp +++ b/examples/basic/modules/external_module.cpp @@ -1,3 +1,4 @@ +#include "dynlib/dyn_module.hpp" #include "modules.hpp" // It's possible to declare "private" functions. @@ -9,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. -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. */ \ No newline at end of file +so the definition doesn't exist. */ diff --git a/examples/modules/modules.hpp b/examples/basic/modules/modules.hpp similarity index 84% rename from examples/modules/modules.hpp rename to examples/basic/modules/modules.hpp index cde4d08..6e581de 100644 --- a/examples/modules/modules.hpp +++ b/examples/basic/modules/modules.hpp @@ -1,7 +1,7 @@ #ifndef __MYMODULE_HPP__ #define __MYMODULE_HPP__ -#include "../../lib/dyn_module.hpp" +#include using foo_ptr = int (*)(int, int); using bar_ptr = int (*)(int); 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/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/examples/makefile b/examples/makefile index 77cac51..61a7e5e 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 -fvisibility=hidden -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 -fvisibility=hidden -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 -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} + +run: + ${BUILD_DIR}/main 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/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/meson.build b/meson.build new file mode 100644 index 0000000..43d6d80 --- /dev/null +++ b/meson.build @@ -0,0 +1,12 @@ +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 new file mode 100644 index 0000000..b192c6c --- /dev/null +++ b/public/dynlib/dyn_module.hpp @@ -0,0 +1,47 @@ +#ifndef __DYN_MODULE_HPP__ +#define __DYN_MODULE_HPP__ + +// 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 DECLARE_DELETER(__typename__) \ + void _delete_udf(__typename__ **pimpl) { \ + if (pimpl != nullptr) { \ + delete *pimpl; /*NOLINT*/ \ + *pimpl = nullptr; \ + } \ + } + +#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__} + +// 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(...) \ + typedef struct Module { \ + __VA_ARGS__ \ + } *ImportedModule; + +#endif diff --git a/lib/dynlib.hpp b/public/dynlib/dynlib.hpp similarity index 69% rename from lib/dynlib.hpp rename to public/dynlib/dynlib.hpp index e6f5576..72e66b7 100644 --- a/lib/dynlib.hpp +++ b/public/dynlib/dynlib.hpp @@ -1,10 +1,12 @@ /** * @file dynlib.hpp - * @brief Defines the DynamicLibrary class for loading and accessing symbols from dynamic libraries. + * @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. + * 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 @@ -13,6 +15,7 @@ #ifndef __DYNLIB_HPP__ #define __DYNLIB_HPP__ +#include #include #include #include @@ -26,36 +29,39 @@ 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); + template T* getItem(std::string_view fname); // 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. - * + * * @param path The path to the dynamic library. - * @return std::shared_ptr A shared pointer to the dynamic library object. + * @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); + auto *lib = new DynamicLibrary(path); return std::shared_ptr(lib); } catch (std::exception &e) { - std::cout<_impl = std::make_unique(path); } @@ -84,8 +90,7 @@ class DynamicLibrary { std::unique_ptr _impl; }; - -///LINUX IMPLEMENTATION +/// LINUX IMPLEMENTATION #if defined(__linux__) #include @@ -94,34 +99,33 @@ 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) { + 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"); } } /** * @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) { + 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; } // Destructor ~Impl() { - if (handle) { + if (handle != nullptr) { dlclose(handle); } handle = nullptr; @@ -133,7 +137,6 @@ class DynamicLibrary::Impl { #endif - #if defined(WIN32) || defined(WIN64) #include @@ -141,7 +144,7 @@ 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) { @@ -154,15 +157,14 @@ class DynamicLibrary::Impl { /** * @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) { + template T* getFunction(std::string_view fname) { FARPROC symbol = GetProcAddress(handle, fname.data()); - return reinterpret_cast(symbol); + return reinterpret_cast(symbol); } // Destructor @@ -178,44 +180,37 @@ class DynamicLibrary::Impl { }; #endif - // 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"<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"); + } + + 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"); }