source: ThirdParty/CodePatterns/src/CodePatterns/ManipulablePrototypeFactory.hpp

Candidate_v1.6.1
Last change on this file was 41e8e2, checked in by Frederik Heber <heber@…>, 8 years ago

Merge commit '084729c5923f0123e695fbe2548b393288c1f13d' as 'ThirdParty/CodePatterns'

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