2009-09-04 16 views
5

Ho una classe Menu personalizzata scritta in C++. Per separare il codice in funzioni di facile lettura, sto utilizzando i callback.Un buon modo per implementare callback utilizzabili in C++

Poiché non desidero utilizzare Singletons per l'host del menu, fornisco un altro parametro (obiettivo) che verrà assegnato al callback come primo parametro (una sorta di soluzione alternativa per il riferimento "questo" mancante) .

Registrazione-Firma

AddItem(string s, void(*callback)(void*,MenuItem*), void* target = NULL) 

Esempio di una registrazione

menu->AddItem(TRANSLATE, "translate", &MyApp::OnModeSelected); 

Esempio di Handler

/* static */ 
void MyApp::OnModeSelected(void* that, MenuItem* item) { 
    MyApp *self = (MyApp*)that; 
    self->activeMode = item->text; 
} 

C'è qualcosa si potrebbe considerare sporco con questo approccio? Ce ne sono forse di migliori?

+0

Si dovrebbe non usare i metodi dei membri statici come callback qui. Dovresti usare solo le funzioni dichiarate extern "C" .Si capita solo di essere fortunato che il compilatore che stai usando usi (attualmente) usi lo stesso metodo per chiamare metodi statici e le funzioni non sono garantite dallo standard –

risposta

10

L'approccio richiede che le funzioni di callback siano funzioni libere o membri statici di una classe. Non consente ai client di utilizzare le funzioni membro come richiamate. Una soluzione a questo è utilizzare boost::function come tipo della richiamata:

typedef boost::function<void (MenuItem*)> callback_type; 
AddItem(const std::string& s, const callback_type& callback = callback_type()); 

client può quindi utilizzare boost::bind o boost::lambda passare nella callback:

menu->AddItem("Open", boost::bind(&MyClass::Open, this)); 

Un'altra opzione è quella di utilizzare boost::signals che consente più callback da registrare per lo stesso evento.

+0

'' MenuItem & ma per il resto è completamente d'accordo. – MSalters

+0

Non si dovrebbero usare membri statici della classe come callback generale (non hanno un ABI definito). La funzione boost :: è solo una generalizzazione della dichiarazione di un'interfaccia (la funzione boost :: utilizza solo l'operatore di interfaccia()). Preferirei che le mie interfacce fossero un po 'più esplicite in questo caso, in modo da non passare un metodo inappropriato (cioè ottenere il compilatore per convalidare il callback) come descritto da orsogufo –

8

Mi piace il tuo approccio. Un'alternativa sarebbe quella di dichiarare un'interfaccia, che è in un certo senso il "OO equivalente" di un callback:

class IMenuEntry { 
public: 
    virtual void OnMenuEntrySelected(MenuItem* item) = 0; 
}; 

La firma di registrazione sarebbe diventato

AddItem(string s, IMenuEntry * entry); 

E l'implementazione del metodo

class MyApp : public IMenuEntry { 
public: 
    virtual void OnMenuEntrySelected(MenuItem* item){ 
     activeMode = item->text; 
    } 
} 

L'approccio dell'interfaccia consente di evitare la "soluzione nulla * per il puntatore mancante this.

1

Non vedo niente di sbagliato tranne che la firma del puntatore della funzione è difficile da leggere. Ma, probabilmente farebbe lo schema observer per raggiungere questo obiettivo.

3

Si potrebbe dare un'occhiata utilizzando boost::bind.

menu->AddItem(TRANSLATE, 
       "translate", 
       boost::bind(&MyApp::OnModeSelected, this, _1, _2)); 
1

Mi raccomando guardando boost::function e boost:bind per questo. Imparare renderà la tua funzione vincolante centinaia di volte più facile.

+0

, non dimenticare boost :: lambda. A volte può farti andare via senza una callback del tutto! – EFraim

+0

boost :: i segnali sono migliori per questa attività – Arpegius

1

Leggi this carta bianca. Costruisce varie tecniche per un meccanismo di callback analizzando prestazioni, usabilità e altri compromessi in modo abbastanza dettagliato.Ho trovato una lettura difficile però :-(

0

vostro potrebbe utilizzare un functor per incapsulare il callback. Ciò consentirebbe di utilizzare sia una funzione C-stile o un'interfaccia oggetto di fornire il callback.

Problemi correlati