Skip to content

Commit c97981c

Browse files
committed
ipfixprobe - introduce pluginFactory
1 parent ffb9cd9 commit c97981c

File tree

4 files changed

+388
-0
lines changed

4 files changed

+388
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/**
2+
* @file
3+
* @author Pavel Siska <[email protected]>
4+
*
5+
* @brief This file contains the definition of the `PluginFactory` class. The class is responsible
6+
* for registering plugins and creating instances of those plugins via generator functions. It
7+
* provides support for various types of object creation, including unique pointers, shared
8+
* pointers and in-place construction.
9+
*
10+
* @copyright Copyright (c) 2025 CESNET, z.s.p.o.
11+
*/
12+
13+
#pragma once
14+
15+
#include "pluginGenerator.hpp"
16+
#include "pluginManifest.hpp"
17+
18+
#include <map>
19+
#include <memory>
20+
#include <stdexcept>
21+
#include <string_view>
22+
#include <type_traits>
23+
24+
namespace ipxp {
25+
26+
/**
27+
* @brief Templated class `PluginFactory` is responsible for managing and creating plugin instances.
28+
*
29+
* The `PluginFactory` class provides a singleton-based interface for registering plugins
30+
* and generating their instances. It supports creating unique and shared pointers, as well as
31+
* objects constructed at pre-allocated memory. Each plugin is identified by a `PluginManifest`.
32+
*
33+
* @tparam Base The base class type that all plugins must inherit from.
34+
* @tparam Args The types of arguments that will be passed to the plugin constructors.
35+
*/
36+
template<typename Base, typename... Args>
37+
class PluginFactory {
38+
public:
39+
/**
40+
* @brief Retrieves the singleton instance of `PluginFactory`.
41+
*
42+
* @return A reference to the singleton `PluginFactory` instance.
43+
*/
44+
static PluginFactory& getInstance()
45+
{
46+
static PluginFactory instance;
47+
return instance;
48+
}
49+
50+
/**
51+
* @brief Registers a plugin with the factory.
52+
*
53+
* This function registers a plugin by associating its manifest with its generator functions.
54+
* The function enforces that the `Derived` type must inherit from the `Base` class.
55+
*
56+
* @tparam Derived The plugin type (class) that inherits from the `Base` class.
57+
* @param manifest The manifest containing metadata about the plugin.
58+
*
59+
* @throw std::logic_error If `Derived` is not derived from `Base`.
60+
*/
61+
template<typename Derived>
62+
void registerPlugin(const PluginManifest& manifest)
63+
{
64+
static_assert(std::is_base_of<Base, Derived>::value, "Derived must be a subclass of Base");
65+
66+
m_registeredPlugins[manifest] = createGenerators<Base, Derived, Args...>();
67+
}
68+
69+
/**
70+
* @brief Retrieves a list of all registered plugins.
71+
*
72+
* @return A vector of `PluginManifest` objects representing the registered plugins.
73+
*/
74+
[[nodiscard]] std::vector<PluginManifest> getRegisteredPlugins()
75+
{
76+
std::vector<PluginManifest> registeredPlugins;
77+
registeredPlugins.reserve(m_registeredPlugins.size());
78+
79+
for (const auto& [pluginManifest, _] : m_registeredPlugins) {
80+
registeredPlugins.push_back(pluginManifest);
81+
}
82+
return registeredPlugins;
83+
}
84+
85+
/**
86+
* @brief Creates a unique pointer to a plugin instance.
87+
*
88+
* @param key The key identifying the plugin (from the manifest).
89+
* @param args The arguments passed to the plugin constructor.
90+
* @return A unique pointer to the plugin instance.
91+
* @throws std::runtime_error If the plugin identified by the key is not registered.
92+
* @throws Any exception from the plugin constructor.
93+
*/
94+
[[nodiscard]] std::unique_ptr<Base> createUnique(std::string_view key, Args... args) const
95+
{
96+
const auto& generators = getGenerators(key);
97+
return generators.uniqueGenerator(std::forward<Args>(args)...);
98+
}
99+
100+
/**
101+
* @brief Creates a shared pointer to a plugin instance.
102+
*
103+
* @param key The key identifying the plugin (from the manifest).
104+
* @param args The arguments passed to the plugin constructor.
105+
* @return A shared pointer to the plugin instance.
106+
* @throws std::runtime_error If the plugin identified by the key is not registered.
107+
* @throws Any exception from the plugin constructor.
108+
*/
109+
[[nodiscard]] std::shared_ptr<Base> createShared(std::string_view key, Args... args) const
110+
{
111+
const auto& generators = getGenerators(key);
112+
return generators.sharedGenerator(std::forward<Args>(args)...);
113+
}
114+
115+
/**
116+
* @brief Constructs a plugin instance at a pre-allocated memory location.
117+
*
118+
* @param key The key identifying the plugin (from the manifest).
119+
* @param ptr The pre-allocated memory where the instance will be constructed.
120+
* @param args The arguments passed to the plugin constructor.
121+
* @return A pointer to the constructed plugin instance.
122+
* @throws std::runtime_error If the plugin identified by the key is not registered.
123+
* @throws Any exception from the plugin constructor.
124+
*/
125+
[[nodiscard]] Base* constructAt(std::string_view key, void* ptr, Args... args) const
126+
{
127+
const auto& generators = getGenerators(key);
128+
return generators.constructAtGenerator(ptr, std::forward<Args>(args)...);
129+
}
130+
131+
private:
132+
PluginFactory() = default;
133+
134+
using Generators = BaseGenerators<Base, Args...>;
135+
136+
Generators getGenerators(std::string_view key) const
137+
{
138+
const auto iter = m_registeredPlugins.find(key);
139+
if (iter == m_registeredPlugins.end()) {
140+
throw std::runtime_error(
141+
"PluginFactory::getGenerators() has failed. Plugin: '" + std::string(key)
142+
+ "' is not registered.");
143+
}
144+
145+
return iter->second;
146+
}
147+
148+
std::map<PluginManifest, Generators> m_registeredPlugins;
149+
};
150+
151+
} // namespace ipxp
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/**
2+
* @file
3+
* @author Pavel Siska <[email protected]>
4+
*
5+
* @brief This file contains the definition of the `BaseGenerators` struct and the
6+
* `createGenerators` function. The `BaseGenerators` struct provides generator functions for
7+
* creating instances of derived classes from a base class. This includes support for unique
8+
* pointers, shared pointers and constructing at pre-allocated memory.
9+
*
10+
* @copyright Copyright (c) 2025 CESNET, z.s.p.o.
11+
*/
12+
13+
#pragma once
14+
15+
#include <functional>
16+
#include <memory>
17+
#include <type_traits>
18+
19+
namespace ipxp {
20+
21+
/**
22+
* @brief Templated struct `BaseGenerators` provides generator functions for creating instances of
23+
* derived classes from a base class.
24+
*
25+
* @tparam Base The base class type.
26+
* @tparam Args The types of arguments for the generator functions (constructor of the derived
27+
* class).
28+
*/
29+
template<typename Base, typename... Args>
30+
struct BaseGenerators {
31+
std::function<std::unique_ptr<Base>(Args...)>
32+
uniqueGenerator; ///< Generator for unique pointer.
33+
std::function<std::shared_ptr<Base>(Args...)>
34+
sharedGenerator; ///< Generator for shared pointer.
35+
std::function<Base*(void*, Args...)>
36+
constructAtGenerator; ///< Generator for constructing at pre-allocated memory.
37+
};
38+
39+
/**
40+
* @brief Creates a set of generator functions for constructing instances of a derived class.
41+
*
42+
* This function generates a `BaseGenerators` instance containing three callable functions
43+
* that create instances of the specified derived type. The generators provide:
44+
* - A factory function returning `std::unique_ptr<Derived>`.
45+
* - A factory function returning `std::shared_ptr<Derived>`.
46+
* - A function for in-place construction using `std::construct_at`.
47+
*
48+
* The function ensures at compile-time that `Derived` is a subclass of `Base`. If `Derived`
49+
* is not nothrow-constructible with `Args...`, the generator functions will not be marked
50+
* as `noexcept`, meaning they can throw exceptions if the constructor of `Derived` fails.
51+
*
52+
* @tparam Base The base class type.
53+
* @tparam Derived The derived class type (must inherit from `Base`).
54+
* @tparam Args The types of arguments used to construct `Derived`.
55+
* @return A `BaseGenerators` instance containing generator functions for creating `Derived`
56+
* objects.
57+
*/
58+
template<typename Base, typename Derived, typename... Args>
59+
static BaseGenerators<Base, Args...> createGenerators() noexcept
60+
{
61+
static_assert(std::is_base_of_v<Base, Derived>, "Derived must be a subclass of Base");
62+
63+
return {
64+
[](Args... args) noexcept(std::is_nothrow_constructible_v<Derived, Args...>) {
65+
return std::make_unique<Derived>(std::forward<Args>(args)...);
66+
},
67+
[](Args... args) noexcept(std::is_nothrow_constructible_v<Derived, Args...>) {
68+
return std::make_shared<Derived>(std::forward<Args>(args)...);
69+
},
70+
[](void* ptr, Args... args) noexcept(std::is_nothrow_constructible_v<Derived, Args...>) {
71+
return std::construct_at(static_cast<Derived*>(ptr), std::forward<Args>(args)...);
72+
}};
73+
}
74+
75+
} // namespace ipxp
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* @file
3+
* @author Pavel Siska <[email protected]>
4+
* @brief Definition of the PluginManifest struct and related utilities.
5+
*
6+
* This file contains the definition of the PluginManifest struct, which represents
7+
* metadata and functionalities associated with a plugin. It also defines a custom comparator for
8+
* PluginManifest instances.
9+
*
10+
* @copyright Copyright (c) 2025 CESNET, z.s.p.o.
11+
*/
12+
13+
#pragma once
14+
15+
#include <functional>
16+
#include <string>
17+
18+
namespace ipxp {
19+
20+
/**
21+
* @brief Struct representing the metadata and functionalities associated with a plugin.
22+
*
23+
* The PluginManifest struct encapsulates important details about a plugin, such as its
24+
* name, description, version, and the required API version. This metadata is used to
25+
* identify and manage plugins within the system.
26+
*/
27+
struct PluginManifest {
28+
std::string name; ///< Name of the plugin.
29+
std::string description; ///< Description of the plugin.
30+
std::string pluginVersion; ///< Version of the plugin.
31+
std::string apiVersion; ///< Required API version.
32+
std::function<void()> usage; ///< Function pointer to the plugin's usage.
33+
};
34+
35+
/**
36+
* @brief Custom less-than comparator for PluginManifest instances.
37+
*
38+
* This operator allows for sorting PluginManifest instances based on their name.
39+
*
40+
* @param lhs Left-hand side PluginManifest instance.
41+
* @param rhs Right-hand side PluginManifest instance.
42+
* @return True if the name of lhs is lexicographically less than the name of rhs, false otherwise.
43+
*/
44+
inline bool operator<(const PluginManifest& lhs, const PluginManifest& rhs)
45+
{
46+
return lhs.name < rhs.name;
47+
}
48+
49+
/**
50+
* @brief Custom equality operator for PluginManifest instances.
51+
*
52+
* This operator allows for comparison between two PluginManifest instances based on their name.
53+
*
54+
* @param lhs Left-hand side PluginManifest instance.
55+
* @param rhs Right-hand side PluginManifest instance.
56+
* @return True if the names of both instances are equal, false otherwise.
57+
*/
58+
inline bool operator==(const PluginManifest& lhs, const PluginManifest& rhs)
59+
{
60+
return lhs.name == rhs.name;
61+
}
62+
63+
} // namespace ipxp
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* @file
3+
* @author Pavel Siska <[email protected]>
4+
*
5+
* @brief This file contains the definition of the `PluginRegistrar` struct.
6+
* The struct is responsible for automatically registering a plugin with the `Factory`.
7+
* It ensures that the `Derived` type is properly registered with the factory under
8+
* the provided manifest.
9+
*
10+
* The `PluginRegistrar` template can be used to simplify the process of
11+
* plugin registration. By specifying the `Derived` type and the corresponding
12+
* `Factory`, this struct allows for easy integration of plugins into the
13+
* factory system at runtime.
14+
*
15+
* @copyright Copyright (c) 2025 CESNET, z.s.p.o.
16+
*/
17+
18+
#pragma once
19+
20+
#include "pluginFactory.hpp"
21+
#include "pluginManifest.hpp"
22+
23+
#include <stdexcept>
24+
#include <type_traits>
25+
26+
namespace ipxp {
27+
28+
/**
29+
* @brief Templated struct `PluginRegistrar` is responsible for automatically registering
30+
* a plugin with the specified `Factory` during construction.
31+
*
32+
* This struct registers a plugin with the factory at runtime. It allows for the specification of a
33+
* `Derived` type, which must be registered under the provided `PluginManifest`. This facilitates
34+
* the registration process, ensuring that the `Derived` class is integrated into the plugin system
35+
* seamlessly.
36+
*
37+
* @tparam Derived The derived class type being registered.
38+
* @tparam Factory The factory type responsible for managing plugins.
39+
*
40+
* @code
41+
* //// basePlugin.hpp
42+
*
43+
* // Example of a base plugin class definition
44+
* class BasePlugin {
45+
* public:
46+
* virtual void doSomething() = 0; // Pure virtual function
47+
* };
48+
*
49+
* // forward declaration of the factory type
50+
* template<typename Base, typename... Args>
51+
* class PluginFactory;
52+
*
53+
* // define the factory type
54+
* // std::string is the type of the constructor parameter
55+
* using BasePluginFactory = PluginFactory<BasePlugin, const std::string>;
56+
*
57+
* //// plugin.hpp
58+
* #include "basePlugin.hpp"
59+
*
60+
* class DerivedPlugin : public BasePlugin {
61+
* public:
62+
* // Constructor requiring a string parameter
63+
* DerivedPlugin(const std::string& params) {
64+
* // Initialize plugin with provided parameters
65+
* }
66+
*
67+
* void doSomething() override {
68+
* // Implementation of the plugin's functionality
69+
* }
70+
* };
71+
*
72+
* //// plugin.cpp
73+
*
74+
* #include "plugin.hpp"
75+
* #include "pluginFactoryRegistration.hpp"
76+
*
77+
* // Registering the plugin with the factory
78+
* // This ensures that the required constructor exists
79+
* static const PluginRegistrar<DerivedPlugin, BasePluginFactory>
80+
* derivedRegistrator(pluginManifest);
81+
* @endcode
82+
*/
83+
template<typename Derived, typename Factory>
84+
struct PluginRegistrar {
85+
/**
86+
* @brief Constructor that automatically registers the `Derived` class with the factory.
87+
*
88+
* This constructor registers the `Derived` class plugin with the specified factory,
89+
* using the provided manifest.
90+
*
91+
* @param manifest The manifest containing metadata about the plugin.
92+
*/
93+
explicit PluginRegistrar(const PluginManifest& manifest)
94+
{
95+
Factory::getInstance().template registerPlugin<Derived>(manifest);
96+
}
97+
};
98+
99+
} // namespace ipxp

0 commit comments

Comments
 (0)