GithubHelp home page GithubHelp logo

martin-olivier / dylib Goto Github PK

View Code? Open in Web Editor NEW
247.0 6.0 32.0 126 KB

C++ cross-platform wrapper around dynamic loading of shared libraries (dll, so, dylib)

Home Page: https://conan.io/center/recipes/dylib

License: MIT License

C++ 73.26% CMake 26.74%
dl dll dylib cpp library dynamic-library dlopen dlsym dynamic-libraries shared-library

dylib's Introduction

dylib

version license cpp

ci coverage

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

⭐ Don't forget to put a star if you like the project!

Compatibility

Works on Linux, MacOS and Windows

Installation

Using a package manager

You can install dylib from vcpkg or conan center:

vcpkg install dylib
conan install --requires=dylib/2.2.1

Using CMake Fetch

You can also fetch dylib to your project using CMake:

include(FetchContent)

FetchContent_Declare(
    dylib
    GIT_REPOSITORY "https://github.com/martin-olivier/dylib"
    GIT_TAG        "v2.2.1"
)

FetchContent_MakeAvailable(dylib)

Documentation

Constructor

The dylib class can load a dynamic library from the system library path

// Load "foo" library from the system library path

dylib lib("foo");

The dylib class can also load a dynamic library from a specific path

// Load "foo" library from relative path "./libs"

dylib lib("./libs", "foo");

// Load "foo" library from full path "/usr/lib"

dylib lib("/usr/lib", "foo");

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

// Windows -> "foo.dll"
// MacOS   -> "libfoo.dylib"
// Linux   -> "libfoo.so"

dylib lib("foo");

// Windows -> "foo.lib"
// MacOS   -> "foo.lib"
// Linux   -> "foo.lib"

dylib lib("foo.lib", dylib::no_filename_decorations);

Get a function or a variable

get_function
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

// Load "foo" dynamic library

dylib lib("foo");

// Get the function "adder" (get_function<T> will return T*)

auto adder = lib.get_function<double(double, double)>("adder");

// Get the variable "pi_value" (get_variable<T> will return T&)

double pi = lib.get_variable<double>("pi_value");

// Use the function "adder" with "pi_value"

double result = adder(pi, pi);

Miscellaneous tools

has_symbol
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

dylib lib("foo");

if (lib.has_symbol("GetModule") == false)
    std::cerr << "symbol 'GetModule' not found in 'foo' lib" << std::endl;

dylib::native_handle_type handle = lib.native_handle();
dylib::native_symbol_type symbol = lib.get_symbol("GetModule");

assert(handle != nullptr && symbol != nullptr);
assert(symbol == dlsym(handle, "GetModule"));

Exceptions

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

Those exceptions inherit from dylib::exception

try {
    dylib lib("foo");
    double pi_value = lib.get_variable<double>("pi_value");
    std::cout << pi_value << std::endl;
} catch (const dylib::load_error &) {
    std::cerr << "failed to load 'foo' library" << std::endl;
} catch (const dylib::symbol_error &) {
    std::cerr << "failed to get 'pi_value' symbol" << std::endl;
}

Example

A full example about the usage of the dylib library is available HERE

Tests

To build unit tests, enter the following commands:

cmake . -B build -DDYLIB_BUILD_TESTS=ON
cmake --build build

To run unit tests, enter the following command inside build directory:

ctest

Community

If you have any question about the usage of the library, do not hesitate to open a discussion

If you want to report a bug or provide a feature, do not hesitate to open an issue or submit a pull request

Contributing

Set the cmake flag DYLIB_BUILD_TESTS to ON to enable tests and make it easier for you to contribute

cmake . -B build -DDYLIB_BUILD_TESTS=ON

Do not forget to sign your commits and use conventional commits when providing a pull request

git commit -s -m "feat: ..."

dylib's People

Contributors

aleksoid1978 avatar breigner01 avatar dolphywind avatar dqhntt avatar jeffersongt avatar kolanich avatar martin-olivier avatar plevold avatar rmaxi-me avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dylib's Issues

Building on Mingw64 (Windows)

[BUG] [Subject of the issue]

Does not seem to build using Mingw64 on Windows 11

