Libdlibxx is a generalized C++11 wrapper library around the dl
functions from <dlfcn.h>
to handle the loading and symbol lookup in
dynamic libraries.
- Until
optional
is introduced into the standard, this code relies on Boost.Optional. - A C++11 compiler.
This library is licensed under the FreeBSD license.
To link to this library, pass the command line argument -ldlibxx
.
// Construct a DL handle with a string.
dlibxx::handle lib("library-name.so");
// Given an already constructed DL handle, replace it with a new DL.
dlibxx::handle lib;
lib.load("library-name.so");
If the library was loaded successfully, a call to the loaded()
function will return true
. Error handling code could be performed as
follows after a library has been loaded.
if (!lib.loaded())
{
std::cerr << lib.error() << std::endl;
return 1;
}
Symbol lookup is performed with the lookup
member function. You must
pass in the type of the function as the template parameter and the name
of the function in the dynamic library as the function argument.
If the function was successfully loaded, then the optional
when
cast to bool will return true
, and false
otherwise. After a
successful load, the function can be retrieved via a call to get()
.
// Get an optional<function<Signature>> to the function.
auto func_symbol = lib.lookup<void(int)>("function_name");
if (func_symbol)
{
std::function<void(int)> f = func_symbol.get();
f(5);
}
else
{
std::cout << "Symbol lookup failed.\n";
}
Due to the frequency of which dynamic libraries are used to create an instance of an abstract class satisfying some interface, a simple interface for creating objects from factory methods is provided.
If the instance could not be created, a shared_ptr
containing
nullptr
will be returned.
struct base_type
{
virtual void foo() = 0;
};
std::shared_ptr<base_type> p = lib.create<base_type>("factory_function");
if (p)
p->foo();
else
std::cout << "Unable to create instance.\n";
When loading the dynamic library, it can be specified whether all symbols
should be bound on opening or only when they are referenced. The
default is to bind all symbols when the library is loaded (through the
use of dlibxx::resolve::now
), but lazy binding is also available.
It can be specified in the constructor as follows:
dlibxx::handle lib("library-name.so", dlibxx::resolve::lazy);
Additionally, if you already have a handle, you can set the resolution policy for future library loads like so:
dlibxx::handle lib;
lib.resolve_policy(dlibxx::resolve::lazy);
In addition to the open policy, the options available for the flag
argument to dlopen
are also available. They can be set with the
set_options
member function before opening the library by ORing the
enum values together. The values available are:
dlibxx::options::none = 0,
dlibxx::options::global = RTLD_GLOBAL,
dlibxx::options::local = RTLD_LOCAL,
dlibxx::options::no_delete = RTLD_NODELETE,
dlibxx::options::no_load = RTLD_NOLOAD,
dlibxx::options::deep_bind = RTLD_DEEPBIND
(See man dlopen
for information on options.)
dlibxx::handle lib;
lib.set_options(dlibxx::options::global | dlibxx::options::no_load);
Run make
and change to the examples/
directory.
Run the demo code with ./demo
and when prompted enter:
./liba.so
./libb.so
./libc.so
and observe the results.
To enable and run the unit tests you first need to ensure that you have initialized the submodules so that gtest/gmock are available:
git submodule update --init
Enable testing:
cd build
cmake -DDLIBXX_ENABLE_TESTS=ON ..
Running make
will now build the unit tests as well. Run the tests with one of
the following:
make test
ctest
ctest -V
- to see the output from gtest
In order to successfully contribute code there must be corresponding test code that fully demonstrates that the contribution works as intended. See previous section on enabling and running the unit test suite.