/* * Factory.hpp * * Created on: Jan 3, 2011 * Author: heber */ #ifndef FACTORY_HPP_ #define FACTORY_HPP_ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include #include #include "CodePatterns/Assert.hpp" #include "CodePatterns/Creator.hpp" class FactoryTest; /** * This template produces the generic factory pattern. * *

Factory Howto

*

Introduction

* * A factory is a class that instantiates other types. In order to be able to * do so the following prerequisites must be met: * -# there is a list of types the factory should be able to produce * -# they all must be derived from a common class (e.g. an interface) which is * the type references from the factory will be returned as. * * The reason for the last one is that it is not possible -- except by an ugly * static_cast -- to retrieve the particular type and if nonetheless needed, * factory is probably the wrong pattern). After all what a factory does is * hiding the specifics of a certain subtype, e.g. to control a motor it is not * necessary to know how fast it goes and what it precisely does, you just need * need the throttle as a control and some kind of rpm feedback. This control * and feedback would be abstracted into the interface class. * *

How to make a class Factory

* * If you want to make a class a Factory you can use the following sequence of * steps. * * Before we begin, we assume the following types to represent ... * - Abstract_Interface_Class: the aforementioned common interface of all * particular types the factory should produce. * - Abstract_Encapsulation_Class: we need to connect the interface class (which * must not be a template) with the types to put into the factory. Hence, we * need this (templated) class that encapsulates each possible type as its * template argument while deriving from (and implementing) the abstract * interface as well. * - MyFactory: Your specific factory that spills out references to * Abstract_Interface_Class that are embodied by * Abstract_Encapsulation_Class instances. * * For present code, have a look at RandomNumberDistributionFactory.def, * RandomNumberDistribution_Encapsulation.hpp and * RandomNumberDistributionFactory.hpp. * * @attention{ * One last \b warning before we begin: Your types should reside in a distinct * (and not the global) namespace, otherwise you may get strange errors such * as * \verbatim * /usr/include/boost/preprocessor/iteration/detail/local.hpp:34: error: type/value mismatch at argument 1 in template parameter list for ‘template class stub’ * /usr/include/boost/preprocessor/iteration/detail/local.hpp:34: error: expected a type, got ‘(FactoryTypeList::ListOfKnownTypes)0u’ * \endverbatim * i.e. he cannot discern between the enumerated types and the true types due. * @remarks But having your particular types in their own name space is always * a good idea. * } * * Now beginning, first remember that all we need is a list of already present * types, i.e. implemented classes. The rest is the stuff below which is all * you need (and although it looks like quite a lot, it is actually not). * * Then, do the following steps: * - create a file "RandomNumberDistributionFactory.def" containing only * preprocessor defines as follows: * @code * #ifndef RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_ * #define RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_ * * #define type_seq (uniform_smallint)(uniform_int) * #define Abstract_Interface_Class RandomNumberDistribution * #define Abstract_Encapsulation_Class RandomNumberDistribution_Encapsulation * #define type_name_space boost:: * #undef type_suffix * * #endif //RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_ * @endcode * where \a type_seq is the list of types, each element in round brackets, * \a Abstract_Interface_Class is the class name of the abstract base class * which a reference to the factory spills out, and * \a Abstract_Encapsulation_Class is the class name of the (templated) * encapsulation class of each type. Optionally, these types may reside in * some other name_space, define this one via \a type_name_space (with suffixes * double colons!). If you need something appended to each type, such as "<>" * then add in \a type_suffix. Note that the first two and the last line are * just to avoid double inclusion, also the define made therein is used for * the following step ... * - create the undefine file "RandomNumberDistributionFactory.undef" which * undefines all the previously made definitions to allow for more factories * created without the danger of the various defines getting in the way of * each other: * @code * #ifdef RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_ * * #undef type_seq * #undef type_seq_size * #undef Abstract_Interface_Class * #undef Abstract_Encapsulation_Class * #undef type_name_space * #undef type_suffix * * #undef RANDOMNUMBERDISTRIBUTIONFACTORY_DEF_ * #endif * @endcode * - Create the encapsulation of the desired types, listed in type_seq via a * templated class: * @code * #include "Creator.hpp" // the creator wrapper * #include "RandomNumberDistribution.hpp" // the abstract interface class * template * class RandomNumberDistribution_Encapsulation : * public RandomNumberDistribution, // inherit from abstract interface * public Creator< // and creator wrapper that has to know about ... * RandomNumberDistribution, // .. the abstract interface as well .. * RandomNumberDistribution_Encapsulation // .. and the * // encapsulation class we right now define * > * { * public: * // ... some interface functions here with template implementation ... * // i.e. those defined as virtual functions in the abstract interface * * RandomNumberDistribution_Encapsulation() {} // constructor * virtual ~RandomNumberDistribution_Encapsulation() {} // virtual destructor * private: * type encapsulated_type; // the instance of the type to encapsulate * }; * @endcode * - in the header of your factory put these before any declaration: * @code * // has to be appear BEFORE Factory.hpp is included! * #include "RandomNumberDistributionFactory.def" * #include "CodePatterns/FactoryTypeList.hpp" * #include "RandomNumberDistributionFactory.undef" * #include "CodePatterns/Factory.hpp" * @endcode * Then declare your factory by inheriting from Factory * @code * class RandomNumberDistributionFactory : * public Factory * { * RandomNumberDistributionFactory(); * virtual ~RandomNumberDistributionFactory(); * }; * #include "RandomNumberDistributionFactory.undef" * @endcode * where the last includes undefines the defines made in the first step and * assures that another factory can be created without any old defines * getting in the way. * @note FactoryTypeList.hpp is necessary as we can't have a forward * declaration of an enum which we need in the header of Factory. * Hence, there is an extra class the wraps the enum which we inherit. * - finally implement the factory by: * @code * #include "RandomNumberDistribution_Encapsulation.hpp" * #include "RandomNumberDistributionFactory.hpp" * // has to be included BEFORE Factory_impl.hpp! * #include "RandomNumberDistributionFactory.def" * #include "CodePatterns/Factory_impl.hpp" * * RandomNumberDistributionFactory::RandomNumberDistributionFactory() {} * RandomNumberDistributionFactory::~RandomNumberDistributionFactory() {} * * CONSTRUCT_SINGLETON(RandomNumberDistributionFactory) * CONSTRUCT_FACTORY(RandomNumberDistribution) * * #include "RandomNumberDistributionFactory.undef" * @endcode * * That's all. */ template class Factory : public FactoryTypeList { public: /** Constructor of class Factory. * */ Factory() { FillEnumTable(); FillPrototypeTable(); } /** (virtual) Destructor of class Factory. * */ virtual ~Factory() { // clear out factories map to allow boost::shared_ptr to do their work (i.e. to release mem) // this is necessary as factory is an object enums.clear(); names.clear(); for (typename InstanceTable::iterator iter = PrototypeTable.begin(); !PrototypeTable.empty(); iter = PrototypeTable.begin()) { delete (iter->second); PrototypeTable.erase(iter); } PrototypeTable.clear(); } /** Enumeration of all types known to this factory */ typedef enum FactoryTypeList::ListOfKnownTypes TypeList; /** Setter for currenttype to produce. * * @param instance_name name of type */ void setCurrentType(const std::string instance_name) { ASSERT(enums.count(instance_name) != 0, "Factory<"+toString(typeid(T).name())+">::setCurrentType() - type "+instance_name+" is not registered."); currenttype = enums[instance_name]; } /** Setter for currenttype to produce. * * @param instance_type enumeration index of type */ void setCurrentType(TypeList instance_type) { ASSERT(names.count(instance_type) != 0, "Factory<"+toString(typeid(T).name())+">::setCurrentType() - enum type "+toString(instance_type)+" is not registered."); currenttype = instance_type; } /** Getter for currenttype to produce. * * @return name of currenttype */ const std::string & getCurrentTypeName() const { return names[currenttype]; } /** Getter for currenttype to produce. * * @return enumeration index of currenttype */ TypeList getCurrentTypeEnum() const { return currenttype; } /** Getter for desired type of product. * * @param enumeration index of product * @return reference to copy of product */ T* getProduct(TypeList instance_type) const { ASSERT(names.count(instance_type) != 0, "Factory<"+toString(typeid(T).name())+">::getProduct() - enum type "+toString(instance_type)+" is not registered."); return PrototypeTable[instance_type]->create(); } /** Getter for desired type of product. * * @param name of product * @return reference to copy of product */ T* getProduct(const std::string instance_name) const { ASSERT(enums.count(instance_name) != 0, "Factory<"+toString(typeid(T).name())+">::getProduct() - type name "+instance_name+" is not registered."); return PrototypeTable[ enums[instance_name] ]->create(); } /** Getter for desired type of product. * * @param type_info object of the desired type * @return reference to copy of current type product or NULL if type mismatch */ T* getProduct(const std::type_info &instance_type_info) const { ASSERT(types.count(instance_type_info.name()) != 0, "Factory<"+toString(typeid(T).name())+">::getProduct() - type info name "+instance_type_info.name()+" is not registered."); return PrototypeTable[ types[instance_type_info.name()] ]->create(); } /** Getter for current type of product. * * @return reference to copy of current type product */ T* getProduct() const { return PrototypeTable[currenttype]->create(); } /** Getter for the name of the desired type of product. * * @return name of distribution */ const std::string &getName(TypeList instance_type) const { ASSERT(names.count(instance_type) != 0, "Factory<"+toString(typeid(T).name())+">::getName() - enum type "+toString(instance_type)+" is not registered."); return names[instance_type]; } /** Getter for the enumeration index of the desired type of product. * * @return enum of distribution */ TypeList getEnum(const std::string instance_name) const { ASSERT(enums.count(instance_name) != 0, "Factory<"+toString(typeid(T).name())+">::getEnum() - type name "+instance_name+" is not registered."); return enums[instance_name]; } protected: /** Creates instances of all possible distribution types * and stores them in \a DistributionPrototypeTable. */ void FillPrototypeTable(); /** Create association for enums to strings and vice versa * and stores them in \a distributions tables. */ void FillEnumTable(); typedef std::map< std::string, TypeList > TypeMap; typedef std::map< std::string, TypeList > EnumMap; typedef std::map< TypeList, ICreator * > InstanceTable; typedef std::map< TypeList, std::string > NameMap; static TypeList currenttype; static TypeMap types; static EnumMap enums; static InstanceTable PrototypeTable; static NameMap names; }; template typename Factory::TypeList Factory::currenttype = (typename Factory::TypeList)0; template typename Factory::TypeMap Factory::types; template typename Factory::EnumMap Factory::enums; template typename Factory::NameMap Factory::names; template typename Factory::InstanceTable Factory::PrototypeTable; /** * This define allows simple instantiation of the necessary factory functions * at a chosen place. */ #define CONSTRUCT_FACTORY(InstanceType) \ template Factory::Factory(); \ template Factory::~Factory(); \ template void Factory::setCurrentType(const std::string instance_name); \ template void Factory::setCurrentType(TypeList instance_type); \ template const std::string & Factory::getCurrentTypeName() const; \ template Factory::TypeList Factory::getCurrentTypeEnum() const; \ template InstanceType* Factory::getProduct(TypeList instance_type) const; \ template InstanceType* Factory::getProduct(const std::string instance_name) const; \ template const std::string &Factory::getName(TypeList instance_type) const; \ template Factory::TypeList Factory::getEnum(const std::string instance_name) const; #endif /* FACTORY_HPP_ */