source: src/Patterns/Factory.hpp@ 724564

Last change on this file since 724564 was 724564, checked in by Frederik Heber <heber@…>, 15 years ago

Factory know has an additional type table and stubs have been refactored.

  • CommonStub.?pp contains basic classes with a int counter inside.
  • Library version is now 2:1:0, API is 1.0.3.
  • Property mode set to 100644
File size: 13.9 KB
Line 
1/*
2 * Factory.hpp
3 *
4 * Created on: Jan 3, 2011
5 * Author: heber
6 */
7
8#ifndef FACTORY_HPP_
9#define FACTORY_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 "Assert.hpp"
20
21#include "Creator.hpp"
22
23class FactoryTest;
24
25/**
26 * This template produces the generic factory pattern.
27 *
28 * <h1> Factory Howto </h1>
29 * <h2> Introduction </h2>
30 *
31 * A factory is a class that instantiates other types. In order to be able to
32 * do so the following prerequisites must be met:
33 * -# there is a list of types the factory should be able to produce
34 * -# they all must be derived from a common class (e.g. an interface) which is
35 * the type references from the factory will be returned as.
36 *
37 * The reason for the last one is that it is not possible -- except by an ugly
38 * static_cast -- to retrieve the particular type and if nonetheless needed,
39 * factory is probably the wrong pattern). After all what a factory does is
40 * hiding the specifics of a certain subtype, e.g. to control a motor it is not
41 * necessary to know how fast it goes and what it precisely does, you just need
42 * need the throttle as a control and some kind of rpm feedback. This control
43 * and feedback would be abstracted into the interface class.
44 *
45 * <h2>How to make a class Factory</h2>
46 *
47 * If you want to make a class a Factory you can use the following sequence of
48 * steps.
49 *
50 * Before we begin, we assume the following types to represent ...
51 * - Abstract_Interface_Class: the aforementioned common interface of all
52 * particular types the factory should produce.
53 * - Abstract_Encapsulation_Class: we need to connect the interface class (which
54 * must not be a template) with the types to put into the factory. Hence, we
55 * need this (templated) class that encapsulates each possible type as its
56 * template argument while deriving from (and implementing) the abstract
57 * interface as well.
58 * - MyFactory: Your specific factory that spills out references to
59 * Abstract_Interface_Class that are embodied by
60 * Abstract_Encapsulation_Class instances.
61 *
62 * For present code, have a look at RandomNumberDistributionFactory.def,
63 * RandomNumberDistribution_Encapsulation.hpp and
64 * RandomNumberDistributionFactory.hpp.
65 *
66 * @attention{
67 * One last \b warning before we begin: Your types should reside in a distinct
68 * (and not the global) namespace, otherwise you may get strange errors such
69 * as
70 * \verbatim
71 * /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’
72 * /usr/include/boost/preprocessor/iteration/detail/local.hpp:34: error: expected a type, got ‘(FactoryTypeList::ListOfKnownTypes)0u’
73 * \endverbatim
74 * i.e. he cannot discern between the enumerated types and the true types due.
75 * @remarks But having your particular types in their own name space is always
76 * a good idea.
77 * }
78 *
79 * Now beginning, first remember that all we need is a list of already present
80 * types, i.e. implemented classes. The rest is the stuff below which is all
81 * you need (and although it looks like quite a lot, it is actually not).
82 *
83 * Then, do the following steps:
84 * - create a file "RandomNumberDistributionFactory.def" containing only
85 * preprocessor defines as follows:
86 * @code
87 * #ifndef RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_
88 * #define RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_
89 *
90 * #define type_seq (uniform_smallint)(uniform_int)
91 * #define Abstract_Interface_Class RandomNumberDistribution
92 * #define Abstract_Encapsulation_Class RandomNumberDistribution_Encapsulation
93 * #define type_name_space boost::
94 *
95 * #endif //RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_
96 * @endcode
97 * where \a type_seq is the list of types, each element in round brackets,
98 * \a Abstract_Interface_Class is the class name of the abstract base class
99 * which a reference to the factory spills out, and
100 * \a Abstract_Encapsulation_Class is the class name of the (templated)
101 * encapsulation class of each type. Optionally, these types may reside in
102 * some other name_space, define this one via type_name_space (with suffixes
103 * double colons!). Note that the first two and the last line are just
104 * to avoid double inclusion, also the define made therein is used for the
105 * following step ...
106 * - create the undefine file "RandomNumberDistributionFactory.undef" which
107 * undefines all the previously made definitions to allow for more factories
108 * created without the danger of the various defines getting in the way of
109 * each other:
110 * @code
111 * #ifdef RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_
112 *
113 * #undef type_seq
114 * #undef type_seq_size
115 * #undef Abstract_Interface_Class
116 * #undef Abstract_Encapsulation_Class
117 * #undef type_name_space
118 *
119 * #undef RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_
120 * #endif
121 * @endcode
122 * - Create the encapsulation of the desired types, listed in type_seq via a
123 * templated class:
124 * @code
125 * #include "Creator.hpp" // the creator wrapper
126 * #include "RandomNumberDistribution.hpp" // the abstract interface class
127 * template <class type>
128 * class RandomNumberDistribution_Encapsulation :
129 * public RandomNumberDistribution, // inherit from abstract interface
130 * public Creator< // and creator wrapper that has to know about ...
131 * RandomNumberDistribution, // .. the abstract interface as well ..
132 * RandomNumberDistribution_Encapsulation<type> // .. and the
133 * // encapsulation class we right now define
134 * >
135 * {
136 * public:
137 * // ... some interface functions here with template implementation ...
138 * // i.e. those defined as virtual functions in the abstract interface
139 *
140 * RandomNumberDistribution_Encapsulation() {} // constructor
141 * virtual ~RandomNumberDistribution_Encapsulation() {} // virtual destructor
142 * private:
143 * type encapsulated_type; // the instance of the type to encapsulate
144 * };
145 * @endcode
146 * - in the header of your factory put these before any declaration:
147 * @code
148 * // has to be appear BEFORE Factory.hpp is included!
149 * #include "RandomNumberDistributionFactory.def"
150 * #include "CodePatterns/FactoryTypeList.hpp"
151 * #include "CodePatterns/Factory.hpp"
152 * @endcode
153 * Then declare your factory by inheriting from Factory<Abstract_Interface_Class>
154 * @code
155 * class RandomNumberDistributionFactory :
156 * public Factory<RandomNumberDistribution>
157 * {
158 * RandomNumberDistributionFactory();
159 * virtual ~RandomNumberDistributionFactory();
160 * };
161 * #include "RandomNumberDistributionFactory.undef"
162 * @endcode
163 * where the last includes undefines the defines made in the first step and
164 * assures that another factory can be created without any old defines
165 * getting in the way.
166 * @note FactoryTypeList.hpp is necessary as we can't have a forward
167 * declaration of an enum which we need in the header of Factory<T>.
168 * Hence, there is an extra class the wraps the enum which we inherit.
169 * - finally implement the factory by:
170 * @code
171 * #include "RandomNumberDistribution_Encapsulation.hpp"
172 * #include "RandomNumberDistributionFactory.hpp"
173 * // has to be included BEFORE Factory_impl.hpp!
174 * #include "RandomNumberDistributionFactory.def"
175 * #include "CodePatterns/Factory_impl.hpp"
176 *
177 * RandomNumberDistributionFactory::RandomNumberDistributionFactory() {}
178 * RandomNumberDistributionFactory::~RandomNumberDistributionFactory() {}
179 *
180 * CONSTRUCT_SINGLETON(RandomNumberDistributionFactory)
181 * CONSTRUCT_FACTORY(RandomNumberDistribution)
182 *
183 * #include "RandomNumberDistributionFactory.undef"
184 * @endcode
185 *
186 * That's all.
187 */
188template <class T>
189class Factory : public FactoryTypeList<T>
190{
191 friend class FactoryTest;
192
193public:
194 /** Constructor of class Factory.
195 *
196 */
197 Factory()
198 {
199 FillEnumTable();
200 FillPrototypeTable();
201 }
202
203 /** (virtual) Destructor of class Factory.
204 *
205 */
206 virtual ~Factory()
207 {
208 // clear out factories map to allow boost::shared_ptr to do their work (i.e. to release mem)
209 // this is necessary as factory is an object
210 enums.clear();
211 names.clear();
212 for (typename InstanceTable::iterator iter = PrototypeTable.begin();
213 !PrototypeTable.empty();
214 iter = PrototypeTable.begin()) {
215 delete (iter->second);
216 PrototypeTable.erase(iter);
217 }
218 PrototypeTable.clear();
219 }
220
221
222 /** Enumeration of all types known to this factory
223 */
224 typedef enum FactoryTypeList<T>::ListOfKnownTypes TypeList;
225
226 /** Setter for currenttype to produce.
227 *
228 * @param instance_name name of type
229 */
230 void setCurrentType(const std::string instance_name)
231 {
232 ASSERT(enums.count(instance_name) != 0,
233 "Factory<"+toString(typeid(T).name())+">::setCurrentType() - type "+instance_name+" is not registered.");
234 currenttype = enums[instance_name];
235 }
236
237 /** Setter for currenttype to produce.
238 *
239 * @param instance_type enumeration index of type
240 */
241 void setCurrentType(TypeList instance_type)
242 {
243 ASSERT(names.count(instance_type) != 0,
244 "Factory<"+toString(typeid(T).name())+">::setCurrentType() - enum type "+toString(instance_type)+" is not registered.");
245 currenttype = instance_type;
246 }
247
248 /** Getter for currenttype to produce.
249 *
250 * @return name of currenttype
251 */
252 const std::string & getCurrentTypeName() const
253 {
254 return names[currenttype];
255 }
256
257 /** Getter for currenttype to produce.
258 *
259 * @return enumeration index of currenttype
260 */
261 TypeList getCurrentTypeEnum() const
262 {
263 return currenttype;
264 }
265
266 /** Getter for desired type of product.
267 *
268 * @param enumeration index of product
269 * @return reference to copy of product
270 */
271 T* getProduct(TypeList instance_type) const
272 {
273 ASSERT(names.count(instance_type) != 0,
274 "Factory<"+toString(typeid(T).name())+">::getProduct() - enum type "+toString(instance_type)+" is not registered.");
275 return PrototypeTable[instance_type]->create();
276 }
277
278 /** Getter for desired type of product.
279 *
280 * @param name of product
281 * @return reference to copy of product
282 */
283 T* getProduct(const std::string instance_name) const
284 {
285 ASSERT(enums.count(instance_name) != 0,
286 "Factory<"+toString(typeid(T).name())+">::getProduct() - type name "+instance_name+" is not registered.");
287 return PrototypeTable[ enums[instance_name] ]->create();
288 }
289
290 /** Getter for desired type of product.
291 *
292 * @param type_info object of the desired type
293 * @return reference to copy of current type product or NULL if type mismatch
294 */
295 T* getProduct(const std::type_info &instance_type_info) const
296 {
297 ASSERT(types.count(instance_type_info.name()) != 0,
298 "Factory<"+toString(typeid(T).name())+">::getProduct() - type info name "+instance_type_info.name()+" is not registered.");
299 return PrototypeTable[ types[instance_type_info.name()] ]->create();
300 }
301
302 /** Getter for current type of product.
303 *
304 * @return reference to copy of current type product
305 */
306 T* getProduct() const
307 {
308 return PrototypeTable[currenttype]->create();
309 }
310
311 /** Getter for the name of the desired type of product.
312 *
313 * @return name of distribution
314 */
315 const std::string &getName(TypeList instance_type) const
316 {
317 ASSERT(names.count(instance_type) != 0,
318 "Factory<"+toString(typeid(T).name())+">::getName() - enum type "+toString(instance_type)+" is not registered.");
319 return names[instance_type];
320 }
321
322 /** Getter for the enumeration index of the desired type of product.
323 *
324 * @return enum of distribution
325 */
326 TypeList getEnum(const std::string instance_name) const
327 {
328 ASSERT(enums.count(instance_name) != 0,
329 "Factory<"+toString(typeid(T).name())+">::getEnum() - type name "+instance_name+" is not registered.");
330 return enums[instance_name];
331 }
332
333
334protected:
335
336 /** Creates instances of all possible distribution types
337 * and stores them in \a DistributionPrototypeTable.
338 */
339 void FillPrototypeTable();
340
341 /** Create association for enums to strings and vice versa
342 * and stores them in \a distributions tables.
343 */
344 void FillEnumTable();
345
346 typedef std::map<
347 std::string,
348 TypeList
349 > TypeMap;
350 typedef std::map<
351 std::string,
352 TypeList
353 > EnumMap;
354 typedef std::map<
355 TypeList,
356 ICreator<T> *
357 > InstanceTable;
358 typedef std::map<
359 TypeList,
360 std::string
361 > NameMap;
362
363 static TypeList currenttype;
364 static TypeMap types;
365 static EnumMap enums;
366 static InstanceTable PrototypeTable;
367 static NameMap names;
368};
369
370template <class T> typename Factory<T>::TypeList Factory<T>::currenttype = (typename Factory<T>::TypeList)0;
371template <class T> typename Factory<T>::TypeMap Factory<T>::types;
372template <class T> typename Factory<T>::EnumMap Factory<T>::enums;
373template <class T> typename Factory<T>::NameMap Factory<T>::names;
374template <class T> typename Factory<T>::InstanceTable Factory<T>::PrototypeTable;
375
376/**
377 * This define allows simple instantiation of the necessary factory functions
378 * at a chosen place.
379 */
380#define CONSTRUCT_FACTORY(InstanceType) \
381 template Factory<InstanceType>::Factory(); \
382 template Factory<InstanceType>::~Factory(); \
383 template void Factory<InstanceType>::setCurrentType(const std::string instance_name); \
384 template void Factory<InstanceType>::setCurrentType(TypeList instance_type); \
385 template const std::string & Factory<InstanceType>::getCurrentTypeName() const; \
386 template Factory<InstanceType>::TypeList Factory<InstanceType>::getCurrentTypeEnum() const; \
387 template InstanceType* Factory<InstanceType>::getProduct(TypeList instance_type) const; \
388 template InstanceType* Factory<InstanceType>::getProduct(const std::string instance_name) const; \
389 template const std::string &Factory<InstanceType>::getName(TypeList instance_type) const; \
390 template Factory<InstanceType>::TypeList Factory<InstanceType>::getEnum(const std::string instance_name) const;
391
392#endif /* FACTORY_HPP_ */
Note: See TracBrowser for help on using the repository browser.