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 schonmal

    liebe 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
    49
    

    kk


Anmelden zum Antworten