2011-02-07 15 views
6

Abbiamo una semplice libreria di comunicazione per i nostri clienti.Chiamata in C++ a un membro della classe

Il mio problema è: come posso salvare un puntatore a un metodo da una classe del nostro cliente?

Library.h è il file di intestazione con tutti i metodi necessari al nostro cliente per stabilire una comunicazione.

library.cpp è il nostro codice. Da qualche parte qui devo salvare i puntatori al metodo della funzione di callback dai nostri clienti.

customer.cpp è un esempio di come un cliente utilizza la nostra libreria.

library.h:

// This is the header file what our customer gets 
class Library { 
    public: 
    template <class Object, class Function> 
    void SetCallback(Object &obj, Function f); 
}; 

library.cpp:

struct T_CUSTOMER { 
    Object o; // <- ??? 
    Function f; // <- ??? 
} customer; 

void Library::SetCallback(Object &obj, Function f) { 
    //Saving the method from our costumer 
    customer.o = obj; // <- ??? 
    customer.f = f;  // <- ??? 
} 

void someFunction(void) { 
    // here i want to call the method from the customer 
    customer.o->customer.f(); //<- ??? 
} 

customer.cpp:

class AnyCustomerClass { 
    private: 
     Library lib; 

    public: 
     AnyCustomerClass() { 
      //< here the customer sets his method which I should call 
      lib.SetCallback(this, &AnyCustomerClass::callback()); 
     } 

     callback() { 
      // do something 
     } 
} 

Grazie per qualsiasi aiuto!

risposta

7

L'idea di base è che tu definisci una classe di callback astratta, che viene effettivamente passata all'interfaccia. Questo si chiama di nuovo a una funzione che passa un solo parametro int:

struct Callback { 
    virtual ~Callback(){} 
    virtual void operator()(int param)=0; 
}; 

Questa classe consente l'implementazione di essere liberi dalla conoscenza del codice è necessario chiamare nuovamente dentro. Ovviamente, per chiamare in una classe, hai bisogno di una istanziazione di Callback che abbia conoscenza del suo obiettivo. Così si poi anche fornire una classe figlia su modelli che lo rende facile per gli utenti della vostra libreria per legare un metodo in una delle loro classi a un'istanza della richiamata generica: -

template<class T> 
class ClassCallback : public Callback { 
    T* _classPtr; 
    typedef void(T::*fncb)(int param); 
    fncb _cbProc; 
public: 
    ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){} 
    virtual void operator()(int param){ 
    (_classPtr->*_cbProc)(param); 
    } 
}; 

Per creare un'istanza del callback dalla loro classe, il codice sarebbe simile a questo. E anche l'invocazione è semplice:

struct CMyClass { 
    Library* _theLibrary; 
    CMyClass(Library* init):_theLibrary(init){ 
    Callback* pCB = new ClassCallback<CMyClass>(&myClass,&CMyClass::OnCb); 
    _theLibrary->SetCallback(pCB); 
    } 
    void OnCb(int){ 
    // callback triggered 
    } 
    void Run(){ 
    _theLibrary->DoWork(); 
    } 
}; 

In Riepilogo: Library.h quindi sarebbe simile a questo. Definire la classe astratta di richiamata, la classe biblioteca e la classe di utilità su modelli che il cliente utilizza per avvolgere il loro classe e il suo metodo di callback con:

// This is the header file what our customer gets 
struct Callback {... }; 
class Library { 
    Callback* _pcb; 
    public: 
    void SetCallback(Callback* pcb){_pcb=pcb;} 
    void DoWork(){ 
     int status=0; 
     (*pcb)(status); 
    } 
    ~Library(){delete _pcb;} 

}; 
template<class T> struct ClassCallback{ ... }; 
+0

Puoi dare un altro sguardo al codice che ho postato ieri? [Link] (http://stackoverflow.com/questions/4919308/callback-in-c-to-a-class-member/4920803#4920803) I mai pensato che sarebbe stato così complicato per me scrivere una funzione di callback. Grazie mille! – Sascha

+0

-1 per ignorare la gestione della memoria. – ltjax

5

L'idea di base è nascondere il tipo esatto dell'oggetto e della funzione (Object e Function nel codice) dietro una chiamata di funzione virtuale e avvolgere entrambi in un'interfaccia astratta (questo è l'idioma 'tipo cancellazione').

È quindi possibile consentire ai clienti di derivare dal tipo "callback di base" tramite un'interfaccia modello.

