Nektar++
Loading...
Searching...
No Matches
BasicUtils/NekFactory.hpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: NekFactory.hpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Factory pattern class for Nektar
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#ifndef NEKTAR_LIBUTILITIES_BASICUTILS_NEKFACTORY
36#define NEKTAR_LIBUTILITIES_BASICUTILS_NEKFACTORY
37
38#include <functional>
39#include <iostream>
40#include <memory>
41#include <sstream>
42#include <string>
43#include <unordered_map>
44
45#ifdef NEKTAR_USE_THREAD_SAFETY
46#include <mutex>
47#include <shared_mutex>
48#include <thread>
49#endif
50
53
55{
56
57#ifdef NEKTAR_USE_THREAD_SAFETY
58// Generate parameter typenames with default type of 'none'
59typedef std::unique_lock<std::shared_mutex> WriteLock;
60typedef std::shared_lock<std::shared_mutex> ReadLock;
61#endif
62
63/**
64 * @class NekFactory
65 *
66 * @brief Provides a generic Factory class.
67 *
68 * Implements a generic object factory. Class-types which use an arbitrary
69 * number of parameters may be used via C++ variadic templating.
70 *
71 * To allow a class to be instantiated by the factory, the following are
72 * required in each class definition (in the case of a single parameter):
73 *
74 * \code
75 * static [baseclass]* create([paramtype1] &P) {
76 * return new [derivedclass](P);
77 * }
78 * static std::string className;
79 * \endcode
80 *
81 * and outside the class definition in the implementation:
82 *
83 * \code
84 * std::string [derivedclass]::className
85 * = Factory<std::string,[baseclass],[paramtype1]>::
86 * RegisterCreatorFunction("[derivedclass]",
87 * [derivedclass]::create,"Description");
88 * \endcode
89 *
90 * The assignment of the static variable className is done through the call to
91 * RegisterCreatorFunction, which registers the class with the factory prior to
92 * the start of the main() routine.
93 *
94 * To create an instance of a derived class, for instance:
95 * \code
96 * [baseclass]* var_name =
97 * Factory<std::string,[baseclass],[paramtype1]>
98 * ::CreateInstance("[derivedclass]",Param1);
99 * \endcode
100 */
101template <typename tKey, // reference tag (e.g. string, int)
102 typename tBase, // base class
103 typename... tParam>
105{
106public:
107 /// Shared pointer to an object of baseclass type.
108 typedef std::shared_ptr<tBase> tBaseSharedPtr;
109 /// CreatorFunction type which takes parameter and returns base class shared
110 /// pointer.
111 typedef std::function<tBaseSharedPtr(tParam...)> CreatorFunction;
112
113 /// Define a struct to hold the information about a module.
115 {
116 ModuleEntry(CreatorFunction pFunc, const std::string pDesc)
117 : m_func(pFunc), m_desc(pDesc)
118 {
119 }
120
121 /// Function used to create instance of class.
123 /// Description of class for use in listing available classes.
124 std::string m_desc;
125 };
126
127 /// Factory map between key and module data.
128 typedef std::unordered_map<tKey, ModuleEntry, HashOp> tMapFactory;
129
130public:
131 NekFactory() = default;
132
133 /**
134 * @brief Create an instance of the class referred to by \c idKey.
135 *
136 * Searches the factory's map for the given key and returns a shared
137 * base class pointer to a new instance of the associated class.
138 * @param idKey Key of class to create.
139 * @param args Parameter to pass to class constructor.
140 * @returns Base class pointer to new instance.
141 */
142 tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
143 {
144#ifdef NEKTAR_USE_THREAD_SAFETY
145 ReadLock vReadLock(m_mutex);
146#endif
147
148 // Now try and find the key in the map.
149 auto it = getMapFactory()->find(idKey);
150
151 // If successful, check the CreatorFunction is defined and
152 // create a new instance of the class.
153 if (it != getMapFactory()->end())
154 {
155 ModuleEntry *tmp = &(it->second);
156#ifdef NEKTAR_USE_THREAD_SAFETY
157 vReadLock.unlock();
158#endif
159
160 if (tmp->m_func)
161 {
162 try
163 {
164 return tmp->m_func(args...);
165 }
166 catch (const std::string &s)
167 {
168 std::stringstream errstr;
169 errstr << "Unable to create module: " << idKey << "\n";
170 errstr << s;
171 NEKERROR(ErrorUtil::efatal, errstr.str());
172 }
173 }
174 }
175
176 // If we get this far, the key doesn't exist, so throw an error.
177 std::stringstream errstr;
178 errstr << "No such module: " << idKey << std::endl;
179 PrintAvailableClasses(errstr);
180 NEKERROR(ErrorUtil::efatal, errstr.str());
181 return tBaseSharedPtr();
182 }
183
184 /**
185 * @brief Register a class with the factory.
186 *
187 * This function is called by each class in a static context (prior
188 * to the execution of main()) and creates an entry for the class
189 * in the factory's map.
190 * @param idKey Key used to reference the class.
191 * @param classCreator Function to call to create an instance
192 * of this class.
193 * @param pDesc Optional description of class.
194 * @returns The given key \c idKey.
195 */
196 tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator,
197 std::string pDesc = "")
198 {
199#ifdef NEKTAR_USE_THREAD_SAFETY
200 WriteLock vWriteLock(m_mutex);
201#endif
202
203 ModuleEntry e(classCreator, pDesc);
204 getMapFactory()->insert(std::pair<tKey, ModuleEntry>(idKey, e));
205 return idKey;
206 }
207
208 /**
209 * @brief Checks if a particular module is available.
210 */
211 bool ModuleExists(tKey idKey)
212 {
213#ifdef NEKTAR_USE_THREAD_SAFETY
214 ReadLock vReadLock(m_mutex);
215#endif
216
217 // Now try and find the key in the map.
218 auto it = getMapFactory()->find(idKey);
219
220 if (it != getMapFactory()->end())
221 {
222 return true;
223 }
224 return false;
225 }
226
227 /**
228 * @brief Prints the available classes to stdout.
229 */
230 void PrintAvailableClasses(std::ostream &pOut = std::cout)
231 {
232#ifdef NEKTAR_USE_THREAD_SAFETY
233 ReadLock vReadLock(m_mutex);
234#endif
235
236 pOut << std::endl << "Available classes: " << std::endl;
237 for (auto &it : *getMapFactory())
238 {
239 pOut << " " << it.first;
240 if (it.second.m_desc != "")
241 {
242 pOut << ":" << std::endl
243 << " " << it.second.m_desc << std::endl;
244 }
245 else
246 {
247 pOut << std::endl;
248 }
249 }
250 }
251
252 /**
253 * @brief Returns the description of a class
254 */
255 std::string GetClassDescription(tKey idKey)
256 {
257#ifdef NEKTAR_USE_THREAD_SAFETY
258 ReadLock vReadLock(m_mutex);
259#endif
260
261 // Now try and find the key in the map.
262 auto it = getMapFactory()->find(idKey);
263
264 std::stringstream errstr;
265 errstr << "No such module: " << idKey << std::endl;
266 ASSERTL0(it != getMapFactory()->end(), errstr.str());
267 return it->second.m_desc;
268 }
269
270protected:
271 /**
272 * @brief Ensure the factory's map is created.
273 * @returns The factory's map.
274 */
276 {
277 return &m_mapFactory;
278 }
279
280private:
281 NekFactory(const NekFactory &rhs) = delete;
282 NekFactory &operator=(const NekFactory &rhs) = delete;
283
285
286#ifdef NEKTAR_USE_THREAD_SAFETY
287 std::shared_mutex m_mutex;
288#endif
289};
290
291} // namespace Nektar::LibUtilities
292
293#endif
#define ASSERTL0(condition, msg)
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Provides a generic Factory class.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
NekFactory(const NekFactory &rhs)=delete
std::unordered_map< tKey, ModuleEntry, HashOp > tMapFactory
Factory map between key and module data.
std::shared_ptr< tBase > tBaseSharedPtr
Shared pointer to an object of baseclass type.
NekFactory & operator=(const NekFactory &rhs)=delete
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
tMapFactory * getMapFactory()
Ensure the factory's map is created.
void PrintAvailableClasses(std::ostream &pOut=std::cout)
Prints the available classes to stdout.
std::function< tBaseSharedPtr(tParam...)> CreatorFunction
CreatorFunction type which takes parameter and returns base class shared pointer.
bool ModuleExists(tKey idKey)
Checks if a particular module is available.
std::string GetClassDescription(tKey idKey)
Returns the description of a class.
std::shared_lock< std::shared_mutex > ReadLock
Definition Thread.h:377
std::unique_lock< std::shared_mutex > WriteLock
Definition Thread.h:376
Define a struct to hold the information about a module.
ModuleEntry(CreatorFunction pFunc, const std::string pDesc)
std::string m_desc
Description of class for use in listing available classes.
CreatorFunction m_func
Function used to create instance of class.