Skip to content

Commit

Permalink
Version 2.0.0 (#48)
Browse files Browse the repository at this point in the history
dylib class is now RAII compliant
breaking changes for dylib ctor
os prefix support (lib under unix)
handle_error exception is now called load_error
return type of get_function is now a function pointer instead of an std::function

Signed-off-by: Martin Olivier <[email protected]>
Co-authored-by: Eyal Rozenberg <[email protected]>
  • Loading branch information
martin-olivier and eyalroz authored Jun 14, 2022
1 parent e06af0f commit 657cd9f
Show file tree
Hide file tree
Showing 12 changed files with 373 additions and 500 deletions.
2 changes: 0 additions & 2 deletions .codecov.yml

This file was deleted.

28 changes: 17 additions & 11 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,25 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Generate Project Files
- name: Generate project files
run: cmake . -B build -DBUILD_TESTS=ON

- name: Build Dynamic Library and Unit Tests
- name: Build dynamic library and unit tests
run: cmake --build build

- name: Run Unit Tests
- name: Run unit tests
working-directory: build
run: ctest

- name: Send Coverage to codecov.io
- name: Generate code coverage
if: ${{ matrix.os == 'ubuntu-latest' }}
run: bash <(curl -s https://codecov.io/bash)
run: gcov -abcfu build/CMakeFiles/unit_tests.dir/tests/tests.cpp.gcda

- name: Send coverage to codecov.io
if: ${{ matrix.os == 'ubuntu-latest' }}
uses: codecov/codecov-action@v2
with:
files: dylib.hpp.gcov

memory_check:
name: memory check
Expand All @@ -40,19 +46,19 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Update Packages
- name: Update packages
run: sudo apt update

- name: Install Valgrind
- name: Install valgrind
run: sudo apt install -y valgrind

- name: Generate Tests Build File
- name: Generate tests build file
run: cmake . -B build -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug

- name: Build Unit Tests
- name: Build unit tests
run: cmake --build build

- name: Run Unit Tests with Valgrind
- name: Run unit tests with valgrind
working-directory: build
run: valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./unit_tests

Expand All @@ -67,4 +73,4 @@ jobs:
run: pip install cpplint

- name: Run cpplint
run: cpplint --linelength=120 --filter=-whitespace/indent include/dylib.hpp
run: cpplint --linelength=140 --filter=-whitespace/indent,-whitespace/parens include/dylib.hpp
15 changes: 6 additions & 9 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
*~
\#*#

# Binary files

*.o
*.so
*.dll
*.dylib
unit_tests

# Build Folders

cmake-build-*
build
Debug

# Coverage files

*.gcda
*.gcno
*.gcov
10 changes: 5 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
endif()

add_library(dynamic_lib SHARED tests/lib.cpp)
set_target_properties(dynamic_lib PROPERTIES PREFIX "")
target_link_libraries(dynamic_lib PRIVATE dylib)

enable_testing()

if(UNIX AND NOT APPLE)
add_compile_options(-fprofile-arcs -ftest-coverage)
endif()

add_executable(unit_tests tests/tests.cpp)
target_link_libraries(unit_tests PRIVATE gtest_main)
target_link_libraries(unit_tests PRIVATE dylib)
target_link_libraries(unit_tests PRIVATE gtest_main dylib)

if(UNIX AND NOT APPLE)
add_compile_options(--coverage)
target_link_libraries(unit_tests PRIVATE gcov)
endif()

Expand Down
155 changes: 70 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,29 @@
# Dylib - C++ cross-platform dynamic library loader
[![Dylib](https://img.shields.io/badge/Dylib-v1.8.3-blue.svg)](https://github.com/martin-olivier/dylib/releases/tag/v1.8.3)
[![MIT license](https://img.shields.io/badge/License-MIT-orange.svg)](https://github.com/martin-olivier/dylib/blob/main/LICENSE)
[![CPP Version](https://img.shields.io/badge/C++-11_and_above-darkgreen.svg)](https://isocpp.org/)

[![GitHub watchers](https://img.shields.io/github/watchers/martin-olivier/dylib?style=social)](https://github.com/martin-olivier/dylib/watchers/)
[![GitHub forks](https://img.shields.io/github/forks/martin-olivier/dylib?style=social)](https://github.com/martin-olivier/dylib/network/members/)
[![GitHub stars](https://img.shields.io/github/stars/martin-olivier/dylib?style=social)](https://github.com/martin-olivier/dylib/stargazers/)

[![workflow](https://github.com/martin-olivier/dylib/actions/workflows/CI.yml/badge.svg)](https://github.com/martin-olivier/dylib/actions/workflows/CI.yml)
[![codecov](https://codecov.io/gh/martin-olivier/dylib/branch/main/graph/badge.svg?token=4V6A9B7PII)](https://codecov.io/gh/martin-olivier/dylib)

[![GitHub download](https://img.shields.io/github/downloads/martin-olivier/dylib/total?style=for-the-badge)](https://github.com/martin-olivier/dylib/releases/download/v1.8.3/dylib.hpp)
<p align="center">
<img height="200" src="https://repository-images.githubusercontent.com/354428648/5ef81a00-95b1-11eb-88a1-e1760bd99ab2" alt="dylib"/>
</p>

<p align="center">
<a href="https://github.com/martin-olivier/dylib/releases/tag/v2.0.0">
<img src="https://img.shields.io/badge/Version-2.0.0-blue.svg" alt="version"/>
</a>
<a href="https://github.com/martin-olivier/dylib/blob/main/LICENSE">
<img src="https://img.shields.io/badge/License-MIT-orange.svg" alt="license"/>
</a>
<a href="https://isocpp.org/">
<img src="https://img.shields.io/badge/Compatibility-C++11-darkgreen.svg" alt="cppversion"/>
</a>
</p>

<p align="center">
<a href="https://github.com/martin-olivier/dylib/actions/workflows/CI.yml">
<img src="https://github.com/martin-olivier/dylib/actions/workflows/CI.yml/badge.svg" alt="ci"/>
</a>
<a href="https://codecov.io/gh/martin-olivier/dylib">
<img src="https://codecov.io/gh/martin-olivier/dylib/branch/main/graph/badge.svg?token=4V6A9B7PII" alt="codecov"/>
</a>
</p>

# Dylib

The goal of this C++ library is to load dynamic libraries (.so, .dll, .dylib) and access its functions and global variables at runtime.

Expand All @@ -28,51 +41,48 @@ include(FetchContent)
FetchContent_Declare(
dylib
GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
GIT_TAG "v1.8.3"
GIT_TAG "v2.0.0"
)
FetchContent_MakeAvailable(dylib)
```

You can also click [HERE](https://github.com/martin-olivier/dylib/releases/download/v1.8.3/dylib.hpp) to download the `dylib` header file.
You can also click [HERE](https://github.com/martin-olivier/dylib/releases/download/v2.0.0/dylib.hpp) to download the `dylib` header file.

# Documentation

## Dylib class
## Constructor

The `dylib` class can load a dynamic library at runtime:
The `dylib` class can load a dynamic library from the system library path
```c++
dylib lib("./dynamic_lib.so");
```
The `dylib` class can detect the file extension of the actual os using `dylib::extension`:
```c++
dylib lib("./dynamic_lib", dylib::extension);
// Load "foo" library from the system library path

dylib lib("foo");
```
or
The `dylib` class can also load a dynamic library from a specific path
```c++
dylib lib;
lib.open("./dynamic_lib", dylib::extension);
```

## Open and close
// Load "foo" lib from relative path "./libs"
`open`
Load a dynamic library into the object. If a dynamic library was already opened, it will be unloaded and replaced
dylib lib("./libs", "foo");
`close`
Unload the dynamic library currently loaded in the object. This function will be automatically called by the class destructor
```c++
// Load ./dynamic_lib.so
// Load "foo" lib from full path "/usr/lib"
dylib lib("./dynamic_lib.so");
dylib lib("/usr/lib", "foo");
```

// Unload ./dynamic_lib.so and load ./other_dynamic_lib.so
The `dylib` class will automatically add os decorations to the library name, but you can disable that by setting `decorations` parameter to false
```c++
// Windows -> "foo.dll"
// MacOS: -> "libfoo.dylib"
// Linux: -> "libfoo.so"

lib.open("./other_dynamic_lib.so");
dylib lib("foo");

// Unload ./other_dynamic_lib.so
// Windows -> "foo.lib"
// MacOS: -> "foo.lib"
// Linux: -> "foo.lib"

lib.close();
dylib lib("foo.lib", false);
```
## Get a function or a variable
Expand All @@ -83,28 +93,25 @@ Get a function from the dynamic library currently loaded in the object
`get_variable`
Get a global variable from the dynamic library currently loaded in the object
```c++
// Load ./dynamic_lib.so
// Load "foo" dynamic library
dylib lib("./dynamic_lib.so");
dylib lib("foo");
// Get the function adder (get_function<T> will return std::function<T>)
// Get the function "adder" (get_function<T> will return T*)
auto adder = lib.get_function<double(double, double)>("adder");
// Get the global variable pi_value
// Get the variable "pi_value" (get_variable<T> will return T&)
const double &pi = lib.get_variable<double>("pi_value");
double pi = lib.get_variable<double>("pi_value");
// Use the function adder with pi_value
// Use the function "adder" with "pi_value"
double result = adder(pi, pi);
```

## Miscellaneous tools

`operator bool`
Returns true if a dynamic library is currently loaded in the object, false otherwise

`has_symbol`
Returns true if the symbol passed as parameter exists in the dynamic library, false otherwise

Expand All @@ -113,72 +120,50 @@ Returns the dynamic library handle
```c++
void example(dylib &lib)
{
if (lib)
std::cout << "Something is currently loaded in the dylib object" << std::endl;

if (lib.has_symbol("GetModule"))
std::cout << "GetModule symbol has been found" << std::endl;

dylib::native_handle_type handle = lib.native_handle();
void *sym = dlsym(handle, "GetModule");
}
```
## Dylib exceptions
## Exceptions
`handle_error`
`load_error`
This exception is raised when the library failed to load or the library encountered symbol resolution issues
`symbol_error`
This exception is raised when the library failed to load a symbol.
This usually happens when you forgot to put `DYLIB_API` before a library function or variable
This exception is raised when the library failed to load a symbol
Those exceptions inherit from `dylib::exception`
```c++
try {
dylib lib("./dynamic_lib.so");
const double &pi_value = lib.get_variable<double>("pi_value");
dylib lib("foo");
double pi_value = lib.get_variable<double>("pi_value");
std::cout << pi_value << std::endl;
}
catch (const dylib::exception &e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
catch (const dylib::load_error &e) {
// failed loading "foo" lib
}
catch (const dylib::symbol_error &e) {
// failed loading "pi_value" symbol
}
return EXIT_SUCCESS;
```

# Example

A full example about the usage of the `dylib` library is available [HERE](example)

# Tips
# Tests

## Remove the lib prefix

> If you use CMake to build a dynamic library, running the below CMake rule will allow you to remove the prefix `lib` for macOS and linux, ensuring that the library shares the same name on all the different OS:
```cmake
set_target_properties(target PROPERTIES PREFIX "")
```

| | Without CMake rule | With CMake rule |
|:-------:|:----------------------|:----------------|
| Linux | ***lib***malloc.so | malloc.so |
| MacOS | ***lib***malloc.dylib | malloc.dylib |
| Windows | malloc.dll | malloc.dll |

## Build and run unit tests

To build the unit tests, enter the following commands:
To build unit tests, enter the following commands:
```sh
cmake . -B build -DBUILD_TESTS=ON
cmake --build build
```

To run the unit tests, enter the following command:
To run unit tests, enter the following command inside "build" directory:
```sh
# on unix
./unit_tests

# on windows, in "Debug" folder
./unit_tests.exe
ctest
```
8 changes: 3 additions & 5 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ project(dylib_example)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})

# dylib fetch

Expand All @@ -15,16 +15,14 @@ include(FetchContent)
FetchContent_Declare(
dylib
GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
GIT_TAG "v1.8.3"
GIT_TAG "v2.0.0"
)

FetchContent_MakeAvailable(dylib)

# build lib.cpp into a shared library

add_library(dynamic_lib SHARED lib.cpp)
set_target_properties(dynamic_lib PROPERTIES PREFIX "")
target_link_libraries(dynamic_lib PRIVATE dylib)

# build main.cpp into an executable

Expand Down
Loading

0 comments on commit 657cd9f

Please sign in to comment.