Funktionspointer dynamisch generieren



  • 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