Nektar++
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 <map>
41#include <memory>
42#include <sstream>
43#include <string>
44
45#ifdef NEKTAR_USE_THREAD_SAFETY
46#include <mutex>
47#include <shared_mutex>
48#include <thread>
49#endif
50
52
54{
55
56#ifdef NEKTAR_USE_THREAD_SAFETY
57// Generate parameter typenames with default type of 'none'
58typedef std::unique_lock<std::shared_mutex> WriteLock;
59typedef std::shared_lock<std::shared_mutex> ReadLock;
60#endif
61
62/**
63 * @class NekFactory
64 *
65 * @brief Provides a generic Factory class.
66 *
67 * Implements a generic object factory. Class-types which use an arbitrary
68 * number of parameters may be used via C++ variadic templating.
69 *
70 * To allow a class to be instantiated by the factory, the following are
71 * required in each class definition (in the case of a single parameter):
72 *
73 * \code
74 * static [baseclass]* create([paramtype1] &P) {
75 * return new [derivedclass](P);
76 * }
77 * static std::string className;
78 * \endcode
79 *
80 * and outside the class definition in the implementation:
81 *
82 * \code
83 * std::string [derivedclass]::className
84 * = Factory<std::string,[baseclass],[paramtype1]>::
85 * RegisterCreatorFunction("[derivedclass]",
86 * [derivedclass]::create,"Description");
87 * \endcode
88 *
89 * The assignment of the static variable className is done through the call to
90 * RegisterCreatorFunction, which registers the class with the factory prior to
91 * the start of the main() routine.
92 *
93 * To create an instance of a derived class, for instance:
94 * \code
95 * [baseclass]* var_name =
96 * Factory<std::string,[baseclass],[paramtype1]>
97 * ::CreateInstance("[derivedclass]",Param1);
98 * \endcode
99 */
100template <typename tKey, // reference tag (e.g. string, int)
101 typename tBase, // base class
102 typename... tParam>
104{
105public:
106 /// Comparison predicator of key
107 typedef std::less<tKey> tPredicator;
108 /// Shared pointer to an object of baseclass type.
109 typedef std::shared_ptr<tBase> tBaseSharedPtr;
110 /// CreatorFunction type which takes parameter and returns base class shared
111 /// pointer.
112 typedef std::function<tBaseSharedPtr(tParam...)> CreatorFunction;
113
114 /// Define a struct to hold the information about a module.
116 {
117 ModuleEntry(CreatorFunction pFunc, const std::string pDesc)
118 : m_func(pFunc), m_desc(pDesc)
119 {
120 }
121
122 /// Function used to create instance of class.
124 /// Description of class for use in listing available classes.
125 std::string m_desc;
126 };
127
128 /// Factory map between key and module data.
129 typedef std::map<tKey, ModuleEntry, tPredicator> TMapFactory;
130
131public:
132 NekFactory() = default;
133
134 /**
135 * @brief Create an instance of the class referred to by \c idKey.
136 *
137 * Searches the factory's map for the given key and returns a shared
138 * base class pointer to a new instance of the associated class.
139 * @param idKey Key of class to create.
140 * @param args Parameter to pass to class constructor.
141 * @returns Base class pointer to new instance.
142 */
143 tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
144 {
145#ifdef NEKTAR_USE_THREAD_SAFETY
146 ReadLock vReadLock(m_mutex);
147#endif
148
149 // Now try and find the key in the map.
150 auto it = getMapFactory()->find(idKey);
151
152 // If successful, check the CreatorFunction is defined and
153 // create a new instance of the class.
154 if (it != getMapFactory()->end())
155 {
156 ModuleEntry *tmp = &(it->second);
157#ifdef NEKTAR_USE_THREAD_SAFETY
158 vReadLock.unlock();
159#endif
160
161 if (tmp->m_func)
162 {
163 try
164 {
165 return tmp->m_func(args...);
166 }
167 catch (const std::string &s)
168 {
169 std::stringstream errstr;
170 errstr << "Unable to create module: " << idKey << "\n";
171 errstr << s;
172 NEKERROR(ErrorUtil::efatal, errstr.str());
173 }
174 }
175 }
176
177 // If we get this far, the key doesn't exist, so throw an error.
178 std::stringstream errstr;
179 errstr << "No such module: " << idKey << std::endl;
180 PrintAvailableClasses(errstr);
181 NEKERROR(ErrorUtil::efatal, errstr.str());
182 return tBaseSharedPtr();
183 }
184
185 /**
186 * @brief Register a class with the factory.
187 *
188 * This function is called by each class in a static context (prior
189 * to the execution of main()) and creates an entry for the class
190 * in the factory's map.
191 * @param idKey Key used to reference the class.
192 * @param classCreator Function to call to create an instance
193 * of this class.
194 * @param pDesc Optional description of class.
195 * @returns The given key \c idKey.
196 */
197 tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator,
198 std::string pDesc = "")
199 {
200#ifdef NEKTAR_USE_THREAD_SAFETY
201 WriteLock vWriteLock(m_mutex);
202#endif
203
204 ModuleEntry e(classCreator, pDesc);
205 getMapFactory()->insert(std::pair<tKey, ModuleEntry>(idKey, e));
206 return idKey;
207 }
208
209 /**
210 * @brief Checks if a particular module is available.
211 */
212 bool ModuleExists(tKey idKey)
213 {
214#ifdef NEKTAR_USE_THREAD_SAFETY
215 ReadLock vReadLock(m_mutex);
216#endif
217
218 // Now try and find the key in the map.
219 auto it = getMapFactory()->find(idKey);
220
221 if (it != getMapFactory()->end())
222 {
223 return true;
224 }
225 return false;
226 }
227
228 /**
229 * @brief Prints the available classes to stdout.
230 */
231 void PrintAvailableClasses(std::ostream &pOut = std::cout)
232 {
233#ifdef NEKTAR_USE_THREAD_SAFETY
234 ReadLock vReadLock(m_mutex);
235#endif
236
237 pOut << std::endl << "Available classes: " << std::endl;
238 for (auto &it : *getMapFactory())
239 {
240 pOut << " " << it.first;
241 if (it.second.m_desc != "")
242 {
243 pOut << ":" << std::endl
244 << " " << it.second.m_desc << std::endl;
245 }
246 else
247 {
248 pOut << std::endl;
249 }
250 }
251 }
252
253 /**
254 * @brief Returns the description of a class
255 */
256 std::string GetClassDescription(tKey idKey)
257 {
258#ifdef NEKTAR_USE_THREAD_SAFETY
259 ReadLock vReadLock(m_mutex);
260#endif
261
262 // Now try and find the key in the map.
263 auto it = getMapFactory()->find(idKey);
264
265 std::stringstream errstr;
266 errstr << "No such module: " << idKey << std::endl;
267 ASSERTL0(it != getMapFactory()->end(), errstr.str());
268 return it->second.m_desc;
269 }
270
271protected:
272 /**
273 * @brief Ensure the factory's map is created.
274 * @returns The factory's map.
275 */
277 {
278 return &mMapFactory;
279 }
280
281private:
282 NekFactory(const NekFactory &rhs) = delete;
283 NekFactory &operator=(const NekFactory &rhs) = delete;
284
286
287#ifdef NEKTAR_USE_THREAD_SAFETY
288 std::shared_mutex m_mutex;
289#endif
290};
291
292} // namespace Nektar::LibUtilities
293
294#endif
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:202
Provides a generic Factory class.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
TMapFactory * getMapFactory()
Ensure the factory's map is created.
NekFactory(const NekFactory &rhs)=delete
std::shared_ptr< tBase > tBaseSharedPtr
Shared pointer to an object of baseclass type.
std::map< tKey, ModuleEntry, tPredicator > TMapFactory
Factory map between key and module data.
NekFactory & operator=(const NekFactory &rhs)=delete
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
Create an instance of the class referred to by idKey.
std::less< tKey > tPredicator
Comparison predicator of key.
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.