NeoFOAM
WIP Prototype of a modern OpenFOAM core
Loading...
Searching...
No Matches
runtimeSelectionFactory.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2// SPDX-FileCopyrightText: 2023 AMR Wind Authors
3// SPDX-FileCopyrightText: 2023-2024 NeoFOAM authors
4// ##############################################################################
5// # Original design taken from amr wind #
6// # from here #
7// # https://github.com/Exawind/amr-wind/blob/v2.1.0/amr-wind/core/Factory.H #
8// ##############################################################################
9// its quite tricky for multiple compilers that bool REGISTERED gets initialized
10// the static_assert helps to register the class
11// https://stackoverflow.com/questions/6420985/
12// how-to-force-a-static-member-to-be-initialized?noredirect=1&lq=1
13#pragma once
14
15#include <memory>
16#include <unordered_map>
17#include <iostream>
18#include <functional>
19
20#include "error.hpp"
21
22namespace NeoFOAM
23{
24
33{
34public:
35
36 std::function<std::string(const std::string&)>
38 std::function<std::string(const std::string&)>
40 std::function<std::vector<std::string>()>
42};
43
54{
55public:
56
57 using LookupTable = std::unordered_map<std::string, BaseClassData>;
58
59 static void registerClass(std::string name, BaseClassData data)
60 {
61 // if not already registered
62 docTable()[name] = data;
63 }
64
72 static std::string doc(const std::string& baseClassName, const std::string& derivedClassName)
73 {
74 return docTable().at(baseClassName).doc(derivedClassName);
75 }
76
84 static std::string schema(const std::string& baseClassName, const std::string& derivedClassName)
85 {
86 // get the schema of the derived class
87 return docTable().at(baseClassName).schema(derivedClassName);
88 }
89
96 static std::vector<std::string> entries(const std::string& baseClassName)
97 {
98 return docTable().at(baseClassName).entries();
99 }
100
102 {
103 static LookupTable tbl;
104 return tbl;
105 }
106};
107
108
119template<class baseClass>
121{
123 {
124 // avoid unused variable warning
125 // is required to instantiate the static variable and with it the registration
126 (void)REGISTERED;
127 }
128
138 static bool init()
139 {
140 BaseClassData data = {baseClass::doc, baseClass::schema, baseClass::entries};
141 BaseClassDocumentation::registerClass(baseClass::name(), data);
142 return true;
143 }
144
145 static bool REGISTERED;
147#ifdef _MSC_VER
148 static_assert((bool)&REGISTERED);
149#endif
150};
151
152// Initialize the static variable and register the class
153template<class baseClass>
155
162{
163public:
164
165 std::function<std::string()> doc;
166 std::function<std::string()> schema;
167};
168
169// Parameters helper type
170template<typename... Args>
172{
173};
174
175// Primary template declaration
176template<typename Base, typename Params>
178
179// Partial specialization for Parameters
192template<typename Base, typename... Args>
193class RuntimeSelectionFactory<Base, Parameters<Args...>> : public RegisterDocumentation<Base>
194{
195public:
196
197 friend Base;
198
199 using CreatorFunc = std::function<std::unique_ptr<Base>(Args...)>;
200 using LookupTable = std::unordered_map<std::string, CreatorFunc>;
201 using ClassDocTable = std::unordered_map<std::string, DerivedClassDocumentation>;
202
209 static std::string doc(const std::string& derivedClassName)
210 {
211 // get the documentation of the derived class
212 return docTable().at(derivedClassName).doc();
213 }
214
221 static std::string schema(const std::string& derivedClassName)
222 {
223 // get the schema of the derived class
224 return docTable().at(derivedClassName).schema();
225 }
226
235 static std::vector<std::string> entries()
236 {
237 std::vector<std::string> entries;
238 for (const auto& it : table())
239 {
240 entries.push_back(it.first);
241 }
242 return entries;
243 }
244
245
259 static std::unique_ptr<Base> create(const std::string& key, Args... args)
260 {
261 keyExistsOrError(key);
262 auto ptr = table().at(key)(std::forward<Args>(args)...);
263 return ptr;
264 }
265
266
272 static void print(std::ostream& os)
273 {
274 const auto& tbl = table();
275 os << Base::name() << " " << tbl.size() << std::endl;
276 for (const auto& it : tbl)
277 {
278 os << " - " << it.first << std::endl;
279 }
280 }
281
292 template<class derivedClass>
293 class Register : public Base
294 {
295 public:
296
297 using Base::Base;
298
300 [[maybe_unused]] static bool REGISTERED;
301#ifdef _MSC_VER
302 static_assert((bool)&REGISTERED);
303#endif
304
315 static bool addSubType()
316 {
317 CreatorFunc func = [](Args... args) -> std::unique_ptr<Base> {
318 return static_cast<std::unique_ptr<Base>>(new derivedClass(std::forward<Args>(args
319 )...));
320 };
321 RuntimeSelectionFactory::table()[derivedClass::name()] = func;
322
324 childData.doc = []() -> std::string { return derivedClass::doc(); };
325 childData.schema = []() -> std::string { return derivedClass::schema(); };
326 RuntimeSelectionFactory::docTable()[derivedClass::name()] = childData;
327
328 return true;
329 }
330
331 ~Register() override
332 {
333 if (REGISTERED)
334 {
335 const auto& tbl = RuntimeSelectionFactory::table();
336 const auto it = tbl.find(derivedClass::name());
337 REGISTERED = (it != tbl.end());
338 }
339 }
340
341#ifdef _MSC_VER
342 private:
343
344 Register() { (void)REGISTERED; }
345#endif
346 };
347
348 virtual ~RuntimeSelectionFactory() = default;
349
350 static std::size_t size() { return table().size(); }
351
361 {
362 static LookupTable tbl;
363 return tbl;
364 }
365
375 {
376 static ClassDocTable tbl;
377 return tbl;
378 }
379
380private:
381
382
388 static void keyExistsOrError(const std::string& name)
389 {
390 const auto& tbl = table();
391 if (tbl.find(name) == tbl.end())
392 {
393 auto msg = std::string(" Could not find constructor for ") + name + "\n";
394 msg += "valid constructors are: \n";
395 for (const auto& it : tbl)
396 {
397 msg += " - " + it.first + "\n";
398 }
399 NF_ERROR_EXIT(msg);
400 }
401 }
402
403 RuntimeSelectionFactory() = default;
404};
405
406// Initialize the static variable and register the class
407template<class Base, class... Args>
408template<class derivedClass>
409bool RuntimeSelectionFactory<Base, Parameters<Args...>>::template Register<
410 derivedClass>::REGISTERED =
411 RuntimeSelectionFactory<Base, Parameters<Args...>>::template Register<derivedClass>::addSubType(
412 );
413
414}; // namespace NeoFOAM
Represents the data for a base class.
std::function< std::string(const std::string &)> schema
std::function< std::string(const std::string &)> doc
std::function< std::vector< std::string >()> entries
Provides a mechanism for registering and retrieving documentation for base and derived classes.
static std::vector< std::string > entries(const std::string &baseClassName)
static void registerClass(std::string name, BaseClassData data)
std::unordered_map< std::string, BaseClassData > LookupTable
static std::string schema(const std::string &baseClassName, const std::string &derivedClassName)
static std::string doc(const std::string &baseClassName, const std::string &derivedClassName)
Class representing the documentation for a derived class.
std::unordered_map< std::string, DerivedClassDocumentation > ClassDocTable
static LookupTable & table()
Returns the lookup table for runtime selection.
static ClassDocTable & docTable()
Returns the documentation table for runtime selection.
static std::string doc(const std::string &derivedClassName)
static std::string schema(const std::string &derivedClassName)
static std::vector< std::string > entries()
Get a vector of all entries in the runtime selection factory.
static std::unique_ptr< Base > create(const std::string &key, Args... args)
Creates an instance of a derived class based on the provided key.
A factory class for runtime selection of derived classes.
A template class for registering derived classes with a base class.
#define NF_ERROR_EXIT(message)
Macro for printing an error message and aborting the program.
Definition error.hpp:108
const std::string & name(const NeoFOAM::Document &doc)
Retrieves the name of a Document.
Template struct for registering documentation of a base class.
static bool init()
Static function to initialize the registration of the class documentation.