lonni@DESKTOP10 MINGW64 ~/dynlibs/dylib
$ cmake . -B build -DBUILD_TESTS=ON
CMake Warning (dev) at C:/msys64/mingw64/share/cmake/Modules/FetchContent.cmake:1264 (message):
  The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is
  not set.  The policy's OLD behavior will be used.  When using a URL
  download, the timestamps of extracted files should preferably be that of
  the time of extraction, otherwise code that depends on the extracted
  contents might not be rebuilt if the URL changes.  The OLD behavior
  preserves the timestamps from the archive instead, but this is usually not
  what you want.  Update your project to the NEW behavior or specify the
  DOWNLOAD_EXTRACT_TIMESTAMP option with a value of true to avoid this
  robustness issue.
Call Stack (most recent call first):
  CMakeLists.txt:25 (FetchContent_Declare)
This warning is for project developers.  Use -Wno-dev to suppress it.

-- Configuring done
-- Generating done
-- Build files have been written to: C:/msys64/home/lonni/dynlibs/dylib/build

then I tried

lonni@DESKTOP10 MINGW64 ~/dynlibs/dylib
$ cmake --build build
[1/8] Building CXX object CMakeFiles/dynamic_lib.dir/tests/lib.cpp.obj
FAILED: CMakeFiles/dynamic_lib.dir/tests/lib.cpp.obj
C:\msys64\mingw64\bin\c++.exe -Ddynamic_lib_EXPORTS  /W4 -std=gnu++11 -MD -MT CMakeFiles/dynamic_lib.dir/tests/lib.cpp.obj -MF CMakeFiles\dynamic_lib.dir\tests\lib.cpp.obj.d -o CMakeFiles/dynamic_lib.dir/tests/lib.cpp.obj -c C:/msys64/home/lonni/dynlibs/dylib/tests/lib.cpp
c++.exe: warning: /W4: linker input file unused because linking not done
c++.exe: error: /W4: linker input file not found: No such file or directory
[2/8] Building CXX object CMakeFiles/unit_tests.dir/tests/tests.cpp.obj
FAILED: CMakeFiles/unit_tests.dir/tests/tests.cpp.obj
C:\msys64\mingw64\bin\c++.exe  -IC:/msys64/home/lonni/dynlibs/dylib/include -isystem C:/msys64/home/lonni/dynlibs/dylib/build/_deps/googletest-src/googletest/include -isystem C:/msys64/home/lonni/dynlibs/dylib/build/_deps/googletest-src/googletest /W4 -std=gnu++11 -MD -MT CMakeFiles/unit_tests.dir/tests/tests.cpp.obj -MF CMakeFiles\unit_tests.dir\tests\tests.cpp.obj.d -o CMakeFiles/unit_tests.dir/tests/tests.cpp.obj -c C:/msys64/home/lonni/dynlibs/dylib/tests/tests.cpp
c++.exe: warning: /W4: linker input file unused because linking not done
c++.exe: error: /W4: linker input file not found: No such file or directory
[3/8] Linking CXX static library lib\libgtest.a
ninja: build stopped: subcommand failed.

Any thoughts?

dylib::get_function() is "neither here nor there"

I'm looking at this kind of line, which we would now write with dylib (2.0):

auto mod = lib.get_function<float *, const char *>("driver::factory");

I find that's not good enough. It's neither here nor there. If you want something fully dynamic, then don't even pass the type via a template, but as a string, e.g.

lib.get_function("driver::factory(float *, const char *)");

where users would manage the calling conventions themselves using a void*.

Similarly, you would also accept

lib.get_function("foo driver::factory(float *, const char *)");

