Boost.DLL
动机
在运行时将特定功能添加到现有软件应用程序可能会很有用。 此类扩展或插件通常使用在运行时加载的动态库模块(DLL,SO/DSO)来实现。
该库旨在以可移植的跨平台方式简化使用C ++的插件开发。
库提供了一种跨平台的可移植方式:
- 加载库
- 导入任何本地函数和变量
- 为C ++ mangled函数和符号创建别名
- 查询库中的节和导出的符号
- 自加载和自查询
- 通过导出的符号获取程序和模块位置
入门
Boost.DLL
是header-only
库。 要开始使用该库,您只需要包含<boost/dll.hpp>
头文件。 之后,您可以自由导入和导出函数和变量。 导入代码需要链接到boost_filesystem
和boost_system
库。
如果要加载库,只需构造boost::dll::shared_library
类,并将库的路径作为参数:
boost::dll::shared_library lib("/test/boost/application/libtest_library.so");
现在,您可以使用get
和get_alias
成员函数轻松地从该库中导入符号:
int plugin_constant = lib.get<const int>("integer_variable");
boost::function<int()> f = lib.get<int()>("function_returning_int");
int& i = lib.get_alias<int>("alias_to_int_variable");
对于boost::dll::shared_library
,只有在boost::dll::shared_library
实例未销毁之前,才可以使用导入的符号。
使用boost::dll::library_info
查询库 ,并使用boost::dll::symbol_location
,boost::dll::this_line_location
和boost::dll::program_location
获取符号信息。
要导入单个函数或变量,可以使用以下一个衬里:
using namespace boost;
// `extern "C"` - specifies C linkage: forces the compiler to export function/variable by a pretty (unmangled) C name.
#define API extern "C" BOOST_SYMBOL_EXPORT
// Importing function.
auto cpp11_func = dll::import<int(std::string&&)>(
path_to_shared_library, "i_am_a_cpp11_function"
);
// Export (DLL/DSL sources):
namespace some_namespace {
API int i_am_a_cpp11_function(std::string&& param) noexcept;
// ^-------------------- function name to use in dll::import<>
}
//Functions description:
import<T>(...)
// Importing variable.
shared_ptr<std::string> cpp_var = dll::import<std::string>(
path_to_shared_library, "cpp_variable_name"
);
// Export (DLL/DSL sources):
namespace your_project_namespace {
API std::string cpp_variable_name;
}
// Functions description:
import<T>(...)
// Importing function by alias name
auto cpp_func = dll::import_alias<std::string(const std::string&)>(
path_to_shared_library, "pretty_name"
);
// Export (DLL/DSL sources):
namespace some_namespace {
std::string i_am_function_with_ugly_name(const std::string& param) noexcept;
}
// When you have no control over function sources or wish to specify another name.
BOOST_DLL_ALIAS(some_namespace::i_am_function_with_ugly_name, pretty_name)
// Functions description:
import_alias<T>(...)
BOOST_DLL_ALIAS
使用导入的变量或函数是安全的,因为从import<T>(...)
和import_alias<T>(...)
函数返回的变量在内部保存对共享库的引用。
BOOST_SYMBOL_EXPORT
只是Boost.Config
中的一个宏,它扩展为__declspec(dllexport)
或__attribute__((visibility("default")))
。 您可以自由使用自己的宏进行导出。
在
Linux/POSIX/MacOS
上,使用"dl"进行链接。 还建议使用“-fvisibility = hidden” flag。
教程
提供本教程的目的是使您了解如何创建和使用插件。
插件基础
创建自己的插件时,要做的第一件事是定义插件接口。 有一个抽象类的示例,它将作为我们的插件API:
#include <boost/config.hpp>
#include <string>
class BOOST_SYMBOL_VISIBLE my_plugin_api {
public:
virtual std::string name() const = 0;
virtual float calculate(float x, float y) = 0;
virtual ~my_plugin_api() {}
};
现在,让我们制作一个DLL/DSO
库,该库将保存插件接口的实现,并使用extern“ C”
和BOOST_SYMBOL_EXPORT
导出它:
#include <boost/config.hpp> // for BOOST_SYMBOL_EXPORT
#include "../tutorial_common/my_plugin_api.hpp"
namespace my_namespace {
class my_plugin_sum : public my_plugin_api {
public:
my_plugin_sum() {
std::cout << "Constructing my_plugin_sum" << std::endl;
}
std::string name() const {
return "sum";
}
float calculate(float x, float y) {
return x + y;
}
~my_plugin_sum() {
std::cout << "Destructing my_plugin_sum ;o)" << std::endl;
}
};
// Exporting `my_namespace::plugin` variable with alias name `plugin`
// (Has the same effect as `BOOST_DLL_ALIAS(my_namespace::plugin, plugin)`)
extern "C" BOOST_SYMBOL_EXPORT my_plugin_sum plugin;
my_plugin_sum plugin;
} // namespace my_namespace
使用boost::dll::import
和append_decorations
加载插件的简单应用程序:
#include <boost/dll/import.hpp> // for import_alias
#include <iostream>
#include "../tutorial_common/my_plugin_api.hpp"
namespace dll = boost::dll;
int main(int argc, char* argv[]) {
boost::dll::fs::path lib_path(argv[1]); // argv[1] contains path to directory with our plugin library
boost::shared_ptr<my_plugin_api> plugin; // variable to hold a pointer to plugin variable
std::cout << "Loading the plugin" << std::endl;
plugin = dll::import<my_plugin_api>( // type of imported symbol is located between `<` and `>`
lib_path / "my_plugin_sum", // path to the library and library name
"plugin", // name of the symbol to import
dll::load_mode::append_decorations // makes `libmy_plugin_sum.so` or `my_plugin_sum.dll` from `my_plugin_sum`
);
std::cout << "plugin->calculate(1.5, 1.5) call: " << plugin->calculate(1.5, 1.5) << std::endl;
}
该应用程序将输出:
Loading the plugin
Constructing my_plugin_sum
plugin->calculate(1.5, 1.5) call: 3
Destructing my_plugin_sum ;o)
插件中的工厂方法
在前面的示例中,我们从插件导入单个变量。 让我们创建一个使用我们的插件API插件并保持某些状态的类:
#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
如您所见,my_namespace::create_plugin
是工厂方法,它创建my_namespace::my_plugin_aggregator
的实例。 我们使用BOOST_DLL_ALIAS
导出名称为create_plugin
的方法。
#include <boost/dll/import.hpp> // for import_alias
#include <boost/function.hpp>
#include <iostream>
#include "../tutorial_common/my_plugin_api.hpp"
namespace dll = boost::dll;
int main(int argc, char* argv[]) {
boost::dll::fs::path shared_library_path(argv[1]); // argv[1] contains path to directory with our plugin library
shared_library_path /= "my_plugin_aggregator";
typedef boost::shared_ptr<my_plugin_api> (pluginapi_create_t)();
boost::function<pluginapi_create_t> creator;
creator = boost::dll::import_alias<pluginapi_create_t>( // type of imported symbol must be explicitly specified
shared_library_path, // path to library
"create_plugin", // symbol to import
dll::load_mode::append_decorations // do append extensions and prefixes
);
boost::shared_ptr<my_plugin_api> plugin = creator();
std::cout << "plugin->calculate(1.5, 1.5) call: " << plugin->calculate(1.5, 1.5) << std::endl;
std::cout << "plugin->calculate(1.5, 1.5) second call: " << plugin->calculate(1.5, 1.5) << std::endl;
std::cout << "Plugin Name: " << plugin->name() << std::endl;
}
在该应用程序中,我们使用boost::dll::import_alias
导入了工厂方法。