source: src/CodePatterns/PrototypeFactory.hpp@ 084729c

Action_Thermostats Add_AtomRandomPerturbation Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_ChronosMutex Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids_IntegrationTest JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg ThirdParty_MPQC_rebuilt_buildsystem TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes stable
Last change on this file since 084729c was 084729c, checked in by Frederik Heber <heber@…>, 8 years ago

Squashed 'ThirdParty/CodePatterns/' content from commit c1e1041

git-subtree-dir: ThirdParty/CodePatterns
git-subtree-split: c1e10418c454f98be2f43d93167642b0008428fc

  • Property mode set to 100644
File size: 19.1 KB
Line 
1/*
2 * PrototypeFactory.hpp
3 *
4 * Created on: Jan 3, 2011
5 * Author: heber
6 */
7
8#ifndef PROTOTYPEFACTORY_HPP_
9#define PROTOTYPEFACTORY_HPP_
10
11// include config.h
12#ifdef HAVE_CONFIG_H
13#include <config.h>
14#endif
15
16#include <map>
17#include <typeinfo>
18
19#include "CodePatterns/Assert.hpp"
20
21#include "CodePatterns/Clone.hpp"
22
23class PrototypeFactoryTest;
24
25/** \section <PrototypeFactory> (Prototype Factory Howto)
26 *
27 * This template produces the generic prototype factory pattern.
28 *
29 * <h2> Introduction </h2>
30 *
31 * The prototype factory is very similar to the factory pattern with one
32 * important difference: An prototype factory contains prototypes that are
33 * cloned, whereas a factory contains creation wrappers that just create
34 * a new object (i.e. without copying any additional information).
35 *
36 * A prototype factory is a class that instantiates other types. In order to be
37 * able to do so the following prerequisites must be met:
38 * -# there is a list of types the factory should be able to produce
39 * -# these types have to implement the Clone pattern, i.e. derive from it and
40 * implement a function clone(), see @ref <Clone> ''(Clone)''.
41 * -# they all must be derived from a common class (e.g. an interface) which is
42 * the type of the reference the factory returned the newly created objects
43 * as.
44 *
45 * The reason for the last one is that it is not possible -- except by an ugly
46 * static_cast -- to retrieve the particular type and if nonetheless needed,
47 * factory is probably the wrong pattern. After all what a factory does is
48 * hiding the specifics of a certain subtype, e.g. to control a motor it is not
49 * necessary to know how fast it goes and what it precisely does, you just need
50 * need the throttle as a control and some kind of rpm feedback. This control
51 * and feedback would be the stuff abstracted into the interface class.
52 *
53 * <h2>How to make a class Prototype Factory</h2>
54 *
55 * If you want to make a class a Prototype Factory you can use the following
56 * sequence of steps.
57 *
58 * Before we begin, we assume the following #defines to represent ...
59 * - Abstract_Interface_Class: the aforementioned common interface of all
60 * particular types the factory should produce.
61 * - Abstract_Encapsulation_Class: we need to connect the interface class (which
62 * must not be a template) with the types to put into the factory. Hence, we
63 * need this (templated) class that encapsulates each possible type as its
64 * template argument while deriving from (and implementing) the abstract
65 * interface as well.
66 * - MyPrototypeFactory: Your specific factory that spills out references to
67 * Abstract_Interface_Class that are embodied by
68 * Abstract_Encapsulation_Class instances.
69 *
70 * For present code, have a look at RandomNumberDistributionFactory.def,
71 * RandomNumberDistribution_Encapsulation.hpp and
72 * RandomNumberDistributionFactory.hpp.
73 *
74 * @attention{
75 * One last \b warning before we begin: Your types should reside in a distinct
76 * (and not the global) namespace, otherwise you may get strange errors such
77 * as
78 * \verbatim
79 * /usr/include/boost/preprocessor/iteration/detail/local.hpp:34: error: type/value mismatch at argument 1 in template parameter list for ‘template<class T> class stub’
80 * /usr/include/boost/preprocessor/iteration/detail/local.hpp:34: error: expected a type, got ‘(FactoryTypeList::ListOfKnownTypes)0u’
81 * \endverbatim
82 * i.e. the compiler cannot discern between the enumerated types and the true
83 * types due.
84 * @remarks But having your particular types in their own name space is always
85 * a good idea.
86 * }
87 *
88 * Now beginning, first remember that all we need is a list of already present
89 * types, i.e. implemented classes. The rest is the stuff below which is all
90 * you need (and although it looks like quite a lot, it is actually not).
91 *
92 * Then, do the following steps:
93 * - create a file "RandomNumberDistributionFactory.def" containing only
94 * preprocessor defines as follows:
95 * @code
96 * #ifndef RANDOMNUMBERDISTRIBUTIONPROTOTYPEFACTORY_DEF_
97 * #define RANDOMNUMBERDISTRIBUTIONPROTOTYPEFACTORY_DEF_
98 *
99 * #define type_seq (uniform_smallint)(uniform_int)
100 * #define Abstract_Interface_Class RandomNumberDistribution
101 * #define Abstract_Encapsulation_Class RandomNumberDistribution_Encapsulation
102 * #define type_name_space boost::
103 * #undef type_suffix
104 *
105 * #endif //RANDOMNUMBERDISTRIBUTIONPROTOTYPEFACTORY_DEF_
106 * @endcode
107 * where \a type_seq is the list of types, each element in round brackets,
108 * \a Abstract_Interface_Class is the class name of the abstract base class
109 * which a reference to the factory spills out, and
110 * \a Abstract_Encapsulation_Class is the class name of the (templated)
111 * encapsulation class of each type. Optionally, these types may reside in
112 * some other name_space, define this one via type_name_space (with suffixes
113 * double colons!). If you need to add something to the types such as "<>",
114 * do so in type_suffix.
115 * Note that the first two and the last line are just to avoid double
116 * inclusion, also the define made therein is used for the following step ...
117 * - create the undefine file "RandomNumberDistributionFactory.undef" which
118 * undefines all the previously made definitions to allow for more factories
119 * created without the danger of the various defines getting in the way of
120 * each other:
121 * @code
122 * #ifdef RANDOMNUMBERDISTRIBUTIONPROTOTYPEFACTORY_DEF_
123 *
124 * #undef type_seq
125 * #undef type_seq_size
126 * #undef Abstract_Interface_Class
127 * #undef Abstract_Encapsulation_Class
128 * #undef type_name_space
129 * #undef type_suffix
130 *
131 * #undef RANDOMNUMBERDISTRIBUTIONPROTOTYPEFACTORY_DEF_
132 * #endif
133 * @endcode
134 * - Create the encapsulation of the desired types, listed in the above define
135 * type_seq via a templated class:
136 * @code
137 * #include "Clone.hpp" // the creator wrapper
138 * #include "RandomNumberDistribution.hpp" // the abstract interface class
139 * template <class type>
140 * class RandomNumberDistribution_Encapsulation :
141 * public RandomNumberDistribution, // inherit from abstract interface
142 * public Clone<RandomNumberDistribution> // the specialized clone pattern
143 * {
144 * // Our Prototype Factory is friend because it needs to access protected cstor
145 * // to instantiate prototypes.
146 * friend class RandomNumberDistributionFactory;
147 *
148 * protected: // no one except factory and ourselves should be allowed to call them
149 * RandomNumberDistribution_Encapsulation() {} // constructor
150 * virtual ~RandomNumberDistribution_Encapsulation() {} // virtual destructor
151 * public:
152 * // ... some interface functions here with template implementation ...
153 * // i.e. those defined as virtual functions in the abstract interface
154 * RandomNumberDistribution* clone() {
155 * // .. instantiate a clone of this instance ..
156 * return MyClone;
157 * };
158 * private:
159 * type encapsulated_type; // the instance of the type to encapsulate
160 * };
161 * @endcode
162 * - in the header of your factory put these before any declaration:
163 * @code
164 * // has to be appear BEFORE PrototypeFactory.hpp is included!
165 * #include "RandomNumberDistributionFactory.def"
166 * #include "CodePatterns/FactoryTypeList.hpp"
167 * #include "RandomNumberDistributionFactory.undef"
168 * #include "CodePatterns/PrototypeFactory.hpp"
169 * @endcode
170 * Then declare your factory by inheriting from Factory<Abstract_Interface_Class>
171 * @code
172 * class RandomNumberDistributionFactory :
173 * public Factory<RandomNumberDistribution>
174 * {
175 * // place here all other classes as friends that are allowed to access
176 * // Factory<RandomNumberDistribution>::getPrototypeManipulator()
177 * public:
178 * void FillPrototypeTable(); // these are defined automatically
179 * void EmptyPrototypeTable(); // you don't have to implement them
180 *
181 * RandomNumberDistributionFactory();
182 * virtual ~RandomNumberDistributionFactory();
183 *
184 * };
185 * #include "RandomNumberDistributionFactory.undef"
186 * @endcode
187 * where the last includes undefines the defines made in the first step and
188 * assures that another factory can be created without any old defines
189 * getting in the way.
190 * @note FactoryTypeList.hpp is necessary as we can't have a forward
191 * declaration of an enum which we need in the header of
192 * PrototypeFactory<T>. Hence, there is an extra class the wraps the
193 * enum which we inherit.
194 * - finally implement the prototype factory by:
195 * @code
196 * #include "RandomNumberDistribution_Encapsulation.hpp"
197 * #include "RandomNumberDistributionFactory.hpp"
198 * // has to be included BEFORE PrototypeFactory_impl.hpp!
199 * #include "RandomNumberDistributionFactory.def"
200 * #include "CodePatterns/PrototypeFactory_impl.hpp"
201 *
202 * RandomNumberDistributionFactory::RandomNumberDistributionFactory() {
203 * FillPrototypeTable();
204 * }
205 * RandomNumberDistributionFactory::~RandomNumberDistributionFactory() {
206 * EmptyPrototypeTable();
207 * }
208 *
209 * CONSTRUCT_SINGLETON(RandomNumberDistributionFactory)
210 * CONSTRUCT_PROTOTYPEFACTORY(RandomNumberDistribution)
211 *
212 * #include "RandomNumberDistributionFactory.undef"
213 * @endcode
214 * @note the two functions within the constructor that fill and empty the
215 * prototype table are constructed automatically but you have to call them
216 * because only you (that is RandomNumberDistributionFactory in our case)
217 * is a true friend of RandomNumberDistribution_Encapsulation and can access
218 * its cstor and dstor.
219 *
220 * That's all.
221 */
222template <class T>
223class PrototypeFactory : public FactoryTypeList<T>
224{
225public:
226 /** Constructor of class PrototypeFactory.
227 *
228 */
229 PrototypeFactory()
230 {
231 FillEnumTable();
232// FillPrototypeTable();
233 }
234
235 /** (virtual) Destructor of class PrototypeFactory.
236 *
237 */
238 virtual ~PrototypeFactory()
239 {
240 // clear out factories map to allow boost::shared_ptr to do their work (i.e. to release mem)
241 // this is necessary as factory is an object
242 enums.clear();
243 names.clear();
244// EmptyPrototypeTable();
245// PrototypeTable.clear();
246 }
247
248
249 /** Typedef of enumeration of all types known to this factory
250 *
251 * @note The true enumeration has to be stored in a wrapper class
252 * FactoryTypeList because enumerations cannot be forward declared as is
253 * necessary here.
254 */
255 typedef enum FactoryTypeList<T>::ListOfKnownTypes TypeList;
256
257 /** Setter for currenttype to produce.
258 *
259 * @param instance_name name of type
260 */
261 void setCurrentType(const std::string instance_name)
262 {
263 ASSERT(enums.count(instance_name) != 0,
264 "PrototypeFactory<"+toString(typeid(T).name())+">::setCurrentType() - type "+instance_name+" is not registered.");
265 currenttype = enums[instance_name];
266 }
267
268 /** Setter for currenttype to produce.
269 *
270 * @param instance_type enumeration index of type
271 */
272 void setCurrentType(TypeList instance_type)
273 {
274 ASSERT(names.count(instance_type) != 0,
275 "PrototypeFactory<"+toString(typeid(T).name())+">::setCurrentType() - enum type "+toString(instance_type)+" is not registered.");
276 currenttype = instance_type;
277 }
278
279 /** Getter for currenttype to produce.
280 *
281 * @return name of currenttype
282 */
283 const std::string & getCurrentTypeName() const
284 {
285 return names[currenttype];
286 }
287
288 /** Getter for currenttype to produce.
289 *
290 * @return enumeration index of currenttype
291 */
292 TypeList getCurrentTypeEnum() const
293 {
294 return currenttype;
295 }
296
297 /** Getter for desired type of product.
298 *
299 * @param enumeration index of product
300 * @return reference to copy of product
301 */
302 T* getProduct(TypeList instance_type) const
303 {
304 ASSERT(names.count(instance_type) != 0,
305 "PrototypeFactory<"+toString(typeid(T).name())+">::getProduct() - enum type "+toString(instance_type)+" is not registered.");
306 return PrototypeTable[instance_type]->clone();
307 }
308
309 /** Getter for desired type of product.
310 *
311 * @param instance_name name of product
312 * @return reference to copy of product
313 */
314 T* getProduct(const std::string instance_name) const
315 {
316 ASSERT(enums.count(instance_name) != 0,
317 "PrototypeFactory<"+toString(typeid(T).name())+">::getProduct() - type name "+instance_name+" is not registered.");
318 return PrototypeTable[ enums[instance_name] ]->clone();
319 }
320
321 /** Getter for desired type of product.
322 *
323 * @param instance_type_info object of the desired type
324 * @return reference to copy of current type product or NULL if type mismatch
325 */
326 T* getProduct(const std::type_info &instance_type_info) const
327 {
328 ASSERT(types.count(instance_type_info.name()) != 0,
329 "PrototypeFactory<"+toString(typeid(T).name())+">::getProduct() - type info name "+instance_type_info.name()+" is not registered.");
330 return PrototypeTable[ types[instance_type_info.name()] ]->clone();
331 }
332
333 /** Getter for current type of product.
334 *
335 * @return reference to copy of current type product
336 */
337 T* getProduct() const
338 {
339 return PrototypeTable[currenttype]->create();
340 }
341
342 /** Getter for the name of the desired type of product.
343 *
344 * @return name of distribution
345 */
346 const std::string &getName(TypeList instance_type) const
347 {
348 ASSERT(names.count(instance_type) != 0,
349 "PrototypeFactory<"+toString(typeid(T).name())+">::getName() - enum type "+toString(instance_type)+" is not registered.");
350 return names[instance_type];
351 }
352
353 /** Getter for the enumeration index of the desired type of product.
354 *
355 * @return enum of distribution
356 */
357 TypeList getEnum(const std::string instance_name) const
358 {
359 ASSERT(enums.count(instance_name) != 0,
360 "PrototypeFactory<"+toString(typeid(T).name())+">::getEnum() - type name "+instance_name+" is not registered.");
361 return enums[instance_name];
362 }
363
364
365protected:
366 /** Yields true prototype of the product and sets entry to NULL on
367 * PrototypeTable.
368 *
369 * @warning This method is \b intentionally protected such that only
370 * specific friends are allowed to access it.
371 * @warning This method performs a dynamic_cast from Clone<IPrototype>* to
372 * IPrototype*. This prevents also that prototype is cloneable outside of
373 * factory.
374 *
375 * @param instance_name name of product
376 * @return const reference to prototype stored in this factory, i.e. read-only
377 */
378 const T& getPrototype(const std::string instance_name)
379 {
380 ASSERT(enums.count(instance_name) != 0,
381 "PrototypeFactory<"+toString(typeid(T).name())+">::getPrototypeManipulator() - type name "+instance_name+" is not registered.");
382 T* prototype = dynamic_cast<T *>(PrototypeTable[ enums[instance_name] ]);
383 return *prototype;
384 }
385
386 /** Yields true prototype of the product and sets entry to NULL on
387 * PrototypeTable.
388 *
389 * @warning This method is \b intentionally protected such that only
390 * specific friends are allowed to access it.
391 *
392 * @param enumeration index of product
393 * @return const reference to prototype stored in this factory, i.e. read-only
394 */
395 const T& getPrototype(TypeList instance_type)
396 {
397 ASSERT(names.count(instance_type) != 0,
398 "PrototypeFactory<"+toString(typeid(T).name())+">::getPrototypeManipulator() - enum type "+toString(instance_type)+" is not registered.");
399 T* prototype = dynamic_cast<T *>(PrototypeTable[instance_type]);
400 return *prototype;
401 }
402
403 /** Yields true prototype of the product and sets entry to NULL on
404 * PrototypeTable.
405 *
406 * @warning This method is \b intentionally protected such that only
407 * specific friends are allowed to access it.
408 *
409 * @param instance_type_info object of the desired type
410 * @return const reference to prototype stored in this factory, i.e. read-only
411 */
412 const T& getPrototype(const std::type_info &instance_type_info)
413 {
414 ASSERT(types.count(instance_type_info.name()) != 0,
415 "PrototypeFactory<"+toString(typeid(T).name())+">::getPrototypeManipulator() - type info name "+instance_type_info.name()+" is not registered.");
416 T* prototype = dynamic_cast<T *>(PrototypeTable[ types[instance_type_info.name()] ]);
417 return *prototype;
418 }
419
420 /** Yields true prototype of the currenttype product and sets entry to NULL
421 * on PrototypeTable.
422 *
423 * @warning This method is \b intentionally protected such that only
424 * specific friends are allowed to access it.
425 *
426 * @return const reference to prototype stored in this factory, i.e. read-only
427 */
428 const T& getPrototype()
429 {
430 T* prototype = dynamic_cast<T *>(PrototypeTable[ currenttype ]);
431 return *prototype;
432 }
433
434 /** Creates instances of all possible distribution types
435 * and stores them in \a DistributionPrototypeTable.
436 *
437 * @note This has to be handed down to the actual implementation of the
438 * desired prototype factory class as cstor and dstor of the abstract
439 * encapsulation class are protected (of a @ref <Clone> (Clone)) and hence
440 * cannot be accessed from this class (because we cannot befriend it from
441 * here).
442 */
443 virtual void FillPrototypeTable() = 0;
444
445 /** Removes all prototypes from \a PrototypeTable and free's memory.
446 *
447 * \sa FillPrototypeTable()
448 */
449 virtual void EmptyPrototypeTable() = 0;
450
451 /** Create association for enums to strings and vice versa
452 * and stores them in \a distributions tables.
453 */
454 void FillEnumTable();
455
456 typedef std::map<
457 std::string,
458 TypeList
459 > TypeMap;
460 typedef std::map<
461 std::string,
462 TypeList
463 > EnumMap;
464 typedef std::map<
465 TypeList,
466 Clone<T> *
467 > InstanceTable;
468 typedef std::map<
469 TypeList,
470 std::string
471 > NameMap;
472
473 static TypeList currenttype;
474 static TypeMap types;
475 static EnumMap enums;
476 static InstanceTable PrototypeTable;
477 static NameMap names;
478};
479
480template <class T> typename PrototypeFactory<T>::TypeList PrototypeFactory<T>::currenttype = (typename PrototypeFactory<T>::TypeList)0;
481template <class T> typename PrototypeFactory<T>::TypeMap PrototypeFactory<T>::types;
482template <class T> typename PrototypeFactory<T>::EnumMap PrototypeFactory<T>::enums;
483template <class T> typename PrototypeFactory<T>::NameMap PrototypeFactory<T>::names;
484template <class T> typename PrototypeFactory<T>::InstanceTable PrototypeFactory<T>::PrototypeTable;
485
486/**
487 * This define allows simple instantiation of the necessary factory functions
488 * at a chosen place.
489 */
490#define CONSTRUCT_PROTOTYPEFACTORY(InstanceType) \
491 template PrototypeFactory<InstanceType>::PrototypeFactory(); \
492 template PrototypeFactory<InstanceType>::~PrototypeFactory(); \
493 template void PrototypeFactory<InstanceType>::setCurrentType(const std::string instance_name); \
494 template void PrototypeFactory<InstanceType>::setCurrentType(TypeList instance_type); \
495 template const std::string & PrototypeFactory<InstanceType>::getCurrentTypeName() const; \
496 template PrototypeFactory<InstanceType>::TypeList PrototypeFactory<InstanceType>::getCurrentTypeEnum() const; \
497 template InstanceType* PrototypeFactory<InstanceType>::getProduct(TypeList instance_type) const; \
498 template InstanceType* PrototypeFactory<InstanceType>::getProduct(const std::string instance_name) const; \
499 template const std::string &PrototypeFactory<InstanceType>::getName(TypeList instance_type) const; \
500 template PrototypeFactory<InstanceType>::TypeList PrototypeFactory<InstanceType>::getEnum(const std::string instance_name) const;
501
502
503#endif /* PROTOTYPEFACTORY_HPP_ */
Note: See TracBrowser for help on using the repository browser.