for any foo, i.e. be willing to ignore the return type in the string (but also to check it if that's at all possible).

However, if you do have the namespace, name and signature at compile-time, then I would expect something like:

namespace driver {
foo (*factory)(float *, const char *) = nullptr;
} // namespace driver
lib.populate(driver::factory);

But I'm not sure the latter can be easily implemented in C++. Perhaps a macro would help here (allowing us to use both the value and the name.

Ambiguity regarding references vs values in README

In the README, we have the following example code:

dylib lib("./dynamic_lib.so");
// ... etc.
// Get the global variable pi_value
double pi = lib.get_variable<double>("pi_value");

Shouldn't this be:

double& pi = lib.get_variable<double>("pi_value");

?
Or perhaps

const double& pi = lib.get_variable<double>("pi_value");

?

I mean, I realize you don't need the address, only the value, for the function call to addr(), but this use makes it unclear whether get_variable returns a value or a reference.

Add a named constant for not using OS decorations

Instead of saying:

dylib lib("foo.lib", false);

I should be able to say:

dylib lib("foo.lib", dylib::no_decorations);

or

dylib lib("foo.lib", dylib::no_os_decorations);

or

dylib lib("foo.lib", dylib::use_filename_verbatim);

(whichever of these identifiers you like best.) Otherwise, the reader has to guess what false means. This breaks the Principle of Least Astonishment.

and similarly

dylib lib("foo", dylib::with_os_decorations);

if I want to.

Change API to accept names without the path and the lib prefix

At the moment, one has to specify either the full path of a dynamic library, or the full path sans the extension (and even in that case, you need to incant the magic spell of mylib::extension). In particular, this means that it is up to library user to construct the path correctly, and to make sure that on some OSes, lib is prepended, while on others it is not. Also, the user has to pass a concrete path, something else which is prone to change when porting.

Instead, or at least, in addition to this API, the user code should be able to not be aware of these issues, and just be able to get the library using its basic name (and perhaps a path).

Ideally, I would like to be able to write

auto foo_lib = dylib::load("foo");

and for load() to do the following:

  • Decide whether to use libfoo or foo as the basename.
  • Decide which extension to append
  • Search the appropriate sequence of directories for the shared library file (e.g. on Linux current dir, then LD_LIBRARY_SEARCH_PATH)

alternatively, and if we're stuck with the current API, I would expect this to work:

auto foo_lib_path = dylib::build_path("foo");
dylib foo_lib = dylib::lib(foo_lib_path);

and the above snippets could have variants specifying the location, e.g.

auto foo_lib = dylib::with_search_paths(dylib::search_recursively, "/some/path1", "/some/path2").load("foo");

for the first option or

auto foo_lib_path = dylib::with_search_paths(dylib::search_recursively,  "/some/path1", "/some/path2").build_path("foo");
dylib foo_lib = dylib::lib(foo_lib_path);

for the second option.

DYLIB_API questionable

dylib is not supposed to have any requirements from dynamic-linking libraries. They exist entirely independently of it, ignorant of its existence. So, where would they know about DYLIB_API from? ... nowhere.

As for the code linking against them, using dylib - it shouldn't have to know about that either; it just defines a function type, e.g.int(void), and get_function()'s with that.

So - it seems to me like this:

#if defined(_WIN32) || defined(_WIN64)
#define DYLIB_API extern "C" __declspec(dllexport)
#else
#define DYLIB_API extern "C"
#endif

is unnecessary. Perhaps it belongs in the test library?

Test suite fails...

  • Devuan GNU/Linux Chimaera
  • main branch

Steps to reproduce

configure, build, make test.

Log

$ ctest --rerun-failed --output-on-failure
Test project /home/eyalroz/src/mine/dylib/build
      Start  1: exemple.exemple_test
 1/10 Test  #1: exemple.exemple_test .............***Failed    0.00 sec
Note: Google Test filter = exemple.exemple_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from exemple
[ RUN      ] exemple.exemple_test
/home/eyalroz/src/mine/dylib/tests/tests.cpp:50: Failure
Expected equality of these values:
  true
  false
[  FAILED  ] exemple.exemple_test (0 ms)
[----------] 1 test from exemple (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] exemple.exemple_test

 1 FAILED TEST

      Start  3: multiple_handles.basic_test
 2/10 Test  #3: multiple_handles.basic_test ......***Failed    0.00 sec
Note: Google Test filter = multiple_handles.basic_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from multiple_handles
[ RUN      ] multiple_handles.basic_test
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] multiple_handles.basic_test (0 ms)
[----------] 1 test from multiple_handles (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] multiple_handles.basic_test

 1 FAILED TEST

      Start  6: get_function.bad_symbol
 3/10 Test  #6: get_function.bad_symbol ..........***Failed    0.00 sec
Note: Google Test filter = get_function.bad_symbol
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from get_function
[ RUN      ] get_function.bad_symbol
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] get_function.bad_symbol (0 ms)
[----------] 1 test from get_function (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] get_function.bad_symbol

 1 FAILED TEST

      Start  8: get_variable.bad_symbol
 4/10 Test  #8: get_variable.bad_symbol ..........***Failed    0.00 sec
Note: Google Test filter = get_variable.bad_symbol
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from get_variable
[ RUN      ] get_variable.bad_symbol
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] get_variable.bad_symbol (0 ms)
[----------] 1 test from get_variable (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] get_variable.bad_symbol

 1 FAILED TEST

      Start  9: get_variable.alter_variables
 5/10 Test  #9: get_variable.alter_variables .....***Failed    0.00 sec
