Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If I understand this correctly, we want a factory function that can select which derived class to instantiate based on constructor inputs. This is the most generic solution that I could come up with so far. You specify mapping inputs to organize factory functions, and then you can specify constructor inputs upon factory invocation. I hate to say that the code explains more than I could in words, however I think the example implementations of <code>FactoryGen.h</code> in <code>Base.h</code> and <code>Derived.h</code> are clear enough with the help of comments. I can provide more details if necessary.</p> <p>FactoryGen.h</p> <pre><code>#pragma once #include &lt;map&gt; #include &lt;tuple&gt; #include &lt;typeinfo&gt; //C++11 typename aliasing, doesn't work in visual studio though... /* template&lt;typename Base&gt; using FactoryGen&lt;Base&gt; = FactoryGen&lt;Base,void&gt;; */ //Assign unique ids to all classes within this map. Better than typeid(class).hash_code() since there is no computation during run-time. size_t __CLASS_UID = 0; template&lt;typename T&gt; inline size_t __GET_CLASS_UID(){ static const size_t id = __CLASS_UID++; return id; } //These are the common code snippets from the factories and their specializations. template&lt;typename Base&gt; struct FactoryGenCommon{ typedef std::pair&lt;void*,size_t&gt; Factory; //A factory is a function pointer and its unique type identifier //Generates the function pointer type so that I don't have stupid looking typedefs everywhere template&lt;typename... InArgs&gt; struct FPInfo{ //stands for "Function Pointer Information" typedef Base* (*Type)(InArgs...); }; //Check to see if a Factory is not null and matches it's signature (helps make sure a factory actually takes the specified inputs) template&lt;typename... InArgs&gt; static bool isValid(const Factory&amp; factory){ auto maker = factory.first; if(maker==nullptr) return false; //we have to check if the Factory will take those inArgs auto type = factory.second; auto intype = __GET_CLASS_UID&lt;FPInfo&lt;InArgs...&gt;&gt;(); if(intype != type) return false; return true; } }; //template inputs are the Base type for which the factory returns, and the Args... that will determine how the function pointers are indexed. template&lt;typename Base, typename... Args&gt; struct FactoryGen : FactoryGenCommon&lt;Base&gt;{ typedef std::tuple&lt;Args...&gt; Tuple; typedef std::map&lt;Tuple,Factory&gt; Map; //the Args... are keys to a map of function pointers inline static Map&amp; get(){ static Map factoryMap; return factoryMap; } template&lt;typename... InArgs&gt; static void add(void* factory, const Args&amp;... args){ Tuple selTuple = std::make_tuple(args...); //selTuple means Selecting Tuple. This Tuple is the key to the map that gives us a function pointer get()[selTuple] = Factory(factory,__GET_CLASS_UID&lt;FPInfo&lt;InArgs...&gt;&gt;()); } template&lt;typename... InArgs&gt; static Base* make(const Args&amp;... args, const InArgs&amp;... inArgs){ Factory factory = get()[std::make_tuple(args...)]; if(!isValid&lt;InArgs...&gt;(factory)) return nullptr; return ((FPInfo&lt;InArgs...&gt;::Type)factory.first) (inArgs...); } }; //Specialize for factories with no selection mapping template&lt;typename Base&gt; struct FactoryGen&lt;Base,void&gt; : FactoryGenCommon&lt;Base&gt;{ inline static Factory&amp; get(){ static Factory factory; return factory; } template&lt;typename... InArgs&gt; static void add(void* factory){ get() = Factory(factory,__GET_CLASS_UID&lt;FPInfo&lt;InArgs...&gt;&gt;()); } template&lt;typename... InArgs&gt; static Base* make(const InArgs&amp;... inArgs){ Factory factory = get(); if(!isValid&lt;InArgs...&gt;(factory)) return nullptr; return ((FPInfo&lt;InArgs...&gt;::Type)factory.first) (inArgs...); } }; //this calls the function "initialize()" function to register each class ONCE with the respective factory (even if a class tries to initialize multiple times) //this step can probably be circumvented, but I'm not totally sure how template &lt;class T&gt; class RegisterInit { int&amp; count(void) { static int x = 0; return x; } //counts the number of callers per derived public: RegisterInit(void) { if ((count())++ == 0) { //only initialize on the first caller of that class T T::initialize(); } } }; </code></pre> <p>Base.h</p> <pre><code>#pragma once #include &lt;map&gt; #include &lt;string&gt; #include &lt;iostream&gt; #include "Procedure.h" #include "FactoryGen.h" class Base { public: static Base* makeBase(){ return new Base; } static void initialize(){ FactoryGen&lt;Base,void&gt;::add(Base::makeBase); } //we want this to be the default mapping, specify that it takes void inputs virtual void speak(){ std::cout &lt;&lt; "Base" &lt;&lt; std::endl; } }; RegisterInit&lt;Base&gt; __Base; //calls initialize for Base </code></pre> <p>Derived.h</p> <pre><code>#pragma once #include "Base.h" class Derived0 : public Base { private: std::string speakStr; public: Derived0(std::string sayThis){ speakStr=sayThis; } static Base* make(std::string sayThis){ return new Derived0(sayThis); } static void initialize(){ FactoryGen&lt;Base,int&gt;::add&lt;std::string&gt;(Derived0::make,0); } //we map to this subclass via int with 0, but specify that it takes a string input virtual void speak(){ std::cout &lt;&lt; speakStr &lt;&lt; std::endl; } }; RegisterInit&lt;Derived0&gt; __d0init; //calls initialize() for Derived0 class Derived1 : public Base { private: std::string speakStr; public: Derived1(std::string sayThis){ speakStr=sayThis; } static Base* make(std::string sayThat){ return new Derived0(sayThat); } static void initialize(){ FactoryGen&lt;Base,int&gt;::add&lt;std::string&gt;(Derived0::make,1); } //we map to this subclass via int with 1, but specify that it takes a string input virtual void speak(){ std::cout &lt;&lt; speakStr &lt;&lt; std::endl; } }; RegisterInit&lt;Derived1&gt; __d1init; //calls initialize() for Derived1 </code></pre> <p>Main.cpp</p> <pre><code>#include &lt;windows.h&gt; //for Sleep() #include "Base.h" #include "Derived.h" using namespace std; int main(){ Base* b = FactoryGen&lt;Base,void&gt;::make(); //no mapping, no inputs Base* d0 = FactoryGen&lt;Base,int&gt;::make&lt;string&gt;(0,"Derived0"); //int mapping, string input Base* d1 = FactoryGen&lt;Base,int&gt;::make&lt;string&gt;(1,"I am Derived1"); //int mapping, string input b-&gt;speak(); d0-&gt;speak(); d1-&gt;speak(); cout &lt;&lt; "Size of Base: " &lt;&lt; sizeof(Base) &lt;&lt; endl; cout &lt;&lt; "Size of Derived0: " &lt;&lt; sizeof(Derived0) &lt;&lt; endl; Sleep(3000); //Windows &amp; Visual Studio, sry } </code></pre> <p>I think this is a pretty flexible/extensible factory library. While the code for it is not very intuitive, I think using it is fairly simple. Of course, my view is biased seeing as I'm the one that wrote it, so please let me know if it is the contrary.</p> <p><strong>EDIT :</strong> Cleaned up the FactoryGen.h file. This is probably my last update, however this has been a fun exercise.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload