408 lines
12 KiB
Plaintext
408 lines
12 KiB
Plaintext
[/
|
|
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]
|
|
|
|
|