Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/cpp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ jobs:
make
make external_module
cd build
./main
./main external_module.so

8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
**/build
**/build

**/builddir

# LSP
.cache/
compile_commands.json
45 changes: 31 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,57 @@
# Dynamic Library Loader

<div align="left">

[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

</div>
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
<!--toc:start-->

- [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)
<!--toc:end-->

# 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 `<dyn_module.hpp>` header file to access macro definitions.
1. **Include Header:**
Include the `<dyn_module.hpp>` 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.

2. **Implement Functions:** Implement the necessary functions required by your module.

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):

Expand All @@ -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.
This project is licensed under the MIT License, allowing for unrestricted use, modification, and distribution. Please review the LICENSE file for detailed licensing terms.
15 changes: 15 additions & 0 deletions examples/advanced/loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "loader.hpp"
#include "modules/modules.hpp"
#include <dynlib/dynlib.hpp>

namespace UnsafeUDF {
[[nodiscard]] std::shared_ptr<DynamicLibrary> init_lib(std::string_view path) {
auto _handle = DynamicLibrary::getLib(path);
auto _mod = DynamicLibrary::getModule<Module>(_handle);

make_udf = _mod._make_udf_m;
init_udf = _mod._init_udf_m;
delete_udf = _mod._delete_udf_m;
return _handle;
}
} // namespace UnsafeUDF
12 changes: 12 additions & 0 deletions examples/advanced/loader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#ifndef LOADER_HPP
#define LOADER_HPP
#include <string_view>
#include <memory>

#include <dynlib/dynlib.hpp>

namespace UnsafeUDF {
[[nodiscard]] std::shared_ptr<DynamicLibrary> init_lib(std::string_view path);
}

#endif
34 changes: 34 additions & 0 deletions examples/advanced/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "modules/modules.hpp"
#include <dynlib/dynlib.hpp>
#include <exception>
#include <stdexcept>
#include <string_view>
#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;
}
44 changes: 44 additions & 0 deletions examples/advanced/meson.build
Original file line number Diff line number Diff line change
@@ -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'],
)
27 changes: 27 additions & 0 deletions examples/advanced/modules/default_lib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "modules.hpp"
#include <iostream>

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
23 changes: 23 additions & 0 deletions examples/advanced/modules/external_module.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "modules.hpp"
#include <iostream>

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);
20 changes: 20 additions & 0 deletions examples/advanced/modules/modules.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
}
31 changes: 31 additions & 0 deletions examples/advanced/modules/modules.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef __MYMODULE_HPP__
#define __MYMODULE_HPP__
#include <dynlib/dyn_module.hpp>

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
26 changes: 26 additions & 0 deletions examples/advanced/modules/wrong.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@

#include "modules.hpp"
#include <iostream>

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};
39 changes: 39 additions & 0 deletions examples/basic/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "modules/modules.hpp"
#include <dynlib/dynlib.hpp>
#include <iostream>

int exec(std::shared_ptr<DynamicLibrary> 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<DynamicLibrary> _handle;

if (argc == 2) {
std::cout << "Loading " << argv[1] << std::endl;
_handle = DynamicLibrary::getLib(argv[1]);
}

return exec(_handle);
}
Loading