Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Mangled Import

Support & Requirements
Mangled Import Example
Class Import
Overloading qualifiers

This section describes the experimental feature of allowing the import of mangled symbols from an dll. While this feature is unique to this library and looks quite promising, it is not throroughly tested and thus not considered stable.

As a short example we can import the following functions quite easily:

//library.dll
namespace foo {
int    bar(int);
double bar(double);
}

And the import looks like this:

auto f1 = import_mangled<int(int)>("library.dll", "foo::bar");
auto f2 = import_mangled<double(double)>("library.dll", "foo::bar");
cout << f1(42)  << endl;
cout << f2(3.2) << endl;

Currently, the Itanium ABI and the MSVC ABI are implemented. The MSVC ABI requires boost.spirit.x3 support, allowing only the usage of MSVC 2015. The Itanium API requires C++11.

  • Gcc
  • Clang
  • MSVC 2015
  • Intel C++

The Itanium API does not import the return type of functions, nor the type of global variables.

The core of the mangled import is the smart_library class. It can import functions and variables in their mangled form; to do this, the smart_library reads the entire outline of the library and demangles every entry point in it. That also means, that this class should only be constructed once.

In order to import all the methods in the following library, we will use the smart_library .

The first thing to do when creating your own plugins is define the plugin interface. There is an example of an abstract class that will be our plugin API:

#include <string>

namespace space {

class BOOST_SYMBOL_EXPORT my_plugin
{
    std::string _name;
public:
   std::string name() const;
   float  calculate(float x, float y);
   int    calculate(int, x,  int y);
   static std::size_t size();
   my_plugin(const std::string & name);
   my_plugin();
   ~my_plugin_api();
   static int value;
};

}

Alright, now we have the defintion for the plugin, so we use it in the following full-fleshed example. Mind that there is a more convenient solution to import member-functions which will be discussed later on. This example shows however what the smart_lib provides as features.

At first we setup the smart library. Mind that the alias class is needed to provide a type-alias for the my_plugin.

#include <boost/dll/smart_library.hpp> // for import_alias
#include <iostream>
#include <memory>

namespace dll = boost::dll;

struct alias;

int main(int argc, char* argv[]) {

    boost::filesystem::path lib_path(argv[1]);   // argv[1] contains path to directory with our plugin library
    dll::smart_lib lib(lib_path);                // smart library instance

In order to create the class, we will need to allocate memory. That of course means, that we need to know the size; unfortunately it is not exported into the dll, so we added the static size function for export. Static are used as plain functions.

So we import it, call it and allocate memory.

auto size_f = lib.get_function<std::size_t()>("space::my_plugin::size"); //get the size function

auto size = size_f();             // get the size of the class
std::unique_ptr<char[], size> buffer(new char[size]);    //allocate a buffer for the import
alias & inst = *reinterpret_cast<alias*>(buffer.get()); //cast it to our alias type.

Now, we have the memory size and a reference with our alias type. In order to use it, we need to register the type as an alias. That will allow the smart library to resolve the type name.

lib.add_type_alias("space::my_plugin"); //add an alias, so i can import a class that is not declared here

In order to use the class, we of course need to initialize it, i.e. call the constructor. The Itanium ABI may also implement an allocating constructor. That is why a constructor may have two functions; since we already have allocated the memory we use the standard constructor version, of the constructor from string. So we select the constructor by passing the signature.

auto ctor = lib.get_constructor<alias(const std::string&)>(); //get the constructor
ctor.call_standard(&inst, "MyName"); //call the non-allocating constructor. The allocating-constructor is a non-portable feature

So since the class is now initialized, we can call the name method. If the function is const and/or volatile the type parameter passed as type must have the same qualifiers.

auto name_f = lib.get_mem_fn<const alias, std::string()>("name");//import the name function
std::cout <<  "Name Call: " << (inst.*name_f)() << std::endl;

Overloaded functions can only be imported seperately.

//import both calculate functions
auto calc_f = lib.get_mem_fn<alias, float(float, float)>("calculate");
auto calc_i = lib.get_mem_fn<alias, int(int, int)>      ("calculate");

std::cout << "calc(float): " << (inst.*calc_f)(5., 2.) << std::endl;
std::cout << "calc(int)  : " << (inst.*calc_f)(5,   2) << std::endl;

Import of static variable is done like with plain variable.

auto & var = lib.get_variable<int>("space::my_plugin::value");
cout << "value " << var << endl;

Since we are finished, we call the desctructor of the class.

    auto dtor = lib.get_destructor<alias>(); //get the destructor
    dtor.call_standard(&inst);
    std::cout << "plugin->calculate(1.5, 1.5) call:  " << plugin->calculate(1.5, 1.5) << std::endl;
}

Back to the Top

Now it is demonstrated, how mangled and methods may be imported. This is however a rather versatile way, so an easier interface is provided, which also allows access to the type_info of an object.

We will take the same class and import the same methods, but do it with the import features.

We put the library into a shared_pointer, because every import will hold such a pointer to it. That is, we do not want to copy it.

#include <boost/dll/smart_library.hpp>
#include <boost/dll/import_mangled.hpp>
#include <boost/dll/import_class.hpp>



int main(int argc, char* argv[]) {

    boost::filesystem::path lib_path(argv[1]);   // argv[1] contains path to directory with our plugin library
    smart_library lib(lib_path);// smart library instance

Similar to the previous example, we need the size of the class.

auto size_f = import_mangled<std::size_t()>("space::my_plugin::size"); //get the size function

auto size = (*size_f)();             // get the size of the class

On a side note, we can also import variable easily with that function.

auto value = import_mangled<int>(lib, "space::my_plugin::value");

We do the forward declaration on the first call, and invoke the constructor directly. This is quite simple and allows to invoke the constructor directly. The destructor will be invoked automatically.

auto cl = import_class<class alias, const std::string&>(lib, "space::my_plugin::some_class", size, "MyName");

Invoking a function will still require to import it first.

auto name = import_mangled<const alias, std::string()>(lib, "name");
std::cout << "Name: " << (cl->*name)() << std::endl;

For overloaded functions, we can import them as groups, which will give us an object containing the overloads.

auto calc = import_mangled<alias, float(float, float), int(int, int)>(lib, "calculate");
std::cout << "Calc(float): " (cl->*calc)(5.f, 2.f) << std::endl;
std::cout << "Calc(int):   " (cl->*calc)(5, 2)     << std::endl;

Additionally, we can access the typeinfo like this.

std::type_info &ti = cl.get_type_info();

Back to the Top

Not handled in the example was the question, of how it is handled if the qualification differs for an overloaded function. This can be done, by passing the class again with another qualification - a function signature will always pick the last one provided.

If we have this in our plugin:

struct plugin
{
    void f(int);
    void f(double);
    void f(int) const;
    void f() const;
    void f() volatile;
    void f(int) volatile;
    void f(double); const volatile;
};

we can import them all at once, with the following command:

auto f = import_class<
    alias, f(int), f(double), //not qualified
    const alias, f(int), f(), //const
    volatile alias, f(), f(int), //volatile
    const volatile alias, f(double)//const volatile
    >(lib, "f");


PrevUpHomeNext