123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- [/
- Copyright 2014 Renato Tegon Forti, Antony Polukhin
- Copyright 2015-2019 Antony Polukhin
- Distributed under the Boost Software License, Version 1.0.
- (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- /]
- [template example_ref[path] '''<ulink url="https://github.com/apolukhin/Boost.DLL/blob/develop/example/'''[path]'''">example/'''[path]'''</ulink>''']
- [def __to_top [link boost_dll.tutorial Back to the Top]]
- [section Tutorial]
- This Tutorial is provided to give you an idea of how to create and use plugins.
- [section Plugin basics]
- 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:
- [import ../example/tutorial_common/my_plugin_api.hpp]
- [plugapi]
- Now let's make a DLL/DSO library that will holds implementation of plugin interface and exports it using the
- `extern "C"` and [macroref BOOST_SYMBOL_EXPORT]:
- [import ../example/tutorial1/my_plugin_sum.cpp]
- [plugcpp_my_plugin_sum]
- Simple application that loads plugin using the [funcref boost::dll::import]
- and [enumref boost::dll::load_mode::type append_decorations]:
- [import ../example/tutorial1/tutorial1.cpp]
- [callplugcpp_tutorial1]
- That application will output:
- [pre
- Loading the plugin
- Constructing my_plugin_sum
- plugin->calculate(1.5, 1.5) call: 3
- Destructing my_plugin_sum ;o)
- ]
- [*Full sources:]
- * [example_ref tutorial_common/my_plugin_api.hpp]
- * [example_ref tutorial1/my_plugin_sum.cpp]
- * [example_ref tutorial1/tutorial1.cpp]
- __to_top
- [endsect]
- [section Factory method in plugin]
- In previous example we were importing from a plugin a single variable. Let's make a class
- that uses our plugin API plugin and holds some state:
- [import ../example/tutorial2/my_plugin_aggregator.cpp]
- [plugcpp_my_plugin_aggregator]
- As you may see, `my_namespace::create_plugin` is a factory method, that creates
- instances of `my_namespace::my_plugin_aggregator`. We export that method with the name "create_plugin"
- using [macroref BOOST_DLL_ALIAS].
- [import ../example/tutorial2/tutorial2.cpp]
- [callplugcpp_tutorial2]
- In that application we have imported the factory method using [funcref boost::dll::import_alias].
- [caution Be careful: `creator` variable holds a reference to the loaded shared library. If this
- variable goes out of scope or will be reset, then the *DLL/DSO will be unloaded* and any attempt to
- dereference the `plugin` variable will lead to *undefined behavior*. ]
- Output of the application will be the following:
- [pre
- plugin->calculate(1.5, 1.5) call: 3
- plugin->calculate(1.5, 1.5) second call: 6
- Plugin Name: aggregator
- ]
- [*Full sources:]
- * [example_ref tutorial2/my_plugin_aggregator.cpp]
- * [example_ref tutorial2/tutorial2.cpp]
- __to_top
- [endsect]
- [section Searching for a symbol in multiple plugins]
- Consider the situation: we have multiple plugins, but only some of them have symbols that we need.
- Let's write a function that search list of plugins and attempts to find `"create_plugin"` method.
- [import ../example/tutorial3/tutorial3.cpp]
- [callplugcpp_tutorial3]
- If we call that method for all our plugins we'll get the following output:
- [pre
- 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:]
- * [example_ref tutorial3/tutorial3.cpp]
- * [example_ref tutorial2/my_plugin_aggregator.cpp]
- * [example_ref tutorial1/my_plugin_sum.cpp]
- __to_top
- [endsect]
- [section Linking plugin into the executable]
- Linking plugin into the executable has the advantages of
- * reducing common size of distribution
- * simplification of installation of distribution
- * faster plugin load
- Let's start from creating a linkable in plugin. Such plugin will have a header,
- common for plugin library itself and for the executable:
- [import ../example/tutorial4/static_plugin.hpp]
- [plugcpp_my_plugin_static]
- Main trick here is the alias definition. When linking plugin into the executable, the alias *must*
- be instantiated in one of the source files of the executable. Otherwise the linker will optimize
- away our plugin.
- Here's how the implementation of the plugin looks like:
- [import ../example/tutorial4/static_plugin.cpp]
- [plugcpp_my_plugin_staic_impl]
- Now if we make a static library from source file and link that static library with the following code,
- we'll be able to import symbols from plugin:
- [import ../example/tutorial4/load_self.cpp]
- [plugcpp_my_plugin_load_self]
- [note Flag '-rdynamic' must be used when linking the plugin into the executable on Linux OS.
- Otherwise loading symbols from self *will fail*.]
- Running the program will output the following:
- [pre
- Call function
- Constructing my_plugin_static
- Computed Value: 0
- Destructing my_plugin_static
- ]
- [note If we want to make a traditional plugin that is located in a separate shared library, all we need to do is
- remove the `#include "static_plugin.hpp"` line and replace `dll::program_location()` with plugin location and name.]
- [*Full sources:]
- * [example_ref tutorial4/static_plugin.hpp]
- * [example_ref tutorial4/static_plugin.cpp]
- * [example_ref tutorial4/load_self.cpp]
- __to_top
- [endsect]
- [section Symbol shadowing problem (Linux)]
- Let's make an executable, link a plugin into it and attempt to load all the existing plugins:
- [import ../example/tutorial5/load_all.cpp]
- [plugcpp_plugins_collector_def]
- [plugcpp_load_all]
- With the default flags you'll get a very strange output:
- [pre
- 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)
- ]
- Why `my_plugin_static` was constructed while we were loading `my_plugin_aggregator`?
- That's because function `create_plugin` from `libmy_plugin_aggregator.so` was shadowed by
- the `create_plugin` function from other plugin. Dynamic linker thought that `create_plugin`
- was already loaded and there is no need to load it again.
- [warning Use "-fvisibility=hidden" flag (at least for plugins) while compiling for POSIX platforms.
- This flag makes your code more portable ("-fvisibility=hidden" is the default behavior under Windows),
- reduces size of the binaries and improves binary load time.
- ]
- Now if we recompile your example with "-fvisibility=hidden" we'll get the following output:
- [pre
- 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:]
- * [example_ref tutorial5/load_all.cpp]
- * [example_ref tutorial4/static_plugin.cpp]
- * [example_ref tutorial2/my_plugin_aggregator.cpp]
- * [example_ref tutorial1/my_plugin_sum.cpp]
- __to_top
- [endsect]
- [section Executing callbacks on library unload]
- Boost.DLL provides no out of the box mechanism for catching library unloads. However such task could be easily implemented.
- [import ../example/tutorial6/on_unload_lib.cpp]
- All you need to do, is write a simple class that stores callbacks and calls them at destruction:
- [plugcpp_on_unload]
- In the example above `my_namespace::on_unload` is a singleton structure that holds a vector of callbacks and
- calls all the callbacks at destruction.
- [import ../example/tutorial6/tutorial6.cpp]
- Now we can load this library and provide a callback:
- [callplugcpp_tutorial6]
- If we run the example we'll get the following output:
- [pre
- Before library unload.
- unloaded
- After library unload.
- ]
- [*Full sources:]
- * [example_ref tutorial6/on_unload_lib.cpp]
- * [example_ref tutorial6/tutorial6.cpp]
- __to_top
- [endsect]
- [section Querying libraries for symbols]
- Situation when we do not know names of functions in plugin could occur. In that case querying library could
- be useful.
- Imagine the situation: we have a project called 'Anna' that is capable of loading and using plugins that contain
- functions with signature `void(const std::string&)`. We do not know function names, but wish to find out them somehow.
- Solution would be pretty simple. Let's agree with plugin developers, that they can name functions as they like,
- but all the plugin functions aliases must be located in section named 'Anna':
- [import ../example/tutorial7/library1.cpp]
- [plugcpp_tutorial7_library1]
- [import ../example/tutorial7/library2.cpp]
- [plugcpp_tutorial7_library2]
- Now we can easily get those functions using the [classref boost::dll::library_info]:
- [import ../example/tutorial7/tutorial7.cpp]
- [callplugcpp_tutorial7]
- If we run the example we'll get the following output:
- [pre
- Function 'print_hello' prints:
- Hello, User!
- Function 'are_you_bored' prints:
- Are you bored, User?
- Function 'howdy' prints:
- How're you doing, User?
- ]
- [note `BOOST_DLL_ALIAS` macro by default places all the aliases into the "boostdll" section. ]
- [*Full sources:]
- * [example_ref tutorial7/library1.cpp]
- * [example_ref tutorial7/library2.cpp]
- * [example_ref tutorial7/tutorial7.cpp]
- __to_top
- [endsect]
- [section Advanced library reference counting]
- As noted in documentation to the [funcref boost::dll::import]
- variables and functions returned from those functions hold a reference to the shared library. However nested objects and
- objects that are returned by `import*` functions do not hold a reference to the shared library. There's no way to solve
- this issue on the Boost.DLL library level, but you can take care of this problem by your own. Here's an example how this
- could be done.
- In this example we'll be importing function that constructs an instance of plugin and binding that
- instance to shared_library.
- First of all we need to define a new plugin api:
- [import ../example/tutorial8/refcounting_api.hpp]
- [plugcpp_my_plugin_refcounting_api]
- This API does not differ much from the previous one. Only one abstract method was added.
- Now let's define the plugin:
- [/
- [import ../example/tutorial8/refcounting_plugin.hpp]
- [plugcpp_my_plugin_refcounting_hpp]
- /]
- [import ../example/tutorial8/refcounting_plugin.cpp]
- [plugcpp_my_plugin_refcounting]
- This plugin does not differ much from our previous examples except the additional method that calls
- [funcref boost::dll::this_line_location] and `create()` function that returns a simple pointer instead of
- `boost::shared_ptr`.
- Now lets make a function that binds a newly created instance of `my_refcounting_api` to a shared library:
- [plugcpp_library_holding_deleter_api_bind]
- In `bind` method we call `plugin->location()`. This call results in a call to
- [funcref boost::dll::this_line_location] and returns the plugin location. Then a `shared_ptr` that holds a `shared_library`
- is created using the `make_shared` call.
- After that we construct a `boost::shared_ptr<my_refcounting_api>` with a `library_holding_deleter` that keeps an instance of the shared library.
- [note Use `std::unique_ptr<my_refcounting_api>` instead of `my_refcounting_api*` in production code to avoid memory leaks when `plugin->location()`
- throws or when some other class rises an exception.]
- That's it, now we can get instance of a plugin:
- [plugcpp_get_plugin_refcounting]
- Here's how it `main()` function look like:
- [import ../example/tutorial8/tutorial8.cpp]
- [import ../example/tutorial8/tutorial8_static.cpp]
- [table
- [[][Runtime plugin load][Plugin was linked in]]
- [[Code][[callplugcpp_tutorial8]][[callplugcpp_tutorial8_static]]]
- [[Output]
- [[pre Plugin name: refcounting,
- location: "/libs/dll/librefcounting_plugin.so"]]
- [[pre Plugin name: refcounting,
- location: "/tutorial8_static"]]
- ]
- ]
- [*Full sources:]
- * [example_ref tutorial8/refcounting_api.hpp]
- * [example_ref tutorial8/refcounting_plugin.hpp]
- * [example_ref tutorial8/refcounting_plugin.cpp]
- * [example_ref tutorial8/tutorial8.cpp]
- * [example_ref tutorial8/tutorial8_static.cpp]
- __to_top
- [endsect]
- [section Importing a C function from Windows dll]
- This a trivial example, but it has one tricky place. When you are importing a C function by *it's name*
- you must use [funcref boost::dll::import], not [funcref boost::dll::import_alias]:
- [import ../example/tutorial9/tutorial9.cpp]
- [callplugcpp_tutorial9]
- [*Full sources:]
- * [example_ref tutorial9/tutorial9.cpp]
- __to_top
- [endsect]
- [endsect]
|