Funktionspointer dynamisch generieren



  • Funcyd schrieb:

    ...Ich habe den Code noch nicht ausprobieren können (Visual Studio 2010), aber er sieht sehr gut aus...

    Das kannst du vergessen, selbst der aktuelle MSVC (2012) kann noch keine variadischen Templates, die seldon hier benutzt hat.



  • Skym0sh0 schrieb:

    Das kannst du vergessen, selbst der aktuelle MSVC (2012) kann noch keine variadischen Templates, die seldon hier benutzt hat.

    Die November 2012 CTP kanns und spätestens mit dem Update 2 wirds wohl auch production ready sein... 😉



  • Die CTP ist "instabil", bringt keine neue STL mit und ist nicht reif für den produktiven Einsatz.
    @dot, hast Du eine Ahnung, für wann update2 anberaumt ist? Auf den MS-Blogs ist einfach nur Funkstille, ich warte sehnsüchtigst -.-



  • Also man kann das ganze auch umsetzen, indem man das Template spezifisch für alle Anzahlen (nicht typen) von Argumenten, die man unterstützen möchte, überlädt.
    Ist ziemlich viel Cut&Paste-Arbeit, aber ich halte das immernoch für übersichtlicher als da mit dem Präprozessor rumzuspielen, selbst wenn die MS STL das so macht und sich die Boost-Jünger auch darauf eingeschossen haben. Aber als Lib-Entwickler muss man eben etwas flexibles liefern, sehe ich schon ein.



  • Decimad schrieb:

    Also man kann das ganze auch umsetzen, indem man das Template spezifisch für alle Anzahlen (nicht typen) von Argumenten, die man unterstützen möchte, überlädt.
    Ist ziemlich viel Cut&Paste-Arbeit, aber ich halte das immernoch für übersichtlicher als da mit dem Präprozessor rumzuspielen, selbst wenn die MS STL das so macht und sich die Boost-Jünger auch darauf eingeschossen haben. Aber als Lib-Entwickler muss man eben etwas flexibles liefern, sehe ich schon ein.

    Ja, das klingt ganz gut. Ich hab nur ein Problem mit Templates. Wie kann ich eine Klasse realisieren, die eine unterschiedliche Anzahl an Template Argumenten entgegennimmt?

    template <typename R, typename Arg1>
    class FuncPtr
    {
    	typedef R (*func_ptr)(Arg1);
    
    	func_ptr myPtr;
    
    public:
    	FuncPtr(func_ptr ptr) : myPtr(ptr) { }
    
    	std::string operator()(const StrVec& v)
    	{
    		return myPtr(FromString<Arg1>(v[0]));
    	}
    };
    
    template <typename R, typename Arg1>
    std::function<std::string(std::vector<std::string> const &)> FormFuncPtr(R (*func)(Arg1))
    {
    	return FuncPtr<R, Arg1>(func);
    }
    
    template <typename R, typename Arg1, typename Arg2>
    class FuncPtr
    {
    	typedef R (*func_ptr)(Arg1, Arg2);
    
    	func_ptr myPtr;
    
    public:
    	FuncPtr(func_ptr ptr) : myPtr(ptr) { }
    
    	template <typename R, typename Arg1, typename Arg2>
    	std::string operator()(const StrVec& v)
    	{
    		return myPtr(FromString<Arg1>(v[0]), FromString<Arg2>(v[0]));
    	}
    };
    
    template <typename R, typename Arg1, typename Arg2>
    std::function<std::string(std::vector<std::string> const &)> FormFuncPtr(R (*func)(Arg1, Arg2))
    {
    	return FuncPtr<R, Arg1, Arg2>(func);
    }
    

    Folgender Code lässt sich nicht kompilieren, da FuncPtr zu viele Template Argumente hat. Wie könnte man das noch lösen?



  • Wenn ich jeweils eine Variante auskommentiere, dann compiliert es und funktioniert perfekt. Beide zusammen gehen aber nicht, was aber notwendig ist, um unterschiedliche Parameteranzahlen zu unterstützen.



  • Variadic Templates:
    Du nimmst eine mit ihnen und spezialisierst dann für die anderen Varianten.

    Edit: Oh, genau die wolltest du ja vermeiden. Mein Fehler... 😃



  • Anders:

    template< typename T >
    class ScriptCallback;
    
    template< typename Ret >
    class ScriptCallback< Ret() >
    {
       //...
    };
    
    template< typename Ret, typename Arg1 >
    class ScriptCallback< Ret(Arg1) >
    {
       //...
    };
    
    // usw. usf.
    


  • Super, das ist viel besser, danke! Könntest du mir noch sagen, was dieses Ret(Arg1) genau bewirkt bzw. besser macht? 🙂



  • Naja, Ret(Arg1) ist der Typ einer Funktion, die Arg1 nimmt unt Ret zurückgibt. Auf diese Art und Weise bekommt man die ganze Signatur in ein Template-Argument und spezialisiert das Template dann für die "Anzahl der Argumente" des einen Template-Arguments.
    Dein fehlerhaftes Beispiel zeigte ja deutlich auf, dass jede Spezialisierung eines Templates die gleiche Anzahl von Template-Argumenten benötigt.

    So hat man jetzt nicht:

    // achtung fehlerhaft
    template< typename Ret, typename Arg1 > struct templ {};
    template< typename Ret, typename Arg1, typename Arg2 > struct templ {};
    templ< int, float > some;
    templ< int, float, double > some2;
    

    sondern

    template< typename T >
    struct templ; // keine default implementierung
    
    template< typename Ret, typename Arg1 >
    struct templ< Ret(Arg1) > struct templ {}; // << immernoch ein Template-Argument
    
    template< typename Ret, typename Arg1, typename Arg2 >
    struct templ< Ret(Arg1,Arg2) > struct templ {}; // << ein Template-Argumenr!
    
    templ<int(float)> some;   // << 1 -> TEMPLATE <- Argument
    templ<int(float,double)> some2; // << 1 -> TEMPLATE <- Argument
    


  • Alles klar, das macht natürlich so Sinn 🙂

    Jetzt muss ich nur noch schauen, wie ich Funktionen ohne Rückgabewert unterstütze. Da tue ich momentan leider etwas schwer. Na ja, ich probiere mal noch etwas, ansonsten brauche ich wohl ein letztes Mal hilfe. 🙂



  • Funcyd schrieb:

    Jetzt muss ich nur noch schauen, wie ich Funktionen ohne Rückgabewert unterstütze.

    Du meinst void als Rückgabetyp? Das sollte nicht allzu schwierig werden. Es ist sogar möglich, void -Ausdrücke mit return zurückzugeben. Kann bei generischem Code praktisch sein.



  • Nexus schrieb:

    Funcyd schrieb:

    Jetzt muss ich nur noch schauen, wie ich Funktionen ohne Rückgabewert unterstütze.

    Du meinst void als Rückgabetyp? Das sollte nicht allzu schwierig werden. Es ist sogar möglich, void -Ausdrücke mit return zurückzugeben. Kann bei generischem Code praktisch sein.

    Und vor allem, um Leute zu verwirren. scnr.

    return throw 0;
    


  • Nexus schrieb:

    Funcyd schrieb:

    Jetzt muss ich nur noch schauen, wie ich Funktionen ohne Rückgabewert unterstütze.

    Du meinst void als Rückgabetyp? Das sollte nicht allzu schwierig werden. Es ist sogar möglich, void -Ausdrücke mit return zurückzugeben. Kann bei generischem Code praktisch sein.

    Ja, genau. So das man eine Callback-Funktion nutzen kann, die nicht std::string, sondern void "zurückgibt".

    Mein aktueller Code sieht so aus. Es klappt auch alles, aber das mit dem void kriege ich nicht auf die Reihe. Mit Templates/Metaprogrammierung hatte ich bisher noch keinen großen Kontakt.

    #include <iostream>
    #include <sstream>
    #include <string>
    #include <functional>
    #include <vector>
    
    typedef std::vector<std::string> StrVec;
    
    template< typename T >
    T FromString(const std::string& s)
    {
    	T res;
    
    	std::stringstream strm(s);
    
    	strm >> res;
    
    	return res;
    }
    
    template< typename T >
    class ScriptCallback;
    
    template< typename R >
    class ScriptCallback< R() >
    {
    	typedef R (*callback)();
    
    	callback callback_;
    
    public:
    	ScriptCallback(callback callback) : callback_(callback) { }
    
    	std::string operator()(const StrVec& v) const
    	{
    		return callback_();
    	}
    };
    
    template < typename R >
    std::function<std::string(const StrVec& v)> MakeCallback(R (*func)())
    {
        return ScriptCallback<R()>(func);
    }
    
    template< typename R, typename A1 >
    class ScriptCallback< R(A1) >
    {
    	typedef R (*callback)(A1);
    
    	callback callback_;
    
    public:
    	ScriptCallback(callback callback) : callback_(callback) { }
    
    	std::string ScriptCallback::operator()(const StrVec& v) const
    	{
    		return callback_(FromString<A1>(v[0]));
    	}
    };
    
    template < typename R, typename A1 >
    std::function<std::string(const StrVec& v)> MakeCallback(R (*func)(A1))
    {
        return ScriptCallback<R(A1)>(func);
    }
    
    std::string Test()
    {
    	std::cout << "!";
    
    	return "Test";
    }
    
    std::string Test1(int m)
    {
    	std::cout << m + 1000;
    
    	return "Test1";
    }
    
    void Empty(int x)
    {
    	std::cout << x;
    }
    
    int main()
    {
    	StrVec v;
    
    	v.push_back("500");
    
    	auto cb = MakeCallback(Test1);
    
    	cb(v);
    }
    

    Man muss die Zeile:

    auto cb = MakeCallback(Test1);
    

    in

    auto cb = MakeCallback(Empty);
    

    ändern, um eine beispielhafte void-Funktion zu nutzen.

    Es lässt sich dann natürlich nicht mehr kompilieren, weil operator() und MakeCallback() natürlich mit return versucht den nicht vorhandenen Rückgabewert zurückzugeben...



  • Dann mache doch einfach überall einen Rückgabewert, die eigentlichen voids returnen bool.



  • Funcyd schrieb:

    Es lässt sich dann natürlich nicht mehr kompilieren, weil operator() und MakeCallback() natürlich mit return versucht den nicht vorhandenen Rückgabewert zurückzugeben...

    Was genau kompiliert nicht? Wie schon angetönt, sollte das Zurückgeben von void -Ausdrücken unproblematisch sein.

    Nathan schrieb:

    Dann mache doch einfach überall einen Rückgabewert, die eigentlichen voids returnen bool.

    Ne, das verbirgt nur Fehler. Eine void -Funktion hat nichts zurückzugeben...



  • Nexus schrieb:

    Funcyd schrieb:

    Es lässt sich dann natürlich nicht mehr kompilieren, weil operator() und MakeCallback() natürlich mit return versucht den nicht vorhandenen Rückgabewert zurückzugeben...

    Was genau kompiliert nicht? Wie schon angetönt, sollte das Zurückgeben von void -Ausdrücken unproblematisch sein.

    Nathan schrieb:

    Dann mache doch einfach überall einen Rückgabewert, die eigentlichen voids returnen bool.

    Ne, das verbirgt nur Fehler. Eine void -Funktion hat nichts zurückzugeben...

    Der Code so wie er da steht kompiliert. Er kompiliert nur dann nicht, wenn man MakeCallback(Empty); nutzt, also eine Funktion ohne Rückgabewert.

    Was du mit der Rückgabe von void-Ausdrücken meinst, weiß ich nicht so genau. Ein Test mit return void; liefert bei mir zumindest Fehler.



  • Funcyd schrieb:

    Was du mit der Rückgabe von void-Ausdrücken meinst, weiß ich nicht so genau. Ein Test mit return void; liefert bei mir zumindest Fehler.

    void ist ja auch kein Ausdruck, sondern ein Typ.

    Was ich meine:

    void a();
    
    void b()
    {
        return a();
    }
    


  • Nexus schrieb:

    Funcyd schrieb:

    Was du mit der Rückgabe von void-Ausdrücken meinst, weiß ich nicht so genau. Ein Test mit return void; liefert bei mir zumindest Fehler.

    void ist ja auch kein Ausdruck, sondern ein Typ.

    Was ich meine:

    void a();
    
    void b()
    {
        return a();
    }
    

    Wow, das ist sehr interessant und ein echt sehr nützliches Feature. Statt des fixen std::string hab ich nun alle Rückgabewerte durch den Template-Parameter R ersetzt. Funktioniert.

    Hatte das nie ausprobiert, da ich das für schwachsinnig hielt. Aber mit dem neuen Wissen, war die Lösung in weniger als 20 Sekunden gefunden.

    Danke. 👍



  • Okay, da du immer einen string zurückgeben willst und wenn der leere String sozusagen "void" repräsentieren soll... hrmmm, ich könnte mir da gerade nur folgendermaßen behelfen:

    template< typename T, typename S >
    struct call_and_return
    {
    	static std::string exec( S s ) {
    		return s();
    	}
    };
    
    template< typename S >
    struct call_and_return<void,S>
    {
    	static std::string exec( S s ) {
    		s();
    		return std::string();
    	}
    };
    
    template< typename T >
    struct some;
    
    template< typename Ret >
    struct some<Ret()> {
    	std::string make() { 
    		return call_and_return<Ret,decltype(func)&>::exec( func );
    	}
    
    	std::function<Ret()> func;
    };
    
    some<void()> bla;
    some<std::string()> blubb;
    
    std::string stra = blubb.make();
    std::string strb = bla.make();
    

    Edit: Na offenbar war es doch anders gedacht 😉


Anmelden zum Antworten