Portieren von Delegates aus C# nach C++ HowTo
-
Hallo ihr,
ich habe gerade einen C# Kurs gehabt. Programmiere aber beruflich eigentlich immer C++. Während des Kurses habe ich delegates kennengelernt und habe da ziemlich einfach folgendes programmiert.
class Program { public delegate double function(double x); // defieniere Delegate public static double eval(function f, double x) // auswerte Funktion die eine function als Parameter bekommt { return f(x); // gibt function an Stelle x zurück } public static double myFunction(double x) // eigene Implementierung einer Funktion die auf das Delegate(Schablone) passt { return x; } static void Main(string[] args) { function f; // delegate f = new function(Math.Cos); // versehe delegate mit function Console.WriteLine(eval(Math.Cos, 0)); // rufe die function in eval direkt auf Console.WriteLine(eval(f, 0)); // oder das damit versehene delegate f = new function(myFunction); // mit selbst geschriebener Funktion Console.WriteLine(eval(myFunction, 3)); Console.WriteLine(eval(f, 3)); Console.ReadKey(); } }jetzt meine Frage:
ich würde sowas gerne in C++ machen. Ich habe dazu auch das Stroustrup buch bemüht und bin da auf das Kapitel Funktionszeiger gestoßen.
dort steht dann zum Beispiel sowas:#include "stdafx.h" #include "Punkt.h"; #include <iostream> #include <string> #include "math.h" using namespace std; void fehler(string s) // Methode die einen string auf den Bildschirm schreibt { cout << s << endl; } void (*efct)(string); // Funktionszeiger mit derselben Funktion int _tmain(int argc, _TCHAR* argv[]) { efct = &fehler; // gebe dem Funktionszeiger die Adresse von fehler efct("Hallo"); // rufen den Funktionszeiger mit "Hallo" auf return 0; }Das ist auch schön das das klappt nur würde ich ja gerne eine Methode mit variablen Funktionen wie oben in C# gemacht aufrufen.
Also auch das es eine eval Methode gibt die ich mit jeder beliebigen Funktion aufrufen kann (Kreis, Gerade, Polynom gerad n etc.) und diese an der Stelle x auswerten kann. Könnt ihr mir vielleicht Schlagwörter schicken oder einfach Programmiertipps geben oder eine Anleitung. Programmieren würd ichs schon selbst.
Ich danke schonmalliebe Grüße
matse
-
Du solltest mal einen Blick auf die boost Bibliotheken werfen, da gibt es z.B. boost::function, das sowas erschlagen könnte.
-
Oder auch std::tr1::function<..> / std::tr1::bind(..)
-
Hallo,
hier mal ein kleines Beispiel für dich mit tr1-Mitteln, welche im neuen Standard dann einfach im std-namespcae zu finden sind unter functional.
Aber bis es soweit ist:#include <iostream> #include <tr1/functional> std::tr1::function<double(double)> delegate; //als templateparameter wird der funktionstyp angegeben, hier eine funktion, die ein double zurückgibt und einen double erwartet double myFunction(double x) { return x; } int main() { delegate MeineFunktion = &myFunction; std::cout << MeineFunktion(3.14) << std::endl; }Soweit einmal runtergetippt aus dem Kopf, sollte so aber funktionieren.
Brauchst du noch ein Beispiel, um Klassenmethoden zu binden, sag einfach Bescheid.Gruß Gate
-
In den Beispielen des OPs werden nur freie Funktionen oder statische Elementfunktionen benutzt. Hier reichen Funktionszeiger sogar aus. In C# kann man über "Delegates" auch nicht-statische Elementfunktionen ("Objekt-Methoden") aufrufen. Das ist sogar der eigentliche Witz an diesen Dingern. Das und noch mehr bekommst Du mit boost/std::tr1 function und boost/std::tr1 bind.
Wenn Du Dich auf freie Funktionen und Elementfunktionen beschränken willst -- also so, wie es bei C# ist, ohne "Funktoren" -- dann kann man das auch mit etwas weniger Overhead erledigen. Man kommt ohne dynamischen Speicher aus und kann dadurch, dass Funktionszeiger als Template-Parameter benutzt werden, Inlining ausnutzen. Das sollte dann mit einem guten Compiler recht flott laufen.
Folgendes kompiliert bei mir mit
g++ -std=c++0x -Wall delegate.cpp(Ich nutze hier die experimentellen C++0x Erweiterungen des GCCs, um beliebig viele Funktionsargumente unterstützen zu können.)
#include <stdexcept> #include <utility> template<class Ret, class...Args> struct wrapper { template<Ret(*FunPtr)(Args...)> struct free_fun { static Ret invoke(void*, Args... args) { return FunPtr(std::forward<Args>(args)...); } }; template<class C, Ret (C::*MemFun)(Args...)> struct mem_fun_nonconst { static Ret invoke(void* obj, Args... args) { return (static_cast<C*>(obj)->*MemFun)(std::forward<Args>(args)...); } }; template<class C, Ret (C::*MemFun)(Args...) const> struct mem_fun_const { static Ret invoke(void* obj, Args... args) { return (static_cast<C const*>(obj)->*MemFun)(std::forward<Args>(args)...); } }; }; class bad_function_call : public std::logic_error { public: bad_function_call() : logic_error("bad function call") {} }; template<typename FunSig> class delegate; template<class Ret, class... Args> class delegate<Ret(Args...)> { Ret (*fun)(void*, Args...); void *data; static Ret unbound(void*, Args...) { throw bad_function_call(); } public: delegate() : fun(&unbound), data(0) {} Ret operator()(Args... args) const { return fun(data,std::forward<Args>(args)...); } template<Ret(*FunPtr)(Args...)> void init() { typedef typename wrapper<Ret,Args...>::template free_fun<FunPtr> w; fun = &w::invoke; data = 0; } template<class C, Ret(C::*MemFun)(Args...)> void init(C* obj) { typedef typename wrapper<Ret,Args...>::template mem_fun_nonconst<C,MemFun> w; fun = &w::invoke; data = obj; } template<class C, Ret(C::*MemFun)(Args...) const> void init(C const* obj) { typedef typename wrapper<Ret,Args...>::template mem_fun_const<C,MemFun> w; fun = &w::invoke; data = const_cast<C*>(obj); } }; #include <iostream> int plus(int a, int b) {return a+b;} int minus(int a, int b) {return a-b;} struct foo { int param; explicit foo(int p) : param(p) {} int dings(int a, int b) const { return (a-param)*(b+param); } }; int main() { delegate<int(int,int)> d; d.init<&plus>(); std::cout << d(9,5) << '\n'; d.init<&minus>(); std::cout << d(9,5) << '\n'; foo f (2); d.init<foo,&foo::dings>(&f); std::cout << d(9,5) << '\n'; }und liefert folgende Ausgabe:
14 4 49kk