Funktionszeiger casten



  • Hallo,

    ich habe eine Memberfunktion die als Argument einen Funktionszeiger benötigt.

    Test::caller(void (*name)())

    Die entsprechende Funktion, die Ausgeführt werden soll
    habe ich allerdings in einer Klasse definiert, ist also
    eine Memberfunktion

    void (MyClass:: *name)()

    ist es möglich die Memberfunktion als Argument zu übergeben ?

    Wie muss ich das bitte casten ?


  • Mod

    MBreuer schrieb:

    ist es möglich die Memberfunktion als Argument zu übergeben ?

    nein, die Memberfunktion müsste statisch sein. Für welches Objekt sollte die Memberfunktion überhaupt aufgerufen werden und woher soll Test::caller das wissen?



  • OK im Grunde muss ich genauer fragen

    ich möchte ein Objekt von MyClass haben
    und anschliessend für dieses Objekt den Funktionszeiger übergeben.

    MyClass param;
    ...

    Test::caller(param.funktion)



  • Das mit den Zeigern auf Member-Funktionen geht so:

    struct Test
    {
    	void function()
    	{
    	}
    };
    
    void caller(Test *test, void (Test::*func)())
    {
    	(test->*func)();
    }
    
    int main()
    {
    	Test t;
    	caller(&t, &Test::function);
    }
    


  • danke jetzt brauche ich das nur noch mit folgender Signatur:
    void caller(void (*func)()) ;

    wobei ich void (Test::*func)() übergeben möchte



  • MBreuer schrieb:

    danke jetzt brauche ich das nur noch mit folgender Signatur:
    void caller(void (*func)()) ;

    void (*func)() ist ein Zeiger auf eine freie Funktion mit 0 Parametern und Rückgabetyp void .

    MBreuer schrieb:

    wobei ich void (Test::*func)() übergeben möchte

    Das hingegen ist eine Memberfunktion. Was willst du also? Inwiefern geht TyRoXx' Code nicht?

    Vielleicht helfen dir auch die Function Pointer Tutorials.



  • MBreuer schrieb:

    danke jetzt brauche ich das nur noch mit folgender Signatur:
    void caller(void (*func)()) ;

    wobei ich void (Test::*func)() übergeben möchte

    void (*func)() ist dafür unbrauchbar. Dieser Funktionszeiger func kann wirklich nur die Adresse einer freien oder statischen Funktion speichern und nichts weiteres, also auch nicht die Information, auf welchem Objekt eine Elementfunktion aufgerufen werden soll (der implizite this -Parameter).

    Probier mal tr1::function oder Boost.Function.

    Gruß,
    SP



  • MBreuer schrieb:

    danke jetzt brauche ich das nur noch mit folgender Signatur:
    void caller(void (*func)()) ;

    wobei ich void (Test::*func)() übergeben möchte

    das haben sich schon viele gedacht 😉
    geht bloss leider nicht. zumindest nicht portierbar.



  • Mir würde eine Lösung für VC++ 8.0 reichen 🙂



  • MBreuer schrieb:

    Mir würde eine Lösung für VC++ 8.0 reichen 🙂

    Woher soll ein void()() wissen, auf welchem Objekt eine Funktion aufgerufen werden soll? Die "Adresse" einer Elementfunktionen ist unabhängig von der Adresse des Objektes, auf dem sie aufgerufen werden soll. Alle Objekte teilen sich quasi dieselbe Elementfunktion und this ist ein versteckter zusätzlicher Parameter. Ein void()() speichert nur die Adresse einer Funktion, nichts anderes, also auch keinen this-Zeiger. Selbst wenn du wild reinterpret_cast benutzt, kann es so gar nicht funktionieren, wie Du es haben willst.

    Siehe MSDN <functional> (TR1). Du kannst im Prinzip alles, was einen entsprechenden Funktions-Aufruf-Operator bietet, zu einem function<>-Objekt konvertieren, einschließlich "normale" Funktionszeiger und bind-Ergebnisse:

    using std::tr1::function;
    using std::tr1::bind;
    using std::tr1::placeholders::_1;
    
    void blah(function<void(double)> f)
    {
      f(3.14159265);
    }
    
    class Klasse
    {
      double faktor;
    public:
      explicit Klasse(double f) : faktor(f) {}
    
      void mf(double b) const
      {
        std::cout << faktor*b << '\n';
      }
    };
    
    void bums(double b)
    {
      std::cout << b << '\n';
    }
    
    int main()
    {
      Klasse dings (2.7);
      blah( bind(&Klasse::mf, dings,_1) ); // kopiert dings
      blah( bind(&Klasse::mf,&dings,_1) ); // speichert nur Zeiger auf dings
      blah( bums );
    }
    


  • Das sieht gut aus, leider bekomme ich es nicht zum Laufen

    // Test.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
    //
    
    #include "stdafx.h"
    #include <functional>
    using std::tr1::function;
    using std::tr1::bind;
    
    void blah(function<void(double)> f)
    {
      f(3.14159265);
    }
    
    class Klasse
    {
      double faktor;
    public:
      explicit Klasse(double f) : faktor(f) {}
    
      void mf(double b) const
      {
        std::cout << faktor*b << '\n';
      }
    };
    
    void bums(double b)
    {
      std::cout << b << '\n';
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
      Klasse dings (2.7);
      blah( bind(&Klasse::mf, dings) ); // kopiert dings in das function<>-Objekt
      blah( bind(&Klasse::mf,&dings) ); // speichert nur Zeiger auf dings
      blah( bums );
    	return 0;
    }
    

    gibt bei mir

    ------ Erstellen gestartet: Projekt: Test, Konfiguration: Debug Win32 ------
    Kompilieren...
    Test.cpp
    f:\projekte\inova\spielwiese\test\test.cpp(6) : error C2039: 'function': Ist kein Element von 'std::tr1'
    f:\projekte\inova\spielwiese\test\test.cpp(6) : error C2873: 'function': Das Symbol kann nicht in einer using-Deklaration verwendet werden
    f:\projekte\inova\spielwiese\test\test.cpp(7) : error C2039: 'bind': Ist kein Element von 'std::tr1'
    f:\projekte\inova\spielwiese\test\test.cpp(7) : error C2873: 'bind': Das Symbol kann nicht in einer using-Deklaration verwendet werden
    f:\projekte\inova\spielwiese\test\test.cpp(9) : error C2065: 'function': nichtdeklarierter Bezeichner
    f:\projekte\inova\spielwiese\test\test.cpp(9) : error C2144: Syntaxfehler: 'double' sollte auf ')' folgen
    f:\projekte\inova\spielwiese\test\test.cpp(9) : error C2059: Syntaxfehler: ')'
    f:\projekte\inova\spielwiese\test\test.cpp(10) : error C2143: Syntaxfehler: Es fehlt ';' vor '{'
    f:\projekte\inova\spielwiese\test\test.cpp(10) : error C2447: '{': Funktionsheader fehlt - Parameterliste im alten Stil?
    f:\projekte\inova\spielwiese\test\test.cpp(22) : error C2039: 'cout': Ist kein Element von 'std'
    f:\projekte\inova\spielwiese\test\test.cpp(22) : error C2065: 'cout': nichtdeklarierter Bezeichner
    f:\projekte\inova\spielwiese\test\test.cpp(28) : error C2039: 'cout': Ist kein Element von 'std'
    f:\projekte\inova\spielwiese\test\test.cpp(28) : error C2065: 'cout': nichtdeklarierter Bezeichner
    f:\projekte\inova\spielwiese\test\test.cpp(34) : error C3861: "blah": Bezeichner wurde nicht gefunden.
    f:\projekte\inova\spielwiese\test\test.cpp(34) : error C3861: "bind": Bezeichner wurde nicht gefunden.
    f:\projekte\inova\spielwiese\test\test.cpp(35) : error C3861: "blah": Bezeichner wurde nicht gefunden.
    f:\projekte\inova\spielwiese\test\test.cpp(35) : error C3861: "bind": Bezeichner wurde nicht gefunden.
    f:\projekte\inova\spielwiese\test\test.cpp(36) : error C3861: "blah": Bezeichner wurde nicht gefunden.
    Das Buildprotokoll wurde unter "file://f:\Projekte\Inova\Spielwiese\Test\Debug\BuildLog.htm" gespeichert.
    Test - 18 Fehler, 0 Warnung(en)
    ========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========

    Welchen Header muss ich einbinden, was muss ich noch beachten.
    Danke für die Hinweise.



  • MBreuer schrieb:

    Welchen Header muss ich einbinden

    Die Header, die std::tr1::function und std::tr1::bind definieren. Die Fehlermeldung besagt ja, dass dem Compiler nicht bewusst ist, dass es die zwei gibt. Außerdem muss die TR1-Bibliothek natürich überhaupt erstmal installiert sein.



  • über
    http://www.aristeia.com/EC3E/TR1_info.html

    kam ich auf
    http://www.microsoft.com/downloads/details.aspx?FamilyId=D466226B-8DAB-445F-A7B4-448B326C48E7&displaylang=en

    und hab da ein wenig bedenken das zu installieren um den Code auszuprobieren.

    Ich denke ich bleibe bei einem Workaround der aus meiner callback-Funktion
    über ein Singleton die entsprechenden Objektinformationen holt, die ich in der Callback-Funktion brauche.



  • MBreuer schrieb:

    Ich denke ich bleibe bei einem Workaround der aus meiner callback-Funktion
    über ein Singleton die entsprechenden Objektinformationen holt, die ich in der Callback-Funktion brauche.

    Eben, warum sauber, wenn es genau hässlich geht? 👍



  • MBreuer schrieb:

    Ich denke ich bleibe bei einem Workaround der aus meiner callback-Funktion
    über ein Singleton die entsprechenden Objektinformationen holt, die ich in der Callback-Funktion brauche.

    Du könntest auch einfach mal mehr über das Problem erzählen, was Du lösen willst. Eventuell kann hier jemand eine andere elegante Lösung beisteuern.

    Kannst auch hier gucken.



  • Ich habe ein Objekt erstellt.
    Attribut filename=filename.xml

    Der Funktionszeiger den ich übergeben will ist der Funktionszeiger einer
    Callback-Funktion.
    Die Lib ruft die Callback funtion auf.
    Jetzt hätte ich gern das diese Callback-Funktion die meines Objektes ist, d.h, der filename (filename.xml) verwendet wird.

    Jetzt ist meine Lösung die das ich ein
    Singleton habe, dieses Singleton at ein Attribut meines Objektes.

    Im Objekt habe ich eine Funktion die das Singleton nach diesem Objekt fragt und den Filenamen zurückgibt.

    Das ganze geht nicht direkt über das Singleton, sondern ich habe noch ein Interface dazwischengeschaltet, so das ich später auch von anderen Stellen den Filenamen holen kann.



  • Und diese ominöse Bibliothek besitzt keinen void *user neben dem Callback? Falls das stimmt würde ich dringend die Bibliothek wechseln.
    Falls sie diese Möglichkeit bietet wäre folgendes eine Abhilfe:

    void cb_wrapper(void *user)
    {
       DeineKlasse *obj = (DeineKlasse *)user;
       obj->*deine_funktion();
    }
    

    Falls du an der Bibliothek festhalten willst ginge auch folgendes:

    struct cb_wrapper
    {
       static DeineKlasse *obj;
       void cb_func()
       {
           obj->*deine_funktion();
       }
    };
    
    //Aufruf:
    cb_wrapper::obj = &obj;
    dumme_lib_cb_func(&cb_wrapper::cb_func)
    


  • MBreuer schrieb:

    Ich habe ein Objekt erstellt.
    Attribut filename=filename.xml

    Der Funktionszeiger den ich übergeben will ist der Funktionszeiger einer
    Callback-Funktion. Die Lib ruft die Callback funtion auf.

    Welche Lib? Deine Lib? Kannst Du da irgendwas an der Schnittstelle ändern? Was ist der genaue Typ des Funktionszeigers (Parameter, Rückgabewert, etc)?

    MBreuer schrieb:

    Jetzt hätte ich gern das diese Callback-Funktion die meines Objektes ist, d.h, der filename (filename.xml) verwendet wird.

    Jetzt ist meine Lösung die das ich ein
    Singleton habe, dieses Singleton at ein Attribut meines Objektes.

    Im Objekt habe ich eine Funktion die das Singleton nach diesem Objekt fragt und den Filenamen zurückgibt.

    Das ganze geht nicht direkt über das Singleton, sondern ich habe noch ein Interface dazwischengeschaltet, so das ich später auch von anderen Stellen den Filenamen holen kann.

    Klingt sehr kompliziert. Dann kannst Du ja auch gleich den Dateinamen als globale Variable anlegen und eine normale freie Funktion als Callback benutzen. Richtig toll ist das allerdings auch nicht.

    Gruß,
    SP



  • ich hatte bisher auch schon 2-3 mal einen fall ohne "void* userdata".

    bleibt entweder singleton/globale variable (evtl. über mutex abgesichert, falls die callbacks nicht asynchron reinkommen -- und kommen sie doch asynchron, dann hilft nurmehr beten *g*).

    oder aber das was z.B. auch die ATL macht: dynamisch thunks erzeugen. ist bloss leider ziemlich hässliche low-level frickelei. dafür ist es in der anwendung sehr flexibel und "schön".

    p.S.: ich hab' mich immer mit der "globale variablen" lösung zufrieden gegeben, da es in diesen fällen ausreichend war, und thunks erstellen ungleich mehr aufwand bedeutet.



  • hustbaer schrieb:

    p.S.: ich hab' mich immer mit der "globale variablen" lösung zufrieden gegeben, da es in diesen fällen ausreichend war, und thunks erstellen ungleich mehr aufwand bedeutet.

    Falls er wirklich void(*)() braucht, könnte man es ja so machen:

    #include <iostream>
    
    void foo_bloed( void (*pf)() )
    {
      pf();
    }
    
    void foo_besser( void (*pf)(void*), void* ud )
    {
      pf(ud);
    }
    
    template< class C, void (C::*Pmf)() >
    struct wrapper {
      static C* pointer;
      static void func_global()
      { (pointer->*Pmf)(); }
      static void func_user(void* ud)
      { (reinterpret_cast<C*>(ud)->*Pmf)(); }
    };
    
    template< class C, void (C::*Pmf)() >
    C* wrapper<C,Pmf>::pointer;
    
    class Klasse {
    public:
      void doit() { std::cout<<"Hello!\n"; }
    };
    
    int main()
    {
      Klasse dings;
      typedef wrapper<Klasse,&Klasse::doit> wt;
      wt::pointer = &dings; // Globale Variable
      foo_bloed(&wt::func_global);
      foo_besser(&wt::func_user, &dings);
    }
    

    Die "globale" Variante funktioniert natürlich nur solange sich Zugriffe auf die globalen Variablen nicht überschneiden. Immerhin bekommt jede Klassen/Elementfunktion-Kombination durch dieses Template einen eigenen Objekt-Zeiger.

    Gruß,
    SP


Anmelden zum Antworten