/* Author: Marco Costalba (C) 2007 This is a simple but powerful object factory. */ #ifndef SIMPLE_FACTORY_HPP #define SIMPLE_FACTORY_HPP #include #include /* 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::register_class(KeyRef key, FnPtr = NULL); void Factory::unregister(str_ref ident); Base* Factory::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 struct FnPtr { typedef Derived* (*type)(Arg); }; template struct FnPtr { typedef Derived* (*type)(); }; /* Traits to avoid hidden copies in parameters passing */ template struct Best { typedef T& type; }; template struct Best { typedef T& type; }; template struct Best { typedef T* type; }; /* Traits to strip const and reference qualifiers */ template struct Naked { typedef T type; }; template struct Naked { typedef T type; }; template struct Naked { typedef T type; }; template struct Naked { 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 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 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 class CreatorBase : public StorableType { public: virtual Base* doCreate(typename Best::type) const = 0; }; /* 'Creator' is subclassed from 'CreatorBase' and wraps the * actual object creator method or custom function pointer. */ template class Creator : public CreatorBase { public: Creator(FnType fn) : createFn(fn) {} virtual Derived* doCreate(typename Best::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 Creator : public CreatorBase { public: Creator(FnType fn) : createFn(fn) {} virtual Derived* doCreate(typename Best::type) const { return (createFn ? createFn() : new Derived()); } FnType createFn; }; /* Some helpers that deal with 'CreatorBase' objects */ template const CreatorBase* checkType(const StorableType* p) const { // check c'tor argument type at runtime return dynamic_cast*>(p); } template const CreatorBase* find(KeyRef nm) const { std::pair r = creators.equal_range(nm); for (ConstIter it = r.first; it != r.second; ++it) { const CreatorBase* p = this->checkType((*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::instance() */ template Base* doObject(KeyRef nm, typename Best::type v) const { typedef typename Naked::type Arg; const CreatorBase* p = this->find(nm); return (p ? p->doCreate(v) : NULL); } template bool doRegister(KeyRef nm, FnType fn) { typedef typename Naked::type Arg; if (this->find(nm)) // (name, argument) pair already exsisting return false; creators.insert(std::make_pair(nm, new Creator(fn))); return true; } template bool doUnregister(KeyRef nm) { typedef typename Naked::type Arg; std::pair r = creators.equal_range(nm); for (Iter it = r.first; it != r.second; ++it) { if (this->checkType((*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 Factory : private Factory_p { 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::instance(). template doObject(nm, a); } template static bool register_class(KeyRef nm, typename FnPtr::type fn = NULL) { typedef typename FnPtr::type FnType; return Factory_p::instance(). template doRegister(nm, fn); } static bool unregister(KeyRef nm) { return Factory_p::instance(). template doUnregister(nm); } /* One c'tor argument overloads */ template static Base* object(KeyRef nm, RowArg v) { return Factory_p::instance(). template doObject(nm, v); } template static bool register_class(KeyRef nm, typename FnPtr::type fn = NULL) { typedef typename FnPtr::type FnType; return Factory_p::instance(). template doRegister(nm, fn); } template static bool unregister(KeyRef nm) { return Factory_p::instance(). template doUnregister(nm); } }; } #endif