diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 7329224..89f0122 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -3,74 +3,96 @@ name: CI
on: [push, pull_request]
defaults:
- run:
- shell: bash
+ run:
+ shell: bash
jobs:
- unit_tests:
- strategy:
- fail-fast: false
- matrix:
- os: [windows-latest, macos-latest, ubuntu-latest]
+ unit_tests_cpp11:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-latest, macos-latest, ubuntu-latest]
- name: ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
+ name: ${{ matrix.os }} (C++11)
+ runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v2
+ steps:
+ - uses: actions/checkout@v2
- - name: Generate project files
- run: cmake . -B build -DBUILD_TESTS=ON
+ - name: Generate project files
+ run: cmake . -B build -DCMAKE_CXX_STANDARD=11 -DBUILD_TESTS=ON
- - name: Build dynamic library and unit tests
- run: cmake --build build
+ - name: Build dynamic library and unit tests
+ run: cmake --build build
- - name: Run unit tests
- working-directory: build
- run: ctest
+ - name: Run unit tests
+ working-directory: build
+ run: ctest
- - name: Generate code coverage
- if: ${{ matrix.os == 'ubuntu-latest' }}
- run: gcov -abcfu build/CMakeFiles/unit_tests.dir/tests/tests.cpp.gcda
+ unit_tests_cpp17:
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-latest, macos-latest, ubuntu-latest]
- - name: Send coverage to codecov.io
- if: ${{ matrix.os == 'ubuntu-latest' }}
- uses: codecov/codecov-action@v2
- with:
- files: dylib.hpp.gcov
+ name: ${{ matrix.os }} (C++17)
+ runs-on: ${{ matrix.os }}
- memory_check:
- name: memory check
- runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
- steps:
- - uses: actions/checkout@v2
+ - name: Generate project files
+ run: cmake . -B build -DCMAKE_CXX_STANDARD=17 -DBUILD_TESTS=ON
- - name: Update packages
- run: sudo apt update
+ - name: Build dynamic library and unit tests
+ run: cmake --build build
- - name: Install valgrind
- run: sudo apt install -y valgrind
+ - name: Run unit tests
+ working-directory: build
+ run: ctest
- - name: Generate tests build file
- run: cmake . -B build -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
+ - name: Generate code coverage
+ if: ${{ matrix.os == 'ubuntu-latest' }}
+ run: gcov -abcfu build/CMakeFiles/unit_tests.dir/tests/tests.cpp.gcda
- - name: Build unit tests
- run: cmake --build build
+ - name: Send coverage to codecov.io
+ if: ${{ matrix.os == 'ubuntu-latest' }}
+ uses: codecov/codecov-action@v2
+ with:
+ files: dylib.hpp.gcov
- - name: Run unit tests with valgrind
- working-directory: build
- run: valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./unit_tests
+ memory_check:
+ name: memory check
+ runs-on: ubuntu-latest
- linter:
- name: linter
- runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
- steps:
- - uses: actions/checkout@v2
+ - name: Update packages
+ run: sudo apt update
- - name: Install cpplint
- run: pip install cpplint
+ - name: Install valgrind
+ run: sudo apt install -y valgrind
- - name: Run cpplint
- run: cpplint --linelength=140 --filter=-whitespace/indent,-whitespace/parens include/dylib.hpp
\ No newline at end of file
+ - name: Generate tests build file
+ run: cmake . -B build -DCMAKE_CXX_STANDARD=17 -DBUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Debug
+
+ - name: Build unit tests
+ run: cmake --build build
+
+ - name: Run unit tests with valgrind
+ working-directory: build
+ run: valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 ./unit_tests
+
+ linter:
+ name: linter
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Install cpplint
+ run: pip install cpplint
+
+ - name: Run cpplint
+ run: cpplint --linelength=140 --filter=-whitespace/indent,-whitespace/parens include/dylib.hpp
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7968a7b..d6ad411 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.14)
project(dylib CXX)
-set(CMAKE_CXX_STANDARD 11)
+if(NOT "${CMAKE_CXX_STANDARD}")
+ set(CMAKE_CXX_STANDARD 11)
+endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(dylib INTERFACE)
@@ -49,6 +51,6 @@ if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
endif()
include(GoogleTest)
- gtest_discover_tests(unit_tests WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
+ gtest_discover_tests(unit_tests PROPERTIES TIMEOUT 600 WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
endif()
endif()
diff --git a/README.md b/README.md
index 56fa9f4..ea61009 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-
+
-
-
+
+
@@ -41,13 +41,13 @@ include(FetchContent)
FetchContent_Declare(
dylib
GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
- GIT_TAG "v2.0.0"
+ GIT_TAG "v2.1.0"
)
FetchContent_MakeAvailable(dylib)
```
-You can also click [HERE](https://github.com/martin-olivier/dylib/releases/download/v2.0.0/dylib.hpp) to download the `dylib` header file.
+You can also click [HERE](https://github.com/martin-olivier/dylib/releases/download/v2.1.0/dylib.hpp) to download the `dylib` header file.
# Documentation
@@ -61,28 +61,28 @@ dylib lib("foo");
```
The `dylib` class can also load a dynamic library from a specific path
```c++
-// Load "foo" lib from relative path "./libs"
+// Load "foo" library from relative path "./libs"
dylib lib("./libs", "foo");
-// Load "foo" lib from full path "/usr/lib"
+// Load "foo" library from full path "/usr/lib"
dylib lib("/usr/lib", "foo");
```
-The `dylib` class will automatically add os decorations to the library name, but you can disable that by setting `decorations` parameter to false
+The `dylib` class will automatically add the filename decorations of the current os to the library name, but you can disable that by setting `decorations` parameter to `dylib::no_filename_decorations`
```c++
// Windows -> "foo.dll"
-// MacOS: -> "libfoo.dylib"
-// Linux: -> "libfoo.so"
+// MacOS -> "libfoo.dylib"
+// Linux -> "libfoo.so"
dylib lib("foo");
// Windows -> "foo.lib"
-// MacOS: -> "foo.lib"
-// Linux: -> "foo.lib"
+// MacOS -> "foo.lib"
+// Linux -> "foo.lib"
-dylib lib("foo.lib", false);
+dylib lib("foo.lib", dylib::no_filename_decorations);
```
## Get a function or a variable
@@ -113,15 +113,20 @@ double result = adder(pi, pi);
## Miscellaneous tools
`has_symbol`
-Returns true if the symbol passed as parameter exists in the dynamic library, false otherwise
+Returns true if the symbol passed as parameter exists in the dynamic library, false otherwise
+
+`get_symbol`
+Get a symbol from the dynamic library currently loaded in the object
`native_handle`
-Returns the dynamic library handle
+Returns the dynamic library handle
```c++
void example(dylib &lib)
{
if (lib.has_symbol("GetModule"))
- std::cout << "GetModule symbol has been found" << std::endl;
+ dylib::native_symbol_type sym = lib.get_symbol("GetModule");
+ else
+ std::cout << "GetModule symbol could not be found" << std::endl;
dylib::native_handle_type handle = lib.native_handle();
void *sym = dlsym(handle, "GetModule");
@@ -144,7 +149,7 @@ try {
std::cout << pi_value << std::endl;
}
catch (const dylib::load_error &e) {
- // failed loading "foo" lib
+ // failed loading "foo" library
}
catch (const dylib::symbol_error &e) {
// failed loading "pi_value" symbol
@@ -167,3 +172,14 @@ To run unit tests, enter the following command inside "build" directory:
```sh
ctest
```
+
+# Community
+
+If you have any question about the usage of the library, do not hesitate to open a [discussion](https://github.com/martin-olivier/dylib/discussions)
+
+If you want to report a bug or provide a feature, do not hesitate to open an [issue](https://github.com/martin-olivier/dylib/issues) or submit a [pull request](https://github.com/martin-olivier/dylib/pulls)
+
+> Do not forget to sign your commits and use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) when providing a pull request
+```sh
+git commit -s -m "feat: ..."
+```
\ No newline at end of file
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
index 409c607..f60ad2f 100644
--- a/example/CMakeLists.txt
+++ b/example/CMakeLists.txt
@@ -15,7 +15,7 @@ include(FetchContent)
FetchContent_Declare(
dylib
GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
- GIT_TAG "v2.0.0"
+ GIT_TAG "v2.1.0"
)
FetchContent_MakeAvailable(dylib)
diff --git a/example/README.md b/example/README.md
index fea2997..fa76f0c 100644
--- a/example/README.md
+++ b/example/README.md
@@ -82,7 +82,7 @@ include(FetchContent)
FetchContent_Declare(
dylib
GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
- GIT_TAG "v2.0.0"
+ GIT_TAG "v2.1.0"
)
FetchContent_MakeAvailable(dylib)
diff --git a/include/dylib.hpp b/include/dylib.hpp
index f389f11..62a9b39 100644
--- a/include/dylib.hpp
+++ b/include/dylib.hpp
@@ -1,6 +1,6 @@
/**
* @file dylib.hpp
- * @version 2.0.0
+ * @version 2.1.0
* @brief C++ cross-platform wrapper around dynamic loading of shared libraries
* @link https://github.com/martin-olivier/dylib
*
@@ -16,6 +16,11 @@
#include
#include
+#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
+#define DYLIB_CPP17
+#include
+#endif
+
#if defined(_WIN32) || defined(_WIN64)
#define WIN32_LEAN_AND_MEAN
#include
@@ -46,8 +51,13 @@ class dylib {
static constexpr const char *suffix = DYLIB_WIN_MAC_OTHER(".dll", ".dylib", ".so");
};
using native_handle_type = DYLIB_WIN_OTHER(HINSTANCE, void *);
+ using native_symbol_type = DYLIB_WIN_OTHER(FARPROC, void *);
static_assert(std::is_pointer::value, "Expecting HINSTANCE to be a pointer");
+ static_assert(std::is_pointer::value, "Expecting FARPROC to be a pointer");
+
+ static constexpr bool add_filename_decorations = true;
+ static constexpr bool no_filename_decorations = false;
/**
* This exception is raised when the library failed to load a dynamic library or a symbol
@@ -103,11 +113,11 @@ class dylib {
* @param decorations add os decorations to the library name
*/
///@{
- dylib(const char *dir_path, const char *name, bool decorations = true) {
- if (!dir_path || !name)
+ dylib(const char *dir_path, const char *lib_name, bool decorations = add_filename_decorations) {
+ if (!dir_path || !lib_name)
throw std::invalid_argument("Null parameter");
- std::string final_name = name;
+ std::string final_name = lib_name;
std::string final_path = dir_path;
if (decorations)
@@ -116,32 +126,68 @@ class dylib {
if (final_path != "" && final_path.find_last_of('/') != final_path.size() - 1)
final_path += '/';
- m_handle = _open((final_path + final_name).c_str());
+ m_handle = open((final_path + final_name).c_str());
if (!m_handle)
- throw load_error("Could not load library \"" + final_path + final_name + "\":\n" + _get_error_description());
+ throw load_error("Could not load library \"" + final_path + final_name + "\"\n" + get_error_description());
}
- dylib(const std::string &dir_path, const std::string &name, bool decorations = true)
- : dylib(dir_path.c_str(), name.c_str(), decorations) {}
+ dylib(const std::string &dir_path, const std::string &lib_name, bool decorations = add_filename_decorations)
+ : dylib(dir_path.c_str(), lib_name.c_str(), decorations) {}
- dylib(const std::string &dir_path, const char *name, bool decorations = true)
- : dylib(dir_path.c_str(), name, decorations) {}
+ dylib(const std::string &dir_path, const char *lib_name, bool decorations = add_filename_decorations)
+ : dylib(dir_path.c_str(), lib_name, decorations) {}
- dylib(const char *dir_path, const std::string &name, bool decorations = true)
- : dylib(dir_path, name.c_str(), decorations) {}
+ dylib(const char *dir_path, const std::string &lib_name, bool decorations = add_filename_decorations)
+ : dylib(dir_path, lib_name.c_str(), decorations) {}
- explicit dylib(const std::string &name, bool decorations = true)
- : dylib("", name.c_str(), decorations) {}
+ explicit dylib(const std::string &lib_name, bool decorations = add_filename_decorations)
+ : dylib("", lib_name.c_str(), decorations) {}
- explicit dylib(const char *name, bool decorations = true)
- : dylib("", name, decorations) {}
- ///@}
+ explicit dylib(const char *lib_name, bool decorations = add_filename_decorations)
+ : dylib("", lib_name, decorations) {}
+
+#ifdef DYLIB_CPP17
+ explicit dylib(const std::filesystem::path &lib_path)
+ : dylib("", lib_path.string().c_str(), no_filename_decorations) {}
+ dylib(const std::filesystem::path &dir_path, const std::string &lib_name, bool decorations = add_filename_decorations)
+ : dylib(dir_path.string().c_str(), lib_name.c_str(), decorations) {}
+
+ dylib(const std::filesystem::path &dir_path, const char *lib_name, bool decorations = add_filename_decorations)
+ : dylib(dir_path.string().c_str(), lib_name, decorations) {}
+#endif
+ ///@}
~dylib() {
if (m_handle)
- _close(m_handle);
+ close(m_handle);
+ }
+
+ /**
+ * Get a symbol from the dynamic library currently loaded in the object
+ *
+ * @throws dylib::symbol_error if the symbol could not be found
+ *
+ * @param symbol_name the symbol name to get from the dynamic library
+ *
+ * @return a pointer to the requested symbol
+ */
+ native_symbol_type get_symbol(const char *symbol_name) const {
+ if (!symbol_name)
+ throw std::invalid_argument("Null parameter");
+ if (!m_handle)
+ throw std::logic_error("The dynamic library handle is null");
+
+ auto symbol = locate_symbol(m_handle, symbol_name);
+
+ if (symbol == nullptr)
+ throw symbol_error("Could not get symbol \"" + std::string(symbol_name) + "\"\n" + get_error_description());
+ return symbol;
+ }
+
+ native_symbol_type get_symbol(const std::string &symbol_name) const {
+ return get_symbol(symbol_name.c_str());
}
/**
@@ -150,18 +196,18 @@ class dylib {
* @throws dylib::symbol_error if the symbol could not be found
*
* @param T the template argument must be the function prototype to get
- * @param name the symbol name of a function to get from the dynamic library
+ * @param symbol_name the symbol name of a function to get from the dynamic library
*
* @return a pointer to the requested function
*/
template
- T *get_function(const char *name) const {
- return reinterpret_cast(locate_symbol(name));
+ T *get_function(const char *symbol_name) const {
+ return reinterpret_cast(get_symbol(symbol_name));
}
template
- T *get_function(const std::string &name) const {
- return get_function(name.c_str());
+ T *get_function(const std::string &symbol_name) const {
+ return get_function(symbol_name.c_str());
}
/**
@@ -170,35 +216,33 @@ class dylib {
* @throws dylib::symbol_error if the symbol could not be found
*
* @param T the template argument must be the type of the variable to get
- * @param name the symbol name of a variable to get from the dynamic library
+ * @param symbol_name the symbol name of a variable to get from the dynamic library
*
* @return a reference to the requested variable
*/
template
- T &get_variable(const char *name) const {
- return *reinterpret_cast(locate_symbol(name));
+ T &get_variable(const char *symbol_name) const {
+ return *reinterpret_cast(get_symbol(symbol_name));
}
template
- T &get_variable(const std::string &name) const {
- return get_variable(name.c_str());
+ T &get_variable(const std::string &symbol_name) const {
+ return get_variable(symbol_name.c_str());
}
/**
* Check if a symbol exists in the currently loaded dynamic library.
* This method will return false if no dynamic library is currently loaded
- * or if the symbol equals nullptr
+ * or if the symbol name is nullptr
*
- * @param symbol the symbol name to look for
+ * @param symbol_name the symbol name to look for
*
* @return true if the symbol exists in the dynamic library, false otherwise
*/
- bool has_symbol(const char *symbol) const noexcept {
- if (!symbol)
- return false;
- if (!m_handle)
+ bool has_symbol(const char *symbol_name) const noexcept {
+ if (!m_handle || !symbol_name)
return false;
- return _get_symbol(m_handle, symbol) != nullptr;
+ return locate_symbol(m_handle, symbol_name) != nullptr;
}
bool has_symbol(const std::string &symbol) const noexcept {
@@ -215,7 +259,7 @@ class dylib {
protected:
native_handle_type m_handle{nullptr};
- static native_handle_type _open(const char *path) noexcept {
+ static native_handle_type open(const char *path) noexcept {
#if defined(_WIN32) || defined(_WIN64)
return LoadLibraryA(path);
#else
@@ -223,29 +267,15 @@ class dylib {
#endif
}
- void *locate_symbol(const char *name) const {
- if (!name)
- throw std::invalid_argument("Null parameter");
- if (!m_handle)
- throw std::logic_error("The dynamic library handle is null");
-
- auto symbol = _get_symbol(m_handle, name);
-
- if (symbol == nullptr)
- throw symbol_error("Could not locate symbol \"" + std::string(name) + "\":\n" + _get_error_description());
- return symbol;
- }
-
- static DYLIB_WIN_OTHER(FARPROC, void *)
- _get_symbol(native_handle_type lib, const char *name) noexcept {
+ static native_symbol_type locate_symbol(native_handle_type lib, const char *name) noexcept {
return DYLIB_WIN_OTHER(GetProcAddress, dlsym)(lib, name);
}
- static void _close(native_handle_type lib) noexcept {
+ static void close(native_handle_type lib) noexcept {
DYLIB_WIN_OTHER(FreeLibrary, dlclose)(lib);
}
- static std::string _get_error_description() noexcept {
+ static std::string get_error_description() noexcept {
#if defined(_WIN32) || defined(_WIN64)
constexpr const size_t buf_size = 512;
auto error_code = GetLastError();
@@ -265,3 +295,4 @@ class dylib {
#undef DYLIB_WIN_MAC_OTHER
#undef DYLIB_WIN_OTHER
+#undef DYLIB_CPP17
diff --git a/tests/tests.cpp b/tests/tests.cpp
index 3f2fe22..1f7da14 100644
--- a/tests/tests.cpp
+++ b/tests/tests.cpp
@@ -126,7 +126,7 @@ TEST(invalid_argument, null_pointer) {
}
TEST(manual_decorations, basic_test) {
- dylib lib(".", dylib::filename_components::prefix + std::string("dynamic_lib") + dylib::filename_components::suffix, false);
+ dylib lib(".", dylib::filename_components::prefix + std::string("dynamic_lib") + dylib::filename_components::suffix, dylib::no_filename_decorations);
auto pi = lib.get_variable("pi_value");
EXPECT_EQ(pi, 3.14159);
}
@@ -185,6 +185,25 @@ TEST(system_lib, basic_test) {
#endif
}
+#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
+TEST(filesystem, basic_test) {
+ bool has_sym = dylib(std::filesystem::path("."), "dynamic_lib").has_symbol("pi_value");
+ EXPECT_TRUE(has_sym);
+
+ bool found = false;
+ for (const auto &file : std::filesystem::recursive_directory_iterator(".")) {
+ if (file.path().extension() == dylib::filename_components::suffix) {
+ try {
+ dylib lib(file.path());
+ if (lib.has_symbol("pi_value"))
+ found = true;
+ } catch (const std::exception &) {}
+ }
+ }
+ EXPECT_TRUE(found);
+}
+#endif
+
int main(int ac, char **av) {
testing::InitGoogleTest(&ac, av);
return RUN_ALL_TESTS();