Domanda

Ho un puntatore a funzione definito da:

typedef void (*EventFunction)(int nEvent);

C'è un modo per gestire quella funzione con un'istanza specifica di un oggetto C ++?

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 }
};

Modifica: Orion ha sottolineato le opzioni che Boost offre come:

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

Purtroppo Boost non è un'opzione per me. Esiste una sorta di "curry" funzione che può essere scritta in C ++ che farà questo tipo di wrapping di un puntatore a una funzione membro in un normale puntatore a funzione?

È stato utile?

Soluzione

Consiglio vivamente l'eccellente libreria FastDelegate di Don Clugston. Fornisce tutto ciò che ti aspetteresti da un vero delegato e nella maggior parte dei casi compila alcune istruzioni ASM. L'articolo di accompagnamento è una buona lettura anche sui puntatori alle funzioni dei membri.

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

Altri suggerimenti

È possibile utilizzare i puntatori a funzione per indicizzare la vtable di una determinata istanza di oggetto. Questo si chiama puntatore alla funzione membro . La tua sintassi dovrebbe cambiare per usare il ". * & Quot; e il " & amp; :: " operatori:

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

e quindi:

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 */ }
};

Scappa dai puntatori di funzione C ++ non elaborati e utilizza std: : funzione invece.

Puoi utilizzare boost :: function se stai usando un vecchio compilatore come Visual Studio 2008 che non ha supporto per C ++ 11.
boost: function e std :: function sono la stessa cosa - hanno portato un bel po 'di roba boost nella libreria std per C ++ 11.

Nota: potresti voler leggere la documentazione della funzione boost invece di quella di microsoft poiché è più facile da capire

Puoi trovare FAQ C ++ di Marshall Cline utile a ciò che stai cercando di realizzare.

Leggi puntatori ai membri . Per chiamare un metodo sulla classe derivata, il metodo deve essere dichiarato nella classe base come virtuale e ignorato nella classe base e il puntatore deve puntare al metodo della classe base. Ulteriori informazioni sui puntatori ai membri virtuali .

Se stai interfacciarsi con una libreria C, non puoi usare una funzione membro della classe senza usare qualcosa come boost :: bind . La maggior parte delle librerie C che accettano una funzione di callback di solito ti permettono anche di passare un argomento extra di tua scelta (di solito di tipo void * ), che puoi usare per avviare la tua classe, così:


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
}

Sfortunatamente, il tipo EventFunction non può puntare a una funzione di B, perché non è il tipo corretto. Potresti renderlo del tipo corretto, ma probabilmente non è proprio la soluzione che desideri:

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

... e poi tutto funziona quando chiami il callback con un obhect di B. Ma probabilmente vuoi essere in grado di chiamare funzioni al di fuori di B, in altre classi che fanno altre cose. Questo è una specie di punto di richiamata. Ma ora questo tipo punta a qualcosa di decisamente in B. Le soluzioni più interessanti sono:

  • Rendi B una classe base, quindi sovrascrivi una funzione virtuale per ogni altra classe che potrebbe essere chiamata. A quindi memorizza un puntatore su B anziché un puntatore a funzione. Molto più pulito.
  • Se non vuoi associare la funzione a un tipo di classe specifico, anche a una classe di base (e non ti darei la colpa), ti suggerisco di rendere la funzione che viene chiamata funzione statica: " < code> static void EventFrom A (int nEvent); " ;. Quindi puoi chiamarlo direttamente, senza un oggetto di B. Ma probabilmente vuoi che chiami un'istanza specifica di B (a meno che B non sia un singleton).
  • Quindi, se vuoi essere in grado di chiamare una specifica istanza di B, ma anche di poter chiamare anche non-B, devi passare qualcos'altro alla tua funzione di callback in modo che la funzione di callback possa chiamare l'oggetto giusto . Rendi la tua funzione statica, come sopra, e aggiungi un parametro void * che farai un puntatore a B.

In pratica si vedono due soluzioni a questo problema: i sistemi ad hoc in cui si passa un vuoto * e l'evento e le gerarchie con funzioni virtuali in una classe base, come i sistemi di finestre

Hai detto che boost non è un'opzione per te, ma hai TR1 disponibile per te?

TR1 offre oggetti function, bind e mem_fn in base alla libreria boost e potresti già averlo in bundle con il tuo compilatore. Non è ancora standard, ma almeno due compilatori che ho usato di recente ce l'hanno.

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

Non è chiaro cosa stai cercando di realizzare qui. ciò che è chiaro è che i puntatori a funzione non sono la strada giusta.

forse quello che stai cercando è il puntatore al metodo.

Ho una serie di classi per questa cosa esatta che uso nel mio framework c ++.

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

Il modo in cui lo gestisco è che ogni funzione di classe che può essere utilizzata come callback richiede una funzione statica che associ il tipo di oggetto ad esso. Ho una serie di macro che lo fanno automaticamente. Fa una funzione statica con lo stesso nome tranne che con un " CB_ " prefisso e un primo parametro aggiuntivo che è il puntatore all'oggetto classe.

Verifica i tipi di classe kGUICallBack e le varie versioni dei modelli per gestire combinazioni di parametri diversi.

#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);}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top