![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
TutorialBoost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 11. Boost.DLL
|
![]() | Caution |
---|---|
Будьте осторожны:< |
Результатом применения будет следующее:
plugin->calculate(1.5, 1.5) call: 3 plugin->calculate(1.5, 1.5) second call: 6 Plugin Name: aggregator
Full sources:
Рассмотрим ситуацию: у нас есть несколько плагинов, но только некоторые из них имеют нужные нам символы. Напишем функцию, которая ищет список плагинов и пытается найти<"create_plugin"
>метод.
#include <boost/dll/import.hpp> // for import_alias #include <boost/make_shared.hpp> #include <boost/function.hpp> #include <iostream> #include "../tutorial_common/my_plugin_api.hpp" namespace dll = boost::dll; std::size_t search_for_symbols(const std::vector<boost::filesystem::path>& plugins) { std::size_t plugins_found = 0; for (std::size_t i = 0; i < plugins.size(); ++i) { std::cout << "Loading plugin: " << plugins[i] << '\n'; dll::shared_library lib(plugins[i], dll::load_mode::append_decorations); if (!lib.has("create_plugin")) { // no such symbol continue; } // library has symbol, importing... typedef boost::shared_ptr<my_plugin_api> (pluginapi_create_t)(); boost::function<pluginapi_create_t> creator = dll::import_alias<pluginapi_create_t>(boost::move(lib), "create_plugin"); std::cout << "Matching plugin name: " << creator()->name() << std::endl; ++ plugins_found; } return plugins_found; }
Если мы назовем этот метод для всех наших плагинов, мы получим следующий результат:
Loading plugin: "/test/libmy_plugin_aggregator.so" Matching plugin name: aggregator Loading plugin: "/test/libmy_plugin_sum.so" Constructing my_plugin_sum Destructing my_plugin_sum ;o)
Full sources:
Подключение плагина к исполняемому имеет преимущества
Давайте начнем с создания подключаемого плагина. Такой плагин будет иметь заголовок, общий для самой библиотеки плагинов и для исполняемого файла:
#include <boost/dll/alias.hpp> // for BOOST_DLL_ALIAS #include <boost/shared_ptr.hpp> #include "../tutorial_common/my_plugin_api.hpp" namespace my_namespace { boost::shared_ptr<my_plugin_api> create_plugin(); // Forward declaration } // namespace my_namespace BOOST_DLL_ALIAS( my_namespace::create_plugin, // <-- this function is exported with... create_plugin // <-- ...this alias name )
Главный трюк здесь - определение псевдонима. При подключении плагина к исполняемому файлу псевдонимдолжен быть инстанцированв одном из исходных файлов исполняемого файла. В противном случае линкер оптимизирует наш плагин.
Вот как выглядит реализация плагина:
#include "static_plugin.hpp" // this is essential, BOOST_SYMBOL_ALIAS must be seen in this file #include <boost/make_shared.hpp> #include <iostream> namespace my_namespace { class my_plugin_static : public my_plugin_api { public: my_plugin_static() { std::cout << "Constructing my_plugin_static" << std::endl; } std::string name() const { return "static"; } float calculate(float x, float y) { return x - y; } ~my_plugin_static() { std::cout << "Destructing my_plugin_static" << std::endl; } }; boost::shared_ptr<my_plugin_api> create_plugin() { return boost::make_shared<my_plugin_static>(); } } // namespace my_namespace
Теперь, если мы сделаем статическую библиотеку из исходного файла и свяжем эту статическую библиотеку со следующим кодом, мы сможем импортировать символы из плагина:
#include <boost/dll/shared_library.hpp> // for shared_library #include <boost/dll/runtime_symbol_info.hpp> // for program_location() #include "static_plugin.hpp" // without this headers some compilers may optimize out the `create_plugin` symbol #include <boost/function.hpp> #include <iostream> namespace dll = boost::dll; int main() { dll::shared_library self(dll::program_location()); std::cout << "Call function" << std::endl; boost::function<boost::shared_ptr<my_plugin_api>()> creator = self.get_alias<boost::shared_ptr<my_plugin_api>()>("create_plugin"); std::cout << "Computed Value: " << creator()->calculate(2, 2) << std::endl; }
![]() | Note |
---|---|
Флаг «-rdynamic» должен использоваться при подключении плагина к исполняемому файлу на ОС Linux. В противном случае загрузка символов из себявыйдет из строя. |
Запуск программы выведет следующее:
Call function Constructing my_plugin_static Computed Value: 0 Destructing my_plugin_static
![]() | Note |
---|---|
Если мы хотим создать традиционный плагин, который находится в отдельной общей библиотеке, все, что нам нужно сделать, это удалить строку< |
Full sources:
Давайте создадим исполняемый файл, подключим к нему плагин и попытаемся загрузить все существующие плагины:
namespace dll = boost::dll; class plugins_collector { // Name => plugin typedef boost::container::map<std::string, dll::shared_library> plugins_t; boost::filesystem::path plugins_directory_; plugins_t plugins_; // loads all plugins in plugins_directory_ void load_all(); // Gets `my_plugin_api` instance using "create_plugin" or "plugin" imports, // stores plugin with its name in the `plugins_` map. void insert_plugin(BOOST_RV_REF(dll::shared_library) lib); public: plugins_collector(const boost::filesystem::path& plugins_directory) : plugins_directory_(plugins_directory) { load_all(); } void print_plugins() const; std::size_t count() const; // ... };
int main(int argc, char* argv[]) { plugins_collector plugins(argv[1]); std::cout << "\n\nUnique plugins " << plugins.count() << ":\n"; plugins.print_plugins(); // ...
С флагами по умолчанию вы получите очень странный выход:
Loaded (0x180db60):"/libs/dll/test/libmy_plugin_aggregator.so" Constructing my_plugin_static Destructing my_plugin_static ... Unique plugins 2: (0x180db60): static (0x180e3b0): sum Destructing my_plugin_sum ;o)
Почему<my_plugin_static
>был построен, когда мы загружали<my_plugin_aggregator
>?
Это связано с тем, что функция<create_plugin
>от<libmy_plugin_aggregator.so
>была скрыта функцией<create_plugin
>от другого плагина. Динамический линкер считал, что<create_plugin
>уже загружен и нет необходимости загружать его снова.
![]() | Warning |
---|---|
Используйте флаг «-fvisibility=hidden» (по крайней мере, для плагинов) при компиляции для платформ POSIX. Этот флаг делает ваш код более портативным («-fvisibility=hidden» — поведение по умолчанию под Windows), уменьшает размер двоичных файлов и улучшает время загрузки двоичных файлов. |
Теперь, если мы перекомпилируем ваш пример с «-fvisibility=hidden», мы получим следующий вывод:
Loaded (0x2406b60):"/libs/dll/test/libmy_plugin_aggregator.so" Loaded (0x2407410):"/libs/dll/test/libgetting_started_library.so" Constructing my_plugin_sum ... Unique plugins 3: (0x2406b60): aggregator (0x7fd1cadce2c8): static (0x24073b0): sum Destructing my_plugin_sum ;o)
Full sources:
Повышаю. DLL не предоставляет механизм извлечения библиотечных разгрузок. Однако такая задача может быть легко выполнена.
Все, что вам нужно сделать, это написать простой класс, который хранит обратные вызовы и вызывает их при разрушении:
#include <boost/dll/alias.hpp> // for BOOST_DLL_ALIAS #include <boost/function.hpp> #include <vector> namespace my_namespace { struct on_unload { typedef boost::function<void()> callback_t; typedef on_unload this_type; ~on_unload() { for (std::size_t i = 0; i < callbacks_.size(); ++i) { callback_t& function = callbacks_[i]; function(); // calling the callback } } // not thread safe static void add(const callback_t& function) { static this_type instance; instance.callbacks_.push_back(function); } private: std::vector<callback_t> callbacks_; on_unload() {} // prohibit construction outside of the `add` function }; // Exporting the static "add" function with name "on_unload" BOOST_DLL_ALIAS(my_namespace::on_unload::add, on_unload) } // namespace my_namespace
В приведенном выше примере<my_namespace::on_unload
>представлена однотонная структура, удерживающая вектор обратных вызовов и вызывающая все обратные вызовы при разрушении.
Теперь мы можем загрузить эту библиотеку и предоставить обратный звонок:
#include <boost/dll/import.hpp> #include <boost/function.hpp> #include <iostream> typedef boost::function<void()> callback_t; void print_unloaded() { std::cout << "unloaded" << std::endl; } int main(int argc, char* argv[]) { // argv[1] contains full path to our plugin library boost::filesystem::path shared_library_path = argv[1]; // loading library and getting a function from it boost::function<void(const callback_t&)> on_unload = boost::dll::import_alias<void(const callback_t&)>( shared_library_path, "on_unload" ); on_unload(&print_unloaded); // adding a callback std::cout << "Before library unload." << std::endl; // Releasing last reference to the library, so that it gets unloaded on_unload.clear(); std::cout << "After library unload." << std::endl; }
Если мы запустим пример, мы получим следующий вывод:
Before library unload. unloaded After library unload.
Full sources:
Может возникнуть ситуация, когда мы не знаем названия функций в плагине. В этом случае библиотека может быть полезной.
Представьте себе ситуацию: у нас есть проект «Анна», который способен загружать и использовать плагины, содержащие функции с подписью<void(const
std::string&)
>. Мы не знаем названий функций, но хотим их как-то узнать.
Решение было бы довольно простым. Давайте согласимся с разработчиками плагинов, что они могут называть функции так, как им нравится, но все псевдонимы функций плагина должны быть расположены в разделе «Анна»:
#include <boost/dll/alias.hpp> // for BOOST_DLL_ALIAS_SECTIONED #include <iostream> #include <string> void print(const std::string& s) { std::cout << "Hello, " << s << '!' << std::endl; } BOOST_DLL_ALIAS_SECTIONED(print, print_hello, Anna)
#include <boost/dll/alias.hpp> // for BOOST_DLL_ALIAS_SECTIONED #include <string> #include <iostream> void print_howdy(const std::string& s) { std::cout << "How're you doing, " << s << '?' << std::endl; } void print_bored(const std::string& s) { std::cout << "Are you bored, " << s << '?' << std::endl; } BOOST_DLL_ALIAS_SECTIONED(print_howdy, howdy, Anna) BOOST_DLL_ALIAS_SECTIONED(print_bored, are_you_bored, Anna)
Теперь мы можем легко получить эти функции с помощью<boost::dll::library_info
>:
#include <boost/dll/shared_library.hpp> #include <boost/dll/library_info.hpp> #include <iostream> void load_and_execute(const boost::filesystem::path libraries[], std::size_t libs_count) { const std::string username = "User"; for (std::size_t i = 0; i < libs_count; ++i) { // Class `library_info` can extract information from a library boost::dll::library_info inf(libraries[i]); // Getting symbols exported from 'Anna' section std::vector<std::string> exports = inf.symbols("Anna"); // Loading library and importing symbols from it boost::dll::shared_library lib(libraries[i]); for (std::size_t j = 0; j < exports.size(); ++j) { std::cout << "\nFunction '" << exports[j] << "' prints:\n\t"; lib.get_alias<void(const std::string&)>(exports[j]) // importing function (username); // calling function } } }
Если мы запустим пример, мы получим следующий вывод:
Function 'print_hello' prints: Hello, User! Function 'are_you_bored' prints: Are you bored, User? Function 'howdy' prints: How're you doing, User?
![]() | Note |
---|---|
< |
Full sources:
Как отмечается в документации к<boost::dll::import
>переменным и функциям, возвращаемым из этих функций, содержится ссылка на общую библиотеку. Однако вложенные объекты и объекты, возвращаемые<import*
>функциями, не содержат ссылки на общую библиотеку. Нет никакого способа решить эту проблему на подъеме. Уровень библиотеки DLL, но вы можете решить эту проблему самостоятельно. Вот пример того, как это можно сделать.
В этом примере мы будем импортировать функцию, которая создает экземпляр плагина и связывает этот экземпляр с shared_library.
Прежде всего, необходимо определить новый плагин api:
#include "../tutorial_common/my_plugin_api.hpp" #include <boost/filesystem/path.hpp> class my_refcounting_api: public my_plugin_api { public: // Returns path to shared object that holds a plugin. // Must be instantiated in plugin. virtual boost::filesystem::path location() const = 0; };
Этот API не сильно отличается от предыдущего. Добавлен только один абстрактный метод.
Теперь давайте определим плагин:
#include "refcounting_plugin.hpp" #include <boost/dll/runtime_symbol_info.hpp> // for this_line_location() namespace my_namespace { class my_plugin_refcounting : public my_refcounting_api { public: // Must be instantiated in plugin boost::filesystem::path location() const { return boost::dll::this_line_location(); // location of this plugin } std::string name() const { return "refcounting"; } // ... }; } // namespace my_namespace // Factory method. Returns *simple pointer*! my_refcounting_api* create() { return new my_namespace::my_plugin_refcounting(); }
Этот плагин не сильно отличается от наших предыдущих примеров, за исключением дополнительного метода, который вызывает<boost::dll::this_line_location
>и<create()
>функцию, которая возвращает простой указатель вместо<boost::shared_ptr
>.
Теперь давайте создадим функцию, которая связывает вновь созданный экземпляр<my_refcounting_api
>с общей библиотекой:
#include <boost/shared_ptr.hpp> #include <boost/make_shared.hpp> #include <boost/dll/shared_library.hpp> struct library_holding_deleter { boost::shared_ptr<boost::dll::shared_library> lib_; void operator()(my_refcounting_api* p) const { delete p; } }; inline boost::shared_ptr<my_refcounting_api> bind(my_refcounting_api* plugin) { // getting location of the shared library that holds the plugin boost::filesystem::path location = plugin->location(); // `make_shared` is an efficient way to create a shared pointer boost::shared_ptr<boost::dll::shared_library> lib = boost::make_shared<boost::dll::shared_library>(location); library_holding_deleter deleter; deleter.lib_ = lib; return boost::shared_ptr<my_refcounting_api>( plugin, deleter ); }
В<bind
>методе мы называем<plugin->location()
>. Этот вызов приводит к вызову<boost::dll::this_line_location
>и возвращает местоположение плагина. Тогда<shared_ptr
>, который держит<shared_library
>, создается с помощью<make_shared
>призыва.
После этого мы создаем<boost::shared_ptr<my_refcounting_api>
>с<library_holding_deleter
>, который хранит экземпляр общей библиотеки.
![]() | Note |
---|---|
Используйте< |
Вот и все, теперь мы можем получить пример плагина:
#include <boost/dll/import.hpp> #include <boost/function.hpp> inline boost::shared_ptr<my_refcounting_api> get_plugin( boost::filesystem::path path, const char* func_name) { typedef my_refcounting_api*(func_t)(); boost::function<func_t> creator = boost::dll::import_alias<func_t>( path, func_name, boost::dll::load_mode::append_decorations // will be ignored for executable ); // `plugin` does not hold a reference to shared library. If `creator` will go out of scope, // then `plugin` can not be used. my_refcounting_api* plugin = creator(); // Returned variable holds a reference to // shared_library and it is safe to use it. return bind( plugin ); // `creator` goes out of scope here and will be destroyed. }
Вот как выглядит функция<main()
>:
Загрузка плагина Runtime |
Плагин был связан в | |
---|---|---|
Код |
#include <iostream> #include "refcounting_api.hpp" int main(int argc, char* argv[]) { boost::shared_ptr<my_refcounting_api> plugin = get_plugin( boost::filesystem::path(argv[1]) / "refcounting_plugin", "create_refc_plugin" ); std::cout << "Plugin name: " << plugin->name() << ", \nlocation: " << plugin->location() << std::endl; }
|
#include <boost/dll/runtime_symbol_info.hpp> // program_location() #include <iostream> #include "refcounting_plugin.hpp" int main() { boost::shared_ptr<my_refcounting_api> plugin = get_plugin( boost::dll::program_location(), "create_refc_plugin" ); std::cout << "Plugin name: " << plugin->name() << ", \nlocation: " << plugin->location() << std::endl; }
|
Выход | Plugin name: refcounting, location: "/libs/dll/librefcounting_plugin.so" | Plugin name: refcounting, location: "/tutorial8_static" |
Full sources:
Это тривиальный пример, но он имеет одно сложное место. Когда вы импортируете функцию C, это имя, вы должны использовать<boost::dll::import
>, а не<boost::dll::import_alias
>:
#include <boost/dll/import.hpp> // for dll::import #include <boost/dll/shared_library.hpp> // for dll::shared_library #include <boost/function.hpp> #include <iostream> #include <windows.h> namespace dll = boost::dll; int main() { typedef HANDLE(__stdcall GetStdHandle_t)(DWORD ); // function signature with calling convention // OPTION #0, requires C++11 compatible compiler that understands GetStdHandle_t signature. auto get_std_handle = dll::import<GetStdHandle_t>( "Kernel32.dll", "GetStdHandle", boost::dll::load_mode::search_system_folders ); std::cout << "0.0 GetStdHandle() returned " << get_std_handle(STD_OUTPUT_HANDLE) << std::endl; // You may put the `get_std_handle` into boost::function<>. But boost::function<Signature> can not compile with // Signature template parameter that contains calling conventions, so you'll have to remove the calling convention. boost::function<HANDLE(DWORD)> get_std_handle2 = get_std_handle; std::cout << "0.1 GetStdHandle() returned " << get_std_handle2(STD_OUTPUT_HANDLE) << std::endl; // OPTION #1, does not require C++11. But without C++11 dll::import<> can not handle calling conventions, // so you'll need to hand write the import. dll::shared_library lib("Kernel32.dll", dll::load_mode::search_system_folders); GetStdHandle_t& func = lib.get<GetStdHandle_t>("GetStdHandle"); // Here `func` does not keep a reference to `lib`, you'll have to deal with that on your own. std::cout << "1.0 GetStdHandle() returned " << func(STD_OUTPUT_HANDLE) << std::endl; return 0; }
Full sources:
Статья Tutorial раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 11. Boost.DLL может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 11. Boost.DLL ::
реклама |