Note: Google Test filter = get_variable.alter_variables
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from get_variable
[ RUN      ] get_variable.alter_variables
/home/eyalroz/src/mine/dylib/tests/tests.cpp:151: Failure
Expected equality of these values:
  true
  false
[  FAILED  ] get_variable.alter_variables (0 ms)
[----------] 1 test from get_variable (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] get_variable.alter_variables

 1 FAILED TEST

      Start 10: bad_arguments.null_pointer
 6/10 Test #10: bad_arguments.null_pointer .......***Failed    0.00 sec
Note: Google Test filter = bad_arguments.null_pointer
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from bad_arguments
[ RUN      ] bad_arguments.null_pointer
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] bad_arguments.null_pointer (0 ms)
[----------] 1 test from bad_arguments (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] bad_arguments.null_pointer

 1 FAILED TEST

      Start 12: os_detector.basic_test
 7/10 Test #12: os_detector.basic_test ...........***Failed    0.00 sec
Note: Google Test filter = os_detector.basic_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from os_detector
[ RUN      ] os_detector.basic_test
/home/eyalroz/src/mine/dylib/tests/tests.cpp:221: Failure
Expected equality of these values:
  true
  false
[  FAILED  ] os_detector.basic_test (0 ms)
[----------] 1 test from os_detector (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] os_detector.basic_test

 1 FAILED TEST

      Start 14: has_symbol.basic_test
 8/10 Test #14: has_symbol.basic_test ............***Failed    0.00 sec
Note: Google Test filter = has_symbol.basic_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from has_symbol
[ RUN      ] has_symbol.basic_test
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] has_symbol.basic_test (0 ms)
[----------] 1 test from has_symbol (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] has_symbol.basic_test

 1 FAILED TEST

      Start 15: operator_bool.basic_test
 9/10 Test #15: operator_bool.basic_test .........***Failed    0.00 sec
Note: Google Test filter = operator_bool.basic_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from operator_bool
[ RUN      ] operator_bool.basic_test
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] operator_bool.basic_test (0 ms)
[----------] 1 test from operator_bool (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] operator_bool.basic_test

 1 FAILED TEST

      Start 16: handle_management.basic_test
10/10 Test #16: handle_management.basic_test .....***Failed    0.00 sec
Note: Google Test filter = handle_management.basic_test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from handle_management
[ RUN      ] handle_management.basic_test
unknown file: Failure
C++ exception with description "dylib: error while loading dynamic library "./dynamic_lib.so"
./dynamic_lib.so: cannot open shared object file: No such file or directory" thrown in the test body.
[  FAILED  ] handle_management.basic_test (0 ms)
[----------] 1 test from handle_management (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] handle_management.basic_test

 1 FAILED TEST


0% tests passed, 10 tests failed out of 10

Total Test time (real) =   0.03 sec

The following tests FAILED:
	  1 - exemple.exemple_test (Failed)
	  3 - multiple_handles.basic_test (Failed)
	  6 - get_function.bad_symbol (Failed)
	  8 - get_variable.bad_symbol (Failed)
	  9 - get_variable.alter_variables (Failed)
	 10 - bad_arguments.null_pointer (Failed)
	 12 - os_detector.basic_test (Failed)
	 14 - has_symbol.basic_test (Failed)
	 15 - operator_bool.basic_test (Failed)
	 16 - handle_management.basic_test (Failed)

Errors involving system calls should use the <system_error> mechanisms

C++11 introduced a mechanism - not a fun one, or an easy one to use, but a mechanism - for working with system errors - among others, the ones that, on Unix, you get via errno, and on Windows, with GetLastError() . This is the <system_error> header.

When the error you encounter is one of those - that's what you should throw. See some SO posts about it:

Better check for symbol presence than NULL comparison

At the moment, there are two ways to check whether a library contains a symbol: Trying to get_xxxx it - which may throw, and requires the type; or get_symbol(), which doesn't throw, and may return NULL.

I'm not sure the user should even have to know about NULL in order to check for symbol presence.

There are different ways to approach this. One would be returning an optional; another - a contains() or has() method which returns a boolean.

Issues with the _get_error() function

We shouldn't return a char* from _get_error() - it's not a buffer to modify. In fact... we can't return a const char* either, unless we malloc it ourselves, which would be a leak or would require extra work outside... it needs to be an std::string unfortunately.

Also, we're getting the description, not the error, so perhaps this should be renamed.

