Funktionspointer dynamisch generieren
-
Das Problem ist, dass hierfür ganz besonders gut Variadic Templates geeignet wären, mit denen ich aber überhaupt keine Erfahrung habe, weil der Hersteller des Compilers meines Unvertrauens es nicht für nötig erachtet hat, diese zeitig einzureichen.
Daher wendest Du Dich am besten eher an die dynamischen Freunde der Templatemagie hier!
-
Ja, das ist echt schade. Habt ihr denn vielleicht eine Idee, wie ich trotzdem an mein Ziel komme? Vielleicht sogar mit einem anderen Ansatz, der aber auch benutzerfreundlich ist?

-
Also, ich denke mir das etwa so:
#include <complex> #include <functional> #include <iostream> #include <sstream> #include <stdexcept> #include <string> #include <tuple> #include <type_traits> #include <vector> int foo(unsigned x, double y, std::complex<double> z) { std::cout << x << " - " << y << " - " << z << '\n'; return -1; } template<typename T> T from_string(std::string const &s) { T result; std::istringstream parser(s); if(parser >> result) { return result; } throw std::invalid_argument("Could not convert parameter from string."); } template<typename R, typename... Args> class string_function { public: typedef R (*function_type)(Args...); string_function(function_type func) : func_(func) { } std::string operator()(std::vector<std::string> const &str_args) { if(str_args.size() != sizeof...(Args)) { throw std::invalid_argument("Number of arguments does not match function signature."); } return std::to_string(apply_args(std::integral_constant<bool, sizeof...(Args) == 0>(), str_args)); } private: template<typename... Args2> R apply_args(std::true_type, std::vector<std::string> const &str_args, Args2&&... args2) { return func_(args2...); } template<typename... Args2> R apply_args(std::false_type, std::vector<std::string> const &str_args, Args2&&... args2) { return apply_args(std::integral_constant<bool, sizeof...(Args2) + 1 == sizeof...(Args)>(), str_args, std::forward<Args2>(args2)..., from_string<typename std::tuple_element<sizeof...(Args2), std::tuple<Args...>>::type>(str_args[sizeof...(Args2)])); } function_type func_; }; template<typename R, typename... Args> std::function<std::string(std::vector<std::string> const &)> make_string_function(R (*func)(Args...)) { return string_function<R, Args...> (func); } int main() { auto sfunc = make_string_function(foo); std::vector<std::string> args { "1", "2.34", "(5, 6)" }; std::cout << sfunc(args) << '\n'; }Für Funktionen ohne Rückgabewert müsstest du dir halt was überlegen und eine entsprechende Überladung hinzufügen; gleiches gilt, wenn du statt einfacher Funktionen auch Funktionsobjekte benutzen können willst (Lambdas beispielsweise). Der Ansatz sollte sich entsprechend erweitern lassen, ggf. stellen sich aber Designfragen (ob die Funktionsobjekte kopier- bzw. movebar sein müssen, wem was gehört etc.). Ist halt Arbeit, sollte aber gehen.
Ohne variadische Templates geht das mit Boost.Preprocessor und viel, viel Gefummel bestimmt auch irgendwie, aber da hab ich wirklich keine Lust drauf.
-
Vielen Dank, seldon. Ich möchte mich bei dir an dieser Stelle nochmal ganz herzlichst bedanken. Insbesondere auch deshalb, weil ich weiß, dass es viele Leute gibt, die sich nach dem Posting eines Vorschlags gar nicht mehr melden.
Vielen lieben Dank!
Ich habe den Code noch nicht ausprobieren können (Visual Studio 2010), aber er sieht sehr gut aus und wenn man es etwas auf sich wirken lässt, dann kann man das Konzept dahinter auch verstehen. Darauf kann ich also gut aufbauen.
Vielen Dank!

-
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
voidals Rückgabetyp? Das sollte nicht allzu schwierig werden. Es ist sogar möglich,void-Ausdrücke mitreturnzurü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
voidals Rückgabetyp? Das sollte nicht allzu schwierig werden. Es ist sogar möglich,void-Ausdrücke mitreturnzurü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
voidals Rückgabetyp? Das sollte nicht allzu schwierig werden. Es ist sogar möglich,void-Ausdrücke mitreturnzurü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...