Frage

Ich habe einen Funktionszeiger definiert durch:

typedef void (*EventFunction)(int nEvent);

Gibt es eine Möglichkeit, diese Funktion mit einer bestimmten Instanz eines C zu behandeln ++ Objekt?

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne() { handler(1); }
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(EventFromA); }  // What do I do here?

    void EventFromA(int nEvent) { // do stuff }
};

Edit: Orion wies die Optionen aus, die Angebote steigern wie:

boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

Leider Boost ist keine Option für mich. Gibt es irgendeine Art von „currying“ -Funktion, die in C geschrieben werden können ++, die diese Art von Verpackung eines Zeigers auf eine Elementfunktion in einen normalen Funktionszeiger tun?

War es hilfreich?

Lösung

I Don Clugston hoch ausgezeichnete FastDelegate Bibliothek empfehlen. Es bietet alles, was man von einem echten Delegierten erwarten würde und kompiliert in den meisten Fällen auf wenige ASM Anweisungen unten. Der begleitende Artikel ist gut zu lesen auf Mitgliedsfunktionszeiger als auch.

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

Andere Tipps

Sie können Funktionszeiger auf Index in die V-Tabelle einer bestimmten Objektinstanz verwenden. Dies ist ein Mitglied Funktionszeiger genannt. Ihre Syntax müsste sich ändern, die zu verwenden und die ‚& ::‘ Operatoren „*“:

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

und dann:

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(&B::EventFromA); } // note: "&::"

    void EventFromA(int nEvent) { /* do stuff */ }
};

Führen Sie weg von rohen C ++ Funktionszeiger, und verwenden Sie std::function statt.

Sie können mit boost::function wenn Sie einen alten Compiler wie Visual Studio 2008 verwenden, die keine Unterstützung für C ++ 11 hat.
boost:function und std::function sind die gleiche Sache - sie ziemlich viel Auftrieb Sachen in die std-Bibliothek für C ++ 11 gezogen

.

Hinweis: Sie können die Boost-Funktion Dokumentation anstelle des Microsoft ein lesen, da es einfacher ist, zu verstehen,

Sie können feststellen, C ++ FAQ von Marshall Cline hilfreich zu dem, was Sie erreichen wollen.

Lesen Sie mehr über Zeiger auf Elemente . Um eine Methode auf der abgeleiteten Klasse zu nennen, hat die Methode in der Basisklasse als virtuelle deklariert werden und in der Basisklasse außer Kraft gesetzt und den Zeiger sollte auf die Basisklasse Methode zeigen. Weitere Informationen zum Zeiger auf virtuelle Mitglieder .

Wenn Sie mit einer C-Bibliothek sind Schnittstellen, dann können Sie nicht eine Klasse Member-Funktion verwenden, ohne etwas wie boost::bind zu verwenden. Die meisten C-Bibliotheken, die eine Callback-Funktion übernehmen in der Regel erlauben auch Sie ein zusätzliches Argument Ihrer Wahl zu übergeben (in der Regel vom Typ void*), mit dem Sie Ihre Klasse verwenden können, um Bootstrap, wie so:


class C
{
public:
  int Method1(void) { return 3; }
  int Method2(void) { return x; }

  int x;
};

// This structure will hold a thunk to
struct CCallback
{
  C *obj;  // Instance to callback on
  int (C::*callback)(void);  // Class callback method, taking no arguments and returning int
};

int CBootstrapper(CCallback *pThunk)
{
  // Call the thunk
  return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}

void DoIt(C *obj, int (C::*callback)(void))
{
  // foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
  struct CCallback thunk = {obj, callback};
  foobar(&CBootstrapper, &thunk);
}

int main(void)
{
  C c;
  DoIt(&c, &C::Method1);  // Essentially calls foobar() with a callback of C::Method1 on c
  DoIt(&c, &C::Method2);  // Ditto for C::Method2
}

Leider kann die EventFunction Typ nicht auf eine Funktion von B zeigen, weil es nicht der richtige Typ ist. Man könnte es die richtige Art machen, aber das ist wahrscheinlich nicht wirklich die Lösung, die Sie wollen:

typedef void (*B::EventFunction)(int nEvent);

... und dann alles funktioniert, wenn Sie den Rückruf mit einem obhect von B. nennen Aber Sie wollen wahrscheinlich Funktionen aufrufen außerhalb von B in der Lage sein, in anderen Klassen, die andere Dinge zu tun. Das ist sozusagen der Punkt eines Rückruf. Aber jetzt diese Art zeigt auf etwas definitiv in B. Weitere attraktive Lösungen sind:

  • Erstellen B eine Basisklasse, dann für jede andere Klasse eine virtuelle Funktion außer Kraft setzen, die aufgerufen werden können. A speichert dann einen Zeiger an Stelle eines Funktionszeiger auf b. Viel sauberer.
  • Wenn Sie die Funktion nicht auf eine bestimmte Klassentyp binden wollen, auch eine Basisklasse (und ich würde es dir nicht verdenken), dann empfehle ich Ihnen, um die Funktion, die eine statische Funktion aufgerufen wird: „static void EventFrom A(int nEvent);“ . Dann können Sie es direkt aufrufen, ohne ein Objekt von B. Aber Sie wahrscheinlich wollen, dass es eine bestimmte Instanz von B nennen (es sei denn, B ein Singleton ist).
  • Wenn Sie also in der Lage sein wollen, eine bestimmte Instanz von B zu nennen, aber in der Lage nicht-B, nennen, dann müssen Sie etwas anderes zu Ihrer Callback-Funktion übergeben, so dass die Callback-Funktion kann das richtige Objekt aufrufen . Machen Sie Ihre Funktion eine statische, wie oben, und fügen Sie einen void * Parameter, die Sie einen Zeiger auf B machen.

In der Praxis Du zwei Lösungen für dieses Problem zu sehen: Ad-hoc-Systeme, bei denen Sie einen void * und das Ereignis übergeben und Hierarchien mit virtuellen Funktionen in einer Basisklasse, wie Systeme Windowing

Sie erwähnen, dass Boost ist keine Option für Sie, aber Sie haben TR1 zur Verfügung?

TR1 bietet Funktion, binden und mem_fn Objekte auf der Boost-Bibliothek basiert, und Sie können bereits mit Ihrem Compiler gebündelt haben. Es ist noch nicht Standard, sondern mindestens zwei Compiler, die ich vor kurzem haben es benutzt hätte.

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702. aspx

Es ist etwas unklar, was Sie versuchen hier zu erreichen. Klar ist, dass die Funktionszeiger nicht der richtige Weg ist.

vielleicht, was Sie suchen ist Zeiger auf Verfahren.

Ich habe eine Reihe von Klassen für diese genaue Sache, die ich in meinem c ++ Framework verwenden.

http://code.google.com/p /kgui/source/browse/trunk/kgui.h

Wie kann ich damit umgehen ist jede Klasse Funktion, die verwendet werden kann als ein Rückruf eine statische Funktion benötigt, die den Objekttyp bindet. Ich habe eine Reihe von Makros, die es automatisch. Es macht eine statische Funktion mit dem gleichen Namen, außer mit einem „cb_“ Präfix und einem zusätzlichen ersten Parameter, der die Klasse Objektzeiger sind.

Zur Kasse der Klassentypen kGUICallBack und verschiedene Template-Versionen davon für verschiedene Parameter-Kombinationen Handhabung.

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top