Generic Lambda factory delegates



  • Aktuell versuche ich auf Basis von C++11 eine generische Lambda Delegate Factory
    umzusetzen, die es mir erlaubt Objekte ohne und mit verschiedenen Konstruktor
    Argumenten zu erzeugen. Dabei habe ich mich an folgendem Artikel orientiert:
    http://www.cprogramming.com/c++11/c++11-lambda-closures.html

    Den Artikel habe ich soweit verstanden. Und ich weiß, dass ich z.B. ein konkretes
    Objekt mit verschiedenen Delegates erzeugen kann:

    class ITest
    {
    public:
    	ITest(const std::string& text){
    		m_text = text;
    	}
    
    	virtual ~ITest(){}
    
    	virtual void saySomething(void) = 0;
    
    protected:
    	std::string m_text = "undefined";
    };
    
    class Test : virtual public ITest
    {
    public:
    	Test():ITest("Hi there!"){}
    	Test(const std::string& text)
    	:ITest(text){}
    
    	Test(const std::string& text, const std::string& name)
    	:ITest(text), m_name(name){
    		m_text = text;
    	}
    
    	virtual void saySomething(void){
    
    		std::string say; 
    		if(m_name.compare("undefined")==0){
    			say = "I've to say: ";
    		}
    		else if(m_name.compare("undefined")!=0){
    			say = m_name + " has to say: ";
    		}
    
    		std::cout << say << m_text << std::endl;
    	}
    
    private:
    	std::string m_name = "undefined";
    };
    
    int main()
    {
    	auto delegate = [&]() -> Test * {return new Test();};
    	auto test = delegate();
    	test->saySomething();
    
    	auto delegate1 = [&](const std::string& text) -> Test * {return new Test(text);};
    	auto test1 = delegate1("I'm created by delegate");
    	test1->saySomething();
    
    	auto delegate2 = [&](const std::string& text, const std::string& name) -> Test * {return new Test(text, name);};
    	auto test2 = delegate2("I'm also created by delegate", "Tom");
    	test2->saySomething();
    	exit(0)
    }
    

    Und ich erhalte folgenden Output:

    $ ./bin/delegates.exe
    I've to say: Hi there!
    I've to say: I'm created by delegate
    Tom has to say: I'm also created by delegate

    Soweit prima. Doch nun möchte ich das ganze in eine generische Factory verpacken, bei der ich den
    jeweiligen konkreten Delegate zuvor registrieren kann um dann bei Bedarf mit 0...n Argumenten das
    Objekt dynamisch zur Laufzeit mit Laufzeitabhängigen Werten zu füllen.

    Nur wie bekomme ich das hin? Ich habe gedacht ich nutze Variadic Templates und die std::function
    um dies zu realisieren. Hier mein erster, grober Entwurf:

    template<class T, typename ... Args>
    class DelegateFactory
    {
    public:
    	std::shared_ptr<T> create(Args&& ... args) const {
    		return std::shared_ptr<T>(static_cast<T*>(m_delegate(std::forward<Args>(args)...)));
    	}
    
    	void registerDelegate(std::function<T* (Args&& ... args)> delegate)
    	{
    		 m_delegate = delegate;
    	}	
    
    private:	
    	std::function<T* (Args&& ... args)> m_delegate = nullptr;
    };
    
    int main()
    {
    	// dies hier klappt noch ...
    	auto fac = DelegateFactory<Test>();
    	fac.registerDelegate(delegate);
    	auto test3 = fac.create();
    	test3->saySomething();
    
    	// Und dieser nicht mehr
    	auto fac1 = DelegateFactory<Test>();
    	fac1.registerDelegate(delegate1);
    	auto test4 = fac.create("I'm created by generic factory delegate");
    	test4->saySomething();
    	exit(0)
    }
    

    Hier einmal die Fehlermeldung:

    d:/tmp/test/samples/ioc/src/signals.cpp: In function 'int main()':
    d:/tmp/test/samples/ioc/src/signals.cpp:210:34: error: no matching function for call to 'DelegateFactory<Test>::registerDelegate(main()::<lambda(const string&)>&)'
    fac1.registerDelegate(delegate1);
    ^
    d:/tmp/test/samples/ioc/src/signals.cpp:210:34: note: candidate is:
    d:/tmp/test/samples/ioc/src/signals.cpp:153:7: note: void DelegateFactory<T, Args>::registerDelegate(std::function<T*(Args&& ...)>) [with T = Test; Args = {}]
    void registerDelegate(std::function<T* (Args&& ... args)> delegate)
    ^
    d:/tmp/test/samples/ioc/src/signals.cpp:153:7: note: no known conversion for argument 1 from 'main()::<lambda(const string&)>' to 'std::function<Test*()>'
    d:/tmp/test/samples/ioc/src/signals.cpp:211:68: error: no matching function for call to 'DelegateFactory<Test>::create(const char [40])'
    auto test4 = fac.create("I'm created by generic factory delegate");
    ^
    d:/tmp/test/samples/ioc/src/signals.cpp:211:68: note: candidate is:
    d:/tmp/test/samples/ioc/src/signals.cpp:149:21: note: std::shared_ptr<_Tp1> DelegateFactory<T, Args>::create(Args&& ...) const [with T = Test; Args = {}]
    std::shared_ptr<T> create(Args&& ... args) const {
    ^
    d:/tmp/test/samples/ioc/src/signals.cpp:149:21: note: candidate expects 0 arguments, 1 provided

    Ist mein Ansatz überhaupt möglich? Wenn ja wie? Mein Ziel ist es, möglichst einfach Delegates
    zu definieren und generisch zu nutzen, da ich zur Laufzeit erst einen Zugriff auf die Parameter
    für den Konstruktor habe, die in Typ und Anzahl variieren können.



  • Snoob schrieb:

    eine generische Lambda Delegate Factory

    da geht noch mehr. Echte Profis verwenden generische Lambda Delegate Factory Provider Service Adapter Facaden um die grenzenlosen Synergieeffekte der Objektorientierung vollständig zu nutzen.

    auto fac1 = DelegateFactory<Test, std::string>(); 
    auto test4 = fac1.create("I'm created by generic factory delegate");
    


  • Danke für den Hinweis. Ok, für den Funktkionspointer muss ich explizit die Argumente mit angeben. D.h. ich brauche je nach Konstruktor Ausprägung exakt eine DelegatFactory ...

    Echte Profis verwenden generische Lambda Delegate Factory Provider Service Adapter Facaden

    Klingt interessant. Hast Du evtl. ein Link wo dazu mehr Infos bekomme, bzw. wo ich mir das einmal exemplarisch, gern auch als Pseudo-Code verinnerlichen kann?



  • Snoob schrieb:

    Danke für den Hinweis. Ok, für den Funktkionspointer muss ich explizit die Argumente mit angeben. D.h. ich brauche je nach Konstruktor Ausprägung exakt eine DelegatFactory ...

    naja, wenn man mal das ganze delegate factory Zeug ignoriert, steht da einfach ein Klassentemplate:

    template<class T, typename ... Args>
    class DelegateFactory 
    // ....
    // much later:
    DelegateFactory<Test> // jetzt steht der Typ fest und Args... ist leer
    

    Snoob schrieb:

    Echte Profis verwenden generische Lambda Delegate Factory Provider Service Adapter Facaden

    Klingt interessant. Hast Du evtl. ein Link wo dazu mehr Infos bekomme, bzw. wo ich mir das einmal exemplarisch, gern auch als Pseudo-Code verinnerlichen kann?

    not sure if well played or broken irony detector



  • DelegateFactory<Test> // jetzt steht der Typ fest und Args... ist leer
    

    Das meinte ich ja, bei der Konkreten Definition der Template Ausprägung muss ich mit angeben ob ich wie oben keine Args habe, oder entsprechend genau die Ausprägung.

    D.h. um alle 3 Konstruktoren von Test bedienen zu können, benötige ich exakt 3 Definitionen:

    DelegateFactory<Test>()  //Ohne Argumente
    DelegateFactory<Test, const std::string&>()  //Ein Konstruktor Argument
    DelegateFactory<Test, const std::string&, const std::string&>(); // //Zwei Argumente
    

    Oder meintest Du was anderes ?

    not sure if well played or broken irony detector

    😃



  • Nachdem die Factory nun funktioniert, stellt sich mir die Frage, wie bekomme ich die in einen STL Container?

    Normalerweise müsste ich ein Interface für die Factory definieren, das kein Klassentemplate ist. Nur wie bekomme ich in dieses Interface eine generische, virtuelle Methode zum Aufruf des Lambda Delegators hin?

    class IDelegateFactory
    {
    public:
    	IDelegateFactory(){}
    	virtual ~IDelegateFactory(){}
    
    	template <class T, typename ... Args>
    	std::shared_ptr<T> create(Args&& ... args) const {
                //Wie bekomme ich den Aufruf an den Delegator in der konkreten Factory hin?
                return this->delegate(args);
    	}
    
    private:
    	virtual std::shared_ptr<void> delegate(????) = 0;
    };
    

    Den Das Interface hat ja keinen Zugriff auf den Delegator der als std::function in der konkreten Klasse definiert ist.


Log in to reply