Per un tutorial, vedere la parte 4. su this website. Oppure guarda come funzionano Boost.Function e Boost.Bind (fanno esattamente questo, anche se con un'interfaccia leggermente più potente)

+1

+1 per boost.function – fouronnes

+0

-1 per l'utilizzo di boost :: per ottenere qualcosa in std :: –

+0

@Chris dove std :: implement type erasure per oggetti funzione (pre-tr1)? Inoltre, non sto nemmeno "usando" il boost, solo elencandolo come riferimento su come queste cose possano essere implementate bene. – ltjax

0

Il codice qui sotto sembra funzionare in qualche modo, ma imho l'intero design è molto sospetto http://codepad.org/9QDcMJAg

#include <stdio.h> 

struct Library { 
    template <class Object,class Function> 
    void SetCallback(Object &obj, Function f); 
}; 


struct Object { 
    void Method(void); 
}; 

typedef void (Object::*Function)(void); 

struct T_CUSTOMER { 
    Object o; 
    Function f; 
}; 

T_CUSTOMER customer; 

template <class Object,class Function> 
void Library::SetCallback(Object &obj, Function f) { 
    customer.o = obj; 
    customer.f = f; 
} 

void someFunction(void) { 
    (customer.o.*customer.f)(); 
} 

struct AnyCustomerClass : Object { 
    Library lib; 

    AnyCustomerClass() { 
    lib.SetCallback(*this, (Function)&AnyCustomerClass::callback); 
    } 

    void callback(void) { 
    printf("callback!\n"); 
    } 
}; 

int main(void) { 

    AnyCustomerClass a; 

    someFunction(); 

} 
3

Il modo più semplice e più flessibile è quello di utilizzare la funzione std ::.

Supponiamo che abbia una funzione (ma potrebbe essere anche una classe), che deve chiamare un'altra funzione passata ad essa. Lo definisco funzione come questa:

#include <functional>   // defines std::function 
#include <iostream> 

typedef std::function<bool(int)> FunctionPtr; 

void myFunction (const FunctionPtr &functionPtr) 
{ 
std::cout << "Before" << std::endl; 
functionPtr(123); 
std::cout << "After" << std::endl; 
} 

Un primo esempio su come usare questo utilizza una funzione globale (o metodo statico), in questo modo:

bool myFunPtr(int i) 
    { 
    std::cout << "FunPtr:" << i << std::endl; 
    return true; 
    } 

int main() 
{ 
myFunction (myFunPtr); 
} 

me a passare il puntatore alla funzione di myFunction.

Posso anche usare un'espressione lambda, in questo modo:

int main() 
{ 
myFunction ([](int i)->bool{std::cout << "Lambda:" << i << std::endl;return true;}); 
} 

Il terzo esempio (e che è probabilmente quello che si vuole), è quello di passare un'istanza di classe che ha definito la funzione di operatore, come questo:

class X 
    { 
    public: 
     X(std::string s) : m_s(s) {} 
     bool operator()(int i) 
     { 
     std::cout << m_s << i << std::endl; 
     return true; 
     } 
    private: 
     std::string m_s; 
    }; 

int main() 
{ 
myFunction ([](int i)->bool{std::cout << "Lambda:" << i << std::endl;return true;}); 

myFunction (myFunPtr); 

X x1("x1:"); 
myFunction (x1); 

X x2("x2:"); 
myFunction (x2); 
} 

Come si può vedere, utilizzando la funzione std ::, posso passare 3 diversi tipi di 'i riferimenti alle funzioni' (puntatori a funzione, lambda e di funtori).

+0

Non tutti hanno ancora un compilatore compatibile tr0 o "pre" C++ 0x. –

0

Grazie per le risposte, ma ho ancora problemi con l'attuazione (I normalmente programmare in Java o in C (microcontrollore). Mai pensato che sarebbe stato così complicato per me per realizzare questo in C++))

Mi piace usare il suggerimento di Chris Becke.

library.h

// This is the header file what our customer gets 
struct Callback { 
    virtual void operator()(int param)=0; 
}; 
class Library { 
    Callback *cb; 
    public: 
    void SetCallback(Callback*); 
}; 
template<class T> 
class ClassCallback : public Callback { 
    T* _classPtr; 
    typedef void(T::*fncb)(int dwTime); 
    fncb _cbProc; 
public: 
    ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){} 
    virtual void operator()(int param){ 
    (_classPtr->*_cbProc)(param); 
    } 
}; 

customer.cpp

class T_Customer{ 
private: 
    Library lib; 
    void OnCallback(int param){ 
      printf("Parameter: %i\n",param); 
     } 
    }; 
public: 
    T_Customer(){ 
     lib.SetCallback(new ClassCallback<T_Customer>(this,&T_Customer::OnCallback)); 
    } 
}; 

library.cpp

void Library::SetCallback(Callback *callback){ 
    cb = callback; 
} 


void executeCallback(){ 
    if(cb) 
    (*cb)(); 
} 

int main(void){ 
    T_Customer customer; 
    executeCallback(); 
    return 0; 
} 
+0

Come hai postato come risposta, l'ho risolto sul posto (spero) –

Problemi correlati