Author: Marco Costalba (C) 2007

   This is a simple but powerful object factory.

#include <map> #include <string> /* To get an object of a given class from the factory you need first to register the class against the factory, passing a class identifier. Interface is defined by these static methods bool Factory<Base>::register_class<Derived, ArgType = void>(KeyRef key, FnPtr = NULL); void Factory<Base>::unregister(str_ref ident); Base* Factory<Base>::object(str_ref ident, ArgType ctor_arg = NULL) Where: - 'Base' is the base class, there is one factory each base class - 'Derived' is a class that inherits 'Base' - 'ArgType' is the type of the argument passed to object constructor, if not supplied then default object constructor will be called. - 'KeyRef' is a const string reference used to query the factory for objects, a common choice for 'key' value is the name of the class. - 'FnPtr' it's a pointer to an user custom function with signature Derived* (*fnPtr) (ArgType) This function, if supplied, will be used instead of the default one to actually create the object. Default one is defined as: Derived* def_ctor(ArgType ctor_arg) { return new Derived(ctor_arg); } Note that in case FnPtr is supplied and custom function requires an argument is mandatory to set the corresponding ArgType in template parameters or a compile error will result. This enforces compile time type checking. And now the implementation... */ namespace simple_factory { typedef std::string Key; // using std::string as key type typedef const Key& KeyRef; struct NoArg {}; /* Traits to deal with custom creator function signature */ template<class Derived, typename Arg> struct FnPtr { typedef Derived* (*type)(Arg); }; template<class Derived> struct FnPtr<Derived, NoArg> { typedef Derived* (*type)(); }; /* Traits to avoid hidden copies in parameters passing */ template<typename T> struct Best { typedef T& type; }; template<typename T> struct Best<T&> { typedef T& type; }; template<typename T> struct Best<T*> { typedef T* type; }; /* Traits to strip const and reference qualifiers */ template<typename T> struct Naked { typedef T type; }; template<typename T> struct Naked<const T> { typedef T type; }; template<typename T> struct Naked<T&> { typedef T type; }; template<typename T> struct Naked<const T&> { typedef T type; }; /* Not accessible private singleton class that implements the factory, it * is not possible to use it directly but user must access through 'Factory' */ template<class Base> class Factory_p { // singleton class, one each base class Factory_p() {} Factory_p(const Factory_p&); Factory_p& operator=(const Factory_p&); ~Factory_p() { for(ConstIter it = creators.begin(); it != creators.end(); ++it) delete (*it).second; } /* 'StorableType' is the common base class to store pointers * to different type of creators, dynamic_cast<> at runtime * will be used to check the (hidden) derived type */ class StorableType { public: virtual ~StorableType() {} }; typedef std::multimap<Key, const StorableType*> Container; typedef typename Container::iterator Iter; typedef typename Container::const_iterator ConstIter; Container creators; /* 'CreatorBase' is subclassed from StorableType and identifies * an unique argument type. Used by runtime argument checking. */ template<typename Arg> class CreatorBase : public StorableType { public: virtual Base* doCreate(typename Best<Arg>::type) const = 0; }; /* 'Creator' is subclassed from 'CreatorBase' and wraps the * actual object creator method or custom function pointer. */ template<class Derived, typename Arg, typename FnType> class Creator : public CreatorBase<Arg> { public: Creator(FnType fn) : createFn(fn) {} virtual Derived* doCreate(typename Best<Arg>::type v) const { return (createFn ? createFn(v) : new Derived(v)); } FnType createFn; }; /* Following the specializations of 'Creator' according to the * number of arguments. There is one specialization each c'tor * argument number. */ /* Zero arguments case */ template<class Derived, typename FnType> class Creator<Derived, NoArg, FnType> : public CreatorBase<NoArg> { public: Creator(FnType fn) : createFn(fn) {} virtual Derived* doCreate(typename Best<NoArg>::type) const { return (createFn ? createFn() : new Derived()); } FnType createFn; }; /* Some helpers that deal with 'CreatorBase' objects */ template<typename Arg> const CreatorBase<Arg>* checkType(const StorableType* p) const { // check c'tor argument type at runtime return dynamic_cast<const CreatorBase<Arg>*>(p); } template<typename Arg> const CreatorBase<Arg>* find(KeyRef nm) const { std::pair<ConstIter, ConstIter> r = creators.equal_range(nm); for (ConstIter it = r.first; it != r.second; ++it) { const CreatorBase<Arg>* p = this->checkType<Arg>((*it).second); if (p) return p; } return NULL; } protected: // only Factory can use Factory_p static Factory_p& instance() { static Factory_p p_instance; return p_instance; } public: /* These methods can be called only from Factory_p::instance(), * no problem in declaring 'public' because only Factory * can access Factory_p<Base>::instance() */ template<typename RowArg> Base* doObject(KeyRef nm, typename Best<RowArg>::type v) const { typedef typename Naked<RowArg>::type Arg; const CreatorBase<Arg>* p = this->find<Arg>(nm); return (p ? p->doCreate(v) : NULL); } template<class Derived, typename RowArg, typename FnType> bool doRegister(KeyRef nm, FnType fn) { typedef typename Naked<RowArg>::type Arg; if (this->find<Arg>(nm)) // (name, argument) pair already exsisting return false; creators.insert(std::make_pair(nm, new Creator<Derived, Arg, FnType>(fn))); return true; } template<typename RowArg> bool doUnregister(KeyRef nm) { typedef typename Naked<RowArg>::type Arg; std::pair<Iter, Iter> r = creators.equal_range(nm); for (Iter it = r.first; it != r.second; ++it) { if (this->checkType<Arg>((*it).second)) { delete (*it).second; creators.erase(it); return true; // can be only one } } return false; } }; /* Class 'Factory' is the only public interface to deal * with the factory. * * It has only static methods so simply declaring * (without defining) the constructor private seems * enough to prevent any possible instantation. * * Q: Why 'Factory' instead of using only 'Factory_p' ? * * A: Because accessing Factory_p directly has an ugly and * involved syntax, 'Factory' is much easier to use. * So because the 'official' interface is Factory, * users should not access Factory_p::instance(), * that's the reason this method is protected. Also using * some external friend template functions does not improve * the syntax for the user and also yields to have another * file for function definitions. */ template<class Base> class Factory : private Factory_p<Base> { Factory(); // prevent instantation public: /* No c'tor argument overloads */ static Base* object(KeyRef nm) { NoArg a; // seems needed, doObject() does not accept temporaries???? return Factory_p<Base>::instance(). template doObject<NoArg>(nm, a); } template<class Derived> static bool register_class(KeyRef nm, typename FnPtr<Derived, NoArg>::type fn = NULL) { typedef typename FnPtr<Derived, NoArg>::type FnType; return Factory_p<Base>::instance(). template doRegister<Derived, NoArg, FnType>(nm, fn); } static bool unregister(KeyRef nm) { return Factory_p<Base>::instance(). template doUnregister<NoArg>(nm); } /* One c'tor argument overloads */ template<typename RowArg> static Base* object(KeyRef nm, RowArg v) { return Factory_p<Base>::instance(). template doObject<RowArg>(nm, v); } template<class Derived, typename RowArg> static bool register_class(KeyRef nm, typename FnPtr<Derived, RowArg>::type fn = NULL) { typedef typename FnPtr<Derived, RowArg>::type FnType; return Factory_p<Base>::instance(). template doRegister<Derived, RowArg, FnType>(nm, fn); } template<typename RowArg> static bool unregister(KeyRef nm) { return Factory_p<Base>::instance(). template doUnregister<RowArg>(nm); } }; } #endif