I am trying to call a function from a DLL. The API for the DLL looks like this:
#pragma once
#include <stdint.h>
#include <stdbool.h>
#if defined _MSC_VER || defined __MINGW32__
#ifdef __cplusplus
#define API extern "C" __declspec(dllexport)
#else
#define API __declspec(dllexport)
#endif
#else
#ifdef __cplusplus
#define API extern "C" __attribute__((visibility("default")))
#else
#define API __attribute__((visibility("default")))
#endif
#endif
API void* construct(const char* initString);
I am loading that function call using boost, and then trying to call it:
#include <boost/dll/import.hpp>
#include <boost/function.hpp>
#include <boost/filesystem.hpp>
#include <iostream>
typedef void* (ConstructT)(const char*);
int main() {
boost::function<ConstructT> constructFunc = boost::dll::import_alias<ConstructT>(
"TestPluginDll.dll",
"construct",
boost::dll::load_mode::append_decorations | boost::dll::load_mode::load_with_altered_search_path
);
std::string initArg = "hello";
void* pluginInstance = constructFunc(initArg.c_str());
std::cout << pluginInstance << std::endl;
}
However, it is failing on the call of the constructFunc
, throwing an error at the following code in boost/dll/import.hpp
:
// Compilation error at this point means that imported function
// was called with unmatching parameters.
//
// Example:
// auto f = dll::import_symbol<void(int)>("function", "lib.so");
// f("Hello"); // error: invalid conversion from 'const char*' to 'int'
// f(1, 2); // error: too many arguments to function
// f(); // error: too few arguments to function
template <class... Args>
inline auto operator()(Args&&... args) const
-> decltype( (*f_)(static_cast<Args&&>(args)...) )
{
return (*f_)(static_cast<Args&&>(args)...);
}
Why is this failing? The type of std::string.c_str()
is const char*
, so it seems like it is the correct type.
For reference, my DLL code looks like this:
#include <plugin_interface.hpp>
#include <iostream>
#include <nlohmann/json.hpp>
class MyPlugin {
public:
MyPlugin() {}
};
extern "C" API void* construct(const char* initString) {
MyPlugin* plugin = new MyPlugin();
return static_cast<void*>(plugin);
}
The exception:
Exception has occurred: W32/0xC0000005
Unhandled exception at 0x00007FF77B713633 in tmp.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.
After even further investigation i've realized that boost is actually working as intended. The problem is caused by wrong usage of boost::dll::import_alias
. It is supposed to be used only with special symbols exported from library via BOOST_DLL_ALIAS
macro. alias
in this context is a variable in global namespace with plain name containing a pointer to actually exported deeply nested function. see Factory method in plugin
#include <boost/dll/alias.hpp> // for BOOST_DLL_ALIAS
#include "../tutorial_common/my_plugin_api.hpp"
namespace my_namespace {
class my_plugin_aggregator : public my_plugin_api {
float aggr_;
my_plugin_aggregator() : aggr_(0) {}
public:
std::string name() const {
return "aggregator";
}
float calculate(float x, float y) {
aggr_ += x + y;
return aggr_;
}
// Factory method
static boost::shared_ptr<my_plugin_aggregator> create() {
return boost::shared_ptr<my_plugin_aggregator>(
new my_plugin_aggregator()
);
}
};
BOOST_DLL_ALIAS(
my_namespace::my_plugin_aggregator::create, // <-- this function is exported with...
create_plugin // <-- ...this alias name
)
} // namespace my_namespace
Since construct
is just a regular function in global namespace it can be imported with boost::dll::import_symbol
boost::function<ConstructT> constructFunc = boost::dll::import_symbol<ConstructT>(
"TestPluginDll.dll",
"construct",
boost::dll::load_mode::append_decorations | boost::dll::load_mode::load_with_altered_search_path
);