source: src/Patterns/Factory.hpp@ 746ff1

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

Added Creator and Factory pattern.

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