GMP 0.4.0
Generative Metaprogramming library for C++
Loading...
Searching...
No Matches
object_factory.hpp
Go to the documentation of this file.
1// ___ __ __ ___
2// / __| \/ | _ \ GMP(Generative Metaprogramming)
3// | (_ | |\/| | _/ version 0.3.0
4// \___|_| |_|_| https://github.com/lkimuk/gmp
5//
6// SPDX-FileCopyrightText: 2020-2026 Miles Li <https://www.cppmore.com/>
7// SPDX-License-Identifier: MIT
8//
9// This file is part of the GMP (Generative Metaprogramming) library.
10// Full project source: https://github.com/lkimuk/gmp
11//
12// This object factory implementation was originally written by the same author
13// for the okdp library in 2020 and later adapted for GMP:
14// https://github.com/lkimuk/okdp
15
16#ifndef GMP_DP_OBJECT_FACTORY_HPP_
17#define GMP_DP_OBJECT_FACTORY_HPP_
18
19#include <functional>
20#include <map>
21#include <memory>
22#include <stdexcept>
23#include <string>
24#include <vector>
25
26#include <gmp/dp/singleton.hpp>
27#include <gmp/macro/macro.hpp>
28
29#if GMP_CPP_AT_LEAST(20)
30#include <concepts>
31#endif // C++20 or later
32
33namespace gmp {
34
57template <typename AbstractProduct, typename... ConstructorArgs>
58class object_factory : public singleton<object_factory<AbstractProduct, ConstructorArgs...>> {
60
61public:
71 template <typename T>
72#if GMP_CPP_AT_LEAST(20)
73 requires std::derived_from<T, AbstractProduct>
74#endif
76 // Registers T into object factory
82 register_type(const std::string& key) {
83 this_type::instance().map_.emplace(key,
84 [](const ConstructorArgs&... args) { return new T(args...); });
85 }
86 };
87
96 void unregister_type(const std::string& key) {
97 this_type::instance().map_.erase(key);
98 }
99
103 bool contains(const std::string& key) const {
104 return this_type::instance().map_.find(key) != this_type::instance().map_.end();
105 }
106
110 std::vector<std::string> registered_keys() const {
111 std::vector<std::string> keys;
112 keys.reserve(this_type::instance().map_.size());
113 for (const auto& entry : this_type::instance().map_) {
114 keys.push_back(entry.first);
115 }
116 return keys;
117 }
118
135 AbstractProduct* create(const std::string& key, const ConstructorArgs&... args) const {
136 auto product = try_create_unique(key, args...);
137 if (!product) {
138 throw std::invalid_argument("Unknown object type passed to factory!");
139 }
140 return product.release();
141 }
142
152 std::shared_ptr<AbstractProduct> create_shared(
153 const std::string& key, const ConstructorArgs&... args) const {
154 return std::shared_ptr<AbstractProduct>(create(key, args...));
155 }
156
166 std::unique_ptr<AbstractProduct> create_unique(
167 const std::string& key, const ConstructorArgs&... args) const {
168 return std::unique_ptr<AbstractProduct>(create(key, args...));
169 }
170
174 std::unique_ptr<AbstractProduct> try_create_unique(
175 const std::string& key, const ConstructorArgs&... args) const {
176 const auto& map = this_type::instance().map_;
177 const auto found = map.find(key);
178 if (found == map.end()) {
179 return {};
180 }
181 return std::unique_ptr<AbstractProduct>(found->second(args...));
182 }
183
187 std::shared_ptr<AbstractProduct> try_create_shared(
188 const std::string& key, const ConstructorArgs&... args) const {
189 return std::shared_ptr<AbstractProduct>(try_create_unique(key, args...));
190 }
191
192private:
193 std::map<std::string, std::function<AbstractProduct*(const ConstructorArgs&... args)>> map_;
194};
195
196#define _GMP_GET_CONCRETE_PRODUCT_CLASS(ConcreteProduct) GMP_IF_THEN_ELSE(GMP_IS_TUPLE(ConcreteProduct), GMP_TUPLE_GET, ConcreteProduct)GMP_IF(GMP_IS_TUPLE(ConcreteProduct), (1, ConcreteProduct))
197#define _GMP_GET_CONSTRUCTOR_TYPES(ConstructorArgs) GMP_IF_THEN_ELSE(GMP_IS_TUPLE(ConstructorArgs), GMP_REMOVE_PARENS, ConstructorArgs)GMP_IF(GMP_IS_TUPLE(ConstructorArgs), (ConstructorArgs))
198#define GMP_FACTORY_REGISTER_WITH_ARGS(AbstractProduct, ConstructorArgs, ConcreteProduct) \
199 static gmp::object_factory<AbstractProduct, _GMP_GET_CONSTRUCTOR_TYPES(ConstructorArgs)>::register_type<_GMP_GET_CONCRETE_PRODUCT_CLASS(ConcreteProduct)> \
200 GMP_CONCAT(gmp_reg_, __COUNTER__)(GMP_IF_THEN_ELSE(GMP_IS_TUPLE(ConcreteProduct), GMP_TUPLE_GET(0, ConcreteProduct), GMP_STRINGIFY(ConcreteProduct)));
201#define GMP_FACTORY_REGISTER_NO_ARGS(AbstractProduct, ConcreteProduct) \
202 static gmp::object_factory<AbstractProduct>::register_type<_GMP_GET_CONCRETE_PRODUCT_CLASS(ConcreteProduct)> \
203 GMP_CONCAT(gmp_reg_, __COUNTER__)(GMP_IF_THEN_ELSE(GMP_IS_TUPLE(ConcreteProduct), GMP_TUPLE_GET(0, ConcreteProduct), GMP_STRINGIFY(ConcreteProduct)));
204
227#define GMP_FACTORY_REGISTER(AbstractProduct, ConstructorArgs, ...) \
228 GMP_EVAL( _GMP_FACTORY_REGISTER_IMPL(AbstractProduct, ConstructorArgs, __VA_ARGS__) )
229#define _GMP_FACTORY_REGISTER_IMPL(AbstractProduct, ConstructorArgs, ...) \
230 _GMP_FACTORY_REGISTER_IMPL_COMPAT_MSVC(AbstractProduct, ConstructorArgs, GMP_IS_EMPTY(__VA_ARGS__), GMP_TUPLE_EMPTY(ConstructorArgs), __VA_ARGS__)
231#if GMP_STANDARD_PREPROCESSOR
232#define _GMP_FACTORY_REGISTER_IMPL_COMPAT_MSVC(AbstractProduct, ConstructorArgs, _0, _1, ...) \
233 GMP_OVERLOAD_INVOKE(_GMP_FACTORY_REGISTER_WHEN, _0, _1)(AbstractProduct, ConstructorArgs, __VA_ARGS__)
234#else
235#define _GMP_FACTORY_REGISTER_IMPL_COMPAT_MSVC(AbstractProduct, ConstructorArgs, _0, _1, ...) \
236 GMP_EXPAND( GMP_OVERLOAD_INVOKE(_GMP_FACTORY_REGISTER_WHEN, _0, _1)(AbstractProduct, ConstructorArgs, __VA_ARGS__) )
237#endif
238#define _GMP_FACTORY_REGISTER_WHEN_1(...)
239#define _GMP_FACTORY_REGISTER_WHEN_0_1(AbstractProduct, ConstructorArgs, ConcreteProduct, ...) \
240 GMP_FACTORY_REGISTER_NO_ARGS(AbstractProduct, ConcreteProduct) \
241 GMP_IF(GMP_BOOL(GMP_SIZE_OF_VAARGS(__VA_ARGS__)), _GMP_FACTORY_REGISTER_WHEN_1_CONTINUE) GMP_IF(GMP_BOOL(GMP_SIZE_OF_VAARGS(__VA_ARGS__)), (AbstractProduct, ConstructorArgs, __VA_ARGS__))
242#define _GMP_FACTORY_REGISTER_WHEN_0_0(AbstractProduct, ConstructorArgs, ConcreteProduct, ...) \
243 GMP_FACTORY_REGISTER_WITH_ARGS(AbstractProduct, ConstructorArgs, ConcreteProduct) \
244 GMP_IF(GMP_BOOL(GMP_SIZE_OF_VAARGS(__VA_ARGS__)), _GMP_FACTORY_REGISTER_WHEN_1_CONTINUE) GMP_IF(GMP_BOOL(GMP_SIZE_OF_VAARGS(__VA_ARGS__)), (AbstractProduct, ConstructorArgs, __VA_ARGS__))
245#define _GMP_FACTORY_REGISTER_WHEN_1_CONTINUE(AbstractProduct, ConstructorArgs, ...) \
246 GMP_DEFER(_GMP_FACTORY_REGISTER_INDIRECT)()(AbstractProduct, ConstructorArgs, __VA_ARGS__)
247#define _GMP_FACTORY_REGISTER_INDIRECT() _GMP_FACTORY_REGISTER_IMPL
248
251} // namespace gmp
252
253#endif // GMP_DP_OBJECT_FACTORY_HPP_
A singleton-backed runtime factory that creates products by string key.
void unregister_type(const std::string &key)
Remove a previously registered product key from the factory.
std::shared_ptr< AbstractProduct > try_create_shared(const std::string &key, const ConstructorArgs &... args) const
Try to create a shared product, returning an empty pointer for an unknown key.
AbstractProduct * create(const std::string &key, const ConstructorArgs &... args) const
Create a concrete product with raw new semantics.
std::unique_ptr< AbstractProduct > create_unique(const std::string &key, const ConstructorArgs &... args) const
Create a concrete product and return it as std::unique_ptr.
std::unique_ptr< AbstractProduct > try_create_unique(const std::string &key, const ConstructorArgs &... args) const
Try to create a product, returning an empty pointer for an unknown key.
bool contains(const std::string &key) const
Test whether a product key is registered.
std::shared_ptr< AbstractProduct > create_shared(const std::string &key, const ConstructorArgs &... args) const
Create a concrete product and return it as std::shared_ptr.
std::vector< std::string > registered_keys() const
Return all registered product keys.
CRTP-based singleton helper with optional dead-reference recovery.
Definition singleton.hpp:54
consteval auto enum_values()
Get all enumerator values of an enumeration type at compile-time.
Definition meta.hpp:193
Definition lock.hpp:21
Register a concrete product type under a string key.
register_type(const std::string &key)
Register T with the specified lookup key.