Let exceptions inherit standard exception classes with strings

The exception class inherits directly from std::exception. But - your exceptions seem to be either runtime errors (file not found), or invalid arguments (e.g. passing a null extension) etc. So, inherit from the appropriate standard exception class. That would also remove the need for the base class with the string member - don't reinvent the wheel unless you have to.

Support non-extern-C symbols

This is a C++ library for working with shared objects, but it only supports unmangled, C-style functions. That means it doesn't serve its primary function. The library must support any C++ function one can load from a shared object. Naturally, this is ABI-specific, but that's either for the user to configure and build accordingly, or potentially a case for multi-ABI support. The latter is much more complicated, and would be a feature request in itself, but function symbols should definitely be looked up by their mangled name, if they're not extern-C.

Can't use nullptr for handle when you don't know it's a pointer

On Windows, the DLL handle is an HINSTANCE. You don't "officially" know that's a pointer. Yet - later in the code you assign a nullptr to m_handle. If you've managed to remove these assignments from the code then nevermind; otherwise - you need to make sure and assign something you know is assignable. So, you might need to either asset it's a pointer, or define a "null handle" constant. The former is probably easier.

Shortcut for addition of DYLIB_API before functions and variables

Discussed in #11

Originally posted by sethupavan12 February 17, 2022
Just to try out the suggested way ....
I tried generating a C dynamic lib using the libray compiler app in MATLAB and tried to extract a function from the generated DLL.
.m -> DLL -> extract usign DYLIB

Here is the matlab file that has been compiled into a DLL

function output = compile(a)
    % find nth prime number using the sieve of Eratosthenes and store in output
    sieve = ones(1,a);
    sieve(1) = 0;
    sieve(2) = 0;
    % store the primes in output
    output = [];
    for i = 1:a
        if sieve(i) == 1
            output = [output i];
        end
    end

end

and when I load the DLL using a C++ file (main.cpp) I got the following error

dylib: error while loading symbol "compile"
The specified procedure could not be found. 

main.cpp which is modified version of your example in readme.md

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

