First we need to get the library we want to load, here we will make a small example library
Loading dynamic libraries at runtime is an operation where at runtime you use the OS’ API to load symbols from a shared library. This library loads named functions, that being functions whose names are not under the influence of name mangling. Because of this your functions have to be exported with C semantics
To create a library simply write some C or C++ code like this:
Header:
#pragma once
extern "C"
{
void begin();
}
Source file:
#include "libtest.hpp"
#include <iostream>
void begin()
{
std::cout << "begin" << std::endl;
}
As you can see the library we made is a C++ one, to compile with C semantics you need to wrap your functions under extern "C"
otherwise name mangling will be enabled
Make sure that if compiling for a Unix system, you need to enable -fPIC
in your compiler flags. This way you can have PIC(Position Independent Code)
On Windows make sure all the functions you want to export are prefixed with __declspec(dllexport)
otherwise they will not be exported from the DLL
The C++ API provides some good syntactic sugar like templates that generally make your life better. The library looks like this:
namespace URLL
{
void* dlopen(const char* location) noexcept;
template<typename T, typename... T2>
void* dlsym(void* handle, const char* name, std::function<T(T2...)>& function) noexcept;
template<typename T>
void* dlsym_val(void* handle, const char* name, T* var) noexcept;
template<typename T>
void* dlsym_func(void* handle, const char* name, T& var) noexcept;
void* dlsym(void* handle, const char* name) noexcept;
// returns 0 on success, everything else must be an error
int dlclose(void* handle) noexcept;
// returns a string with the corresponding error, if there is no error it returns null
char* dlerror() noexcept;
}
First you need to include the urll.h
header file, after that you need to load a library from a file. This is how the library is intended to be used:
dlopen
with a string argument being the path to the library you want to load, save the handle pointerdlsym
functions to load a symbol and check if it has been successfully loaded by checking if the return value is not nullptr
dlclose
dlerror
to get an error messageAll functions are under the URLL
namespace. Here is an example of the C++ API
void load()
{
auto* handle = URLL::dlopen("./libLIBRARY.so");
if (handle == nullptr)
{
std::cout << dlerror() << std::endl;
return;
}
std::function<void(void)> func;
if (URLL::dlsym(handle, "begin", func) == handle)
func();
URLL::dlclose(handle);
}
The code is completely compatible with the library example we listed above.
On the line where we use dlsym
we use the std::function
support and templates. This as said in the last page is enabled using the URLL_USE_FUNCTIONAL
macro
We also have a generic dlsym
that just returns a void*
that you can use to manually cast to the type of your variable/function. Additionally we also provide support for C function pointers using the dlsym_func
function and support for variables using the dlsym_var
function
The C API is much more barebones, it looks like this:
void* urll_dlopen(const char* location);
void* urll_dlsym(void* handle, const char* name);
void* urll_dlsym_func(void* handle, const char* name, void** function);
void* urll_dlsym_var(void* handle, const char* name, void* var);
int urll_dlclose(void* handle);
char* urll_dlerror();
notice how if we compare with the C++ API, the only differences in most cases are that the names are prefixed with urll_
to signify the missing of the namespaces feature in C
Here is an example that uses the C API. Remember that for the C API you need to include the curll.h
header
void load()
{
void* handle = urll_dlopen("./libLIBRARY.so");
if (handle == NULL)
{
printf("%s\n", urll_dlerror());
return;
}
void (*test)(void) = NULL;
if (urll_dlsym_func(handle, "begin", &test))
{
test();
}
urll_dlclose(handle);
}
Here to load a function we call dlsym_func
to load a C function pointer. For loading variables we also have dlsym_var
and a generic dlsym
that just returns a void*
where you can manually cast your variables/functions
This library depends on libdl
when compiling on Unix based systems so make sure you are linking against it