Dynamic Loading
OpenMethod supports dynamic loading on operating systems that are capable of
handling C++ templates correctly during dynamic link. A dynamic library can add
classes, methods and overriders to an existing policy. initialize
must then be
called to rebuild the dispatch tables.
This leads to a problem: any virtual_ptr
in existence before initialize
is
called again becomes invalid. This also applies to vptrs that are stored inside
objects by inplace_vptr
.
This applies only to cases where a dynamic library adds to an existing policy. Even if the dynamic library itself uses open-methods, for example as an implementation detail, but it uses its own policy, there is no issue. |
The solution is to use a policy that contains the indirect_vptr
facet. Instead
of storing the vptr directly, it stores a reference to the vptr.
Here is an example:
// dl.hpp
#include <string>
#include <boost/openmethod.hpp>
struct Animal {
virtual ~Animal() {
}
};
struct Herbivore : Animal {};
struct Carnivore : Animal {};
struct Cow : Herbivore {};
struct Wolf : Carnivore {};
struct dynamic_policy : boost::openmethod::default_registry::with<
boost::openmethod::policies::indirect_vptr> {};
template<class Class>
using dyn_vptr = boost::openmethod::virtual_ptr<Class, dynamic_policy>;
BOOST_OPENMETHOD(
encounter, (dyn_vptr<Animal>, dyn_vptr<Animal>), std::string,
dynamic_policy);
The policy must be passed to the method as well as the
virtual_ptr s.
|
The indirect_vptr
facet tells virtual_ptr
to use a pointer to the vptr. Even
tough the value of the vptr changes when initialize
is called, the vptrs are
stored in the same place (the policy’s static_vptr<Class>
variables).
We can now register the classes and and provide an overrider:
// dl_main.cpp
#include <cstring>
#include <iostream>
#include <dlfcn.h>
#include <unistd.h>
#include <boost/openmethod.hpp>
#include <boost/openmethod/unique_ptr.hpp>
#include <boost/openmethod/compiler.hpp>
#include "dl.hpp"
BOOST_OPENMETHOD_CLASSES(
Animal, Herbivore, Cow, Wolf, Carnivore, dynamic_policy);
BOOST_OPENMETHOD_OVERRIDE(
encounter, (dyn_vptr<Animal>, dyn_vptr<Animal>), std::string) {
return "ignore\n";
}
At this point we only have one overrider. Animals of all species ignore one another:
auto main() -> int {
using namespace boost::openmethod;
initialize<dynamic_policy>();
std::cout << "Before loading library\n";
auto gracie = make_unique_virtual<Cow, dynamic_policy>();
// Wolf _willy;
// auto willy = virtual_ptr<Wolf, dynamic_policy>(_willy);
auto willy = make_unique_virtual<Wolf, dynamic_policy>();
std::cout << "Gracie encounters Willy -> "
<< encounter(gracie, willy); // ignore
std::cout << "Willy encounters Gracie -> "
<< encounter(willy, gracie); // ignore
Let’s load a dynamic library containing this code:
// dl_shared.cpp
#include <string>
#include <boost/openmethod.hpp>
#include "dl.hpp"
BOOST_OPENMETHOD_OVERRIDE(
encounter, (dyn_vptr<Herbivore>, dyn_vptr<Carnivore>), std::string) {
return "run\n";
}
struct Tiger : Carnivore {};
BOOST_OPENMETHOD_CLASSES(Tiger, Carnivore, dynamic_policy);
extern "C" auto make_tiger() -> Tiger* {
return new Tiger;
}
BOOST_OPENMETHOD_OVERRIDE(
encounter, (dyn_vptr<Carnivore>, dyn_vptr<Herbivore>), std::string) {
return "hunt\n";
}
Now back to main
:
char dl_path[4096];
dl_path[readlink("/proc/self/exe", dl_path, sizeof(dl_path))] = 0;
*strrchr(dl_path, '/') = 0;
strcat(dl_path, "/libdl_shared.so");
void* handle = dlopen(dl_path, RTLD_NOW);
if (!handle) {
std::cerr << "dlopen() failed: " << dlerror() << "\n";
exit(1);
}
std::cout << "\nAfter loading library\n";
boost::openmethod::initialize<dynamic_policy>();
auto make_tiger =
reinterpret_cast<Animal* (*)()>(dlsym(handle, "make_tiger"));
if (!make_tiger) {
std::cerr << "dlsym() failed: " << dlerror() << "\n";
exit(1);
}
std::cout << "Willy encounters Gracie -> "
<< encounter(willy, gracie); // hunt
{
auto hobbes = std::unique_ptr<Animal>(make_tiger());
std::cout << "Gracie encounters Hobbes -> "
<< encounter(gracie, *hobbes); // run
}
After unloading the library, we must call initialize
again:
dlclose(handle);
std::cout << "\nAfter unloading library\n";
boost::openmethod::initialize<dynamic_policy>();
std::cout << "Gracie encounters Willy -> "
<< encounter(gracie, willy); // ignore
std::cout << "Willy encounters Gracie -> "
<< encounter(willy, gracie); // ignore