int main()
{
    try {
        dylib lib("./compileLibCompiler", dylib::extension);

        auto nth_prime = lib.get_function<int(int)>("compile");
        //nth_prime(5);
        int a = 5;
        int ans = nth_prime(a);
        printf("%d\n", ans);

        return 0;

     }
    catch (const dylib::exception &e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

What do you think went wrong here?

macOS dynamic shared module extension probably shouldn't default to .dylib or allow both .dylib and .so

In the README, you specify that the file extension dylib looks for to load macOS shared modules is .dylib.

Unfortunately, there seems to be a mixture of uses of both .dylib and .so for these kinds of shared objects in macOS.

Interestingly, CMake uses .dylib for shared libraries, and .so for shared modules (the kind that are only intended for loading at runtime, and cannot be linked to).

Sample output from building one of my projects with CMake where I have two duplicate libraries, one SHARED and one MODULE:

[ 76%] Linking CXX shared library libgbfp.dylib
[ 82%] Linking CXX shared module libgbfpm.so

See also this CMake issue: https://gitlab.kitware.com/cmake/cmake/-/issues/21189

I feel like the best solution may be to check for both extensions on macOS. What do you think?

Compilation warnings with GCC on Linux

  • Devuan GNU/Linux Chimaera
  • Development branch HEAD

Steps to reproduce

Configured with BUILD_TESTS and built.

Expected behaviour

No compiler warnings

Actual behaviour

/home/eyalroz/src/mine/dylib/tests/lib.cpp:4:18: warning: ‘pi_value’ initialized and declared ‘extern’
    4 | DYLIB_API double pi_value = 3.14159;
      |                  ^~~~~~~~
/home/eyalroz/src/mine/dylib/tests/lib.cpp:5:17: warning: ‘ptr’ initialized and declared ‘extern’
    5 | DYLIB_API void *ptr = (void *)1;
      |                 ^~~

Now, the warnings are pretty innocuous, but - maybe you should create an extern "C" block to avoid them? Or perhaps - a proper-C library?

List all loadable symbols in dylib

Hi there!

In my use case, I need to load multiple dylibs globally at the same time. I want to be able to check if these dylibs have conflicting symbols, so I need a way to list all defined exported symbols to compare them with other libs I want to load.

Something similar can be done using dlinfo on Linux, but I need a cross-platform solution.

What do you think?

Don't use std::function

std::function is a heavy container, whose main use is encapsulating potentially complex invokable objects. See this discussion; why use it? A simple (typed) function pointer should probably do nicely.

Avoid compiler warnings about initializing externs

When building with GCC 10.2.1, I get:

/path/to/dylib/tests/lib.cpp:4:18: warning: ‘pi_value’ initialized and declared ‘extern’
    4 | DYLIB_API double pi_value = 3.14159;
      |                  ^~~~~~~~
/path/to/src/dylib/tests/lib.cpp:5:17: warning: ‘ptr’ initialized and declared ‘extern’
    5 | DYLIB_API void *ptr = (void *)1;
      |                 ^~~

The point is that either you declare something extern (coming from elsewhere), or you initialize it, but not both on the same line.

[Question] How should I use the dylib class as a member of another class?

Deleting the copy constructor and the move assignment operator makes it hard to manage the lifetime of dylib members when working with static constructor-like methods. (When I want to return an error and not throw an exception in the constructor itself)

class HostFXR
{
public:
    hostfxr_initialize_for_dotnet_command_line_fn initialize_for_dotnet_command_line_fn{};
    hostfxr_initialize_for_runtime_config_fn initialize_for_runtime_config_fn{};
    hostfxr_get_runtime_delegate_fn get_runtime_delegate_fn{};
    hostfxr_run_app_fn run_app_fn{};
    hostfxr_close_fn close_fn{};

    static HostFXR init()
    {
        size_t buffer_size = 0;
        get_hostfxr_path(nullptr, &buffer_size, nullptr);
        auto lib_path = static_cast<char_t *>(calloc(buffer_size, sizeof(char_t)));
        get_hostfxr_path(lib_path, &buffer_size, nullptr);

        dylib lib(lib_path);
        HostFXR fxr;

        fxr.initialize_for_dotnet_command_line_fn = *lib.get_function<hostfxr_initialize_for_dotnet_command_line_fn>("hostfxr_initialize_for_dotnet_command_line");

        printf("%p\n", fxr.initialize_for_dotnet_command_line_fn);

        return fxr;
    }
};

This is the only solution I found that works for me, the dylib object goes out of scope but the loaded function pointers don't, which is... questionable (Are they still valid?)

Why is it the case from the first place? Is it related to multithreading / managing the raw handle lifetime?

Code duplication

The code of get_function() and get_variable() is almost identical. Even if you want to distinguish the function names, there's no good reason to duplicate the code in there.

error: called object type 'void (**)(unsigned char, const char *, ...)' is not a function or function pointer

When defining a function with the following:auto printFunction = lib.get_function<void(*)(std::uint8_t, const char*, ...)>("printFunction"); the error appears

Description

MacOS 10.15.7 (Using the dylib.hpp files inside my folder and using the lib.cpp and cmake.txt)

I expected a the function to run from a dylib running on attached to another proccess.
lib.cpp (dynamic_lib.dylib):

#include <iostream>
#include <mach-o/dyld.h>
#include <cstdarg>

int64_t ASLR(int64_t arg1) {
    return (_dyld_get_image_vmaddr_slide(0) + arg1);
}

enum colours : std::uint8_t
{
    regular,
    info,
    warn,
    error
};

// Define the print function prototype
extern "C" {
    void printFunction(std::uint8_t color, const char* message, ...);
}

int main() {
    // Calculate the address using ASLR
    int64_t address = ASLR(0x100c5ae91);

    // Convert the address to a function pointer
    auto printFunction = reinterpret_cast<void(*)(std::uint8_t, const char*, ...)>(address);


    return 0;
}

main.cpp:

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

int main(int argc, char* argv[]) {
    if (argc < 2) {
        std::cerr << "Usage: " << argv[0] << " <message>" << std::endl;
        return 1;
    }

    const char* message = argv[1];

    try {
        // Load the dynamic library
        dylib lib("./", "dynamic_lib.dylib");

        // Get the printFunction symbol
        auto printFunction = lib.get_function<void(*)(std::uint8_t, const char*, ...)>("printFunction");

        // Call the function from the dynamic library
        printFunction(1, message); //<-- error
    } catch (const dylib::exception &ex) {
        std::cerr << "Error: " << ex.what() << std::endl;
        return 1;
    }

    return 0;
}

Considering the dylib is going to be attached to another process, those arguments are required. The actual function may not end up working, and I may need to use IPC. Regardless, I don't know how to fix this error.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.