The loader
module enables dynamic loading of proxel libraries (shared libraries) like plugins.
A loader
-compatible library has embedded a list of Factorys for the proxels it contains,
which assists in the creation of a corresponding FactoryMap.
The advantage of this is that the user doesn't have to include any specific header files from the
library in their consuming application, and with the addition of the yaml
module,
no hard coded proxel names are required at all in the compiled client code.
There are three important aspects when dealing with proxel libraries:
- When compiling the proxel library, you must have already decided on a concrete
PropertyList
(or "value adapter").
At the time of writing, theflow::yaml::YAMLPropertyList
is the only PropertyList we have implemented. - For each proxel, the factory must be "registered" using the macro
REGISTER_PROXEL_FACTORY
from the header file"superflow/loader/register_factory.h"
. - When loading the library into the consuming application, create a
flow::load::ProxelLibrary
for each shared library,
and keep them in scope as long as their proxels are in use.
The interface of a PropertyList
is defined through the templated function
flow::value<T>
from "superflow/value.h"
.
It is required that a valid PropertyList
defines the following methods:
bool hasKey(const std::string& key)
, which tells if the given key exists or not.T convertValue(const std::string& key)
, which retrieves a value from the list.
In addition, flow::load::loadFactories<PropertyList>
demands the existence of the static string PropertyList::adapter_name
.
It must contain the value of the macro LOADER_ADAPTER_NAME
(see the next section).
This macro will export a named symbol to a named section in the shared library,
which can later be retrieved by those names. See the Boost::DLL
documentation for more details.
For the user, we want this to be as pleasant an experience as possible:
#include "my/proxel.h"
#include "superflow/loader/register_factory.h"
template<typename PropertyList>
flow::Proxel::Ptr createMyProxel(const PropertyList& adapter)
{
return std::make_shared<MyProxel>(
flow::value<int>(adapter, "key")
);
}
REGISTER_PROXEL_FACTORY(MyProxel, createMyProxel)
In order for this to work, we demand that the author of the PropertyList defines the following macros:
LOADER_ADAPTER_HEADER
, path to the concrete property list header fileLOADER_ADAPTER_NAME
, a short, unique identifier for the property listLOADER_ADAPTER_TYPE
, the concrete type of property list
For the flow::yaml
module, these macros are defined through the target's "interface compile definitions".
That means that when you link your proxel library to flow::yaml
, they will be automatically defined.
A proxel library and its FactoryMap are accessed through the use of a flow::load::ProxelLibrary
object.
You construct it using the path to the shared library file, and fetch the FactoryMap using the method loadFactories<PropertyList>
.
An object of this class must not go out of scope as long as its proxels are in use.
That will cause the shared library to be unloaded, and your application to crash!
const flow::load::ProxelLibrary library{"path/to/library"};
const auto factories = library.loadFactories<flow::yaml::YAMLPropertyList>();
You can collect factories from multiple libraries using the free function loadFactories
.
Remember that the libraries mustn't go out of scope, hence we keep the vector.
const std::vector<flow::load::ProxelLibrary> library_paths{
{"path/to/library1"},
{"path/to/library2"}
};
const auto factories = flow::load::loadFactories<flow::yaml::YAMLPropertyList>(
library_paths
);
If you separate the name of the library and the path to the directory in which the library resides, Boost can automatically determine the prefix and postfix of the shared library file.
const std::string library_directory{"/directory"};
const std::string library_name{"myproxels"};
const flow::load::ProxelLibrary library{library_directory, library_name};
/// Linux result: /directory/libmyproxels.so
/// Windows result: /directory/myproxels.dll
See the tests in the loader
module for more examples, or read the report for more details.