2013-04-10 13 views
13

La domanda è strettamente relativa a std::function e non a boost::function. Vedere la sezione Aggiornamento in fondo a questa domanda per ulteriori dettagli, in particolare la parte su cui non è possibile confrontare gli oggetti std::function non vuoti secondo lo standard C++ 11.std :: funzione come richiamata, è possibile annullare la registrazione?


Il modello di classe C++ 11 std::function è grande per il mantenimento di una raccolta di callback. Uno può memorizzarli in un vector, ad esempio e richiamarli quando necessario. Tuttavia, mantenere questi oggetti e consentire l'annullamento della registrazione sembra impossibile.

mi permetta di essere specifico, immaginate questa classe:

class Invoker 
{ 
public: 
    void Register(std::function<void()> f); 
    void Unregister(std::function<void()> f); 

    void InvokeAll(); 

private: 
    // Some container that holds the function objects passed to Register() 
}; 

Esempio scenario di utilizzo:

void foo() 
{ 
} 

int main() 
{ 
    std::function<void()> f1{foo}; 
    std::function<void()> f2{[] {std::cout << "Hello\n";} }; 

    Invoker inv; 

    // The easy part 

    // Register callbacks 
    inv.Register(f1); 
    inv.Register(f2); 

    // Invoke them 
    inv.InvokeAll(); 

    // The seemingly impossible part. How can Unregister() be implemented to actually 
    // locate the correct object to unregister (i.e., remove from its container)? 
    inv.Unregister(f2); 
    inv.Unregister(f1); 
} 

E 'abbastanza chiaro come la funzione Register() può essere implementata. Tuttavia, come si dovrebbe implementare lo Unregister(). Diciamo che il contenitore che contiene gli oggetti funzione è vector<std::function<void()>>. Come si troverà un particolare oggetto funzione passato alla chiamata Unregister()? std::function fornisce un sovraccarico operator==, ma verifica solo un oggetto funzione vuoto (ad esempio, non può essere utilizzato per confrontare due oggetti funzione non vuoti per vedere se entrambi fanno riferimento alla stessa chiamata effettiva ).

Apprezzerei qualsiasi idea.

Aggiornamento:

Idea finora consistono principalmente l'aggiunta di un cookie da associare a ciascun oggetto std::function che può essere utilizzato per annullare la registrazione esso. Speravo in qualcosa che non fosse esogeno per l'oggetto std::function stesso. Inoltre, sembra esserci molta confusione tra std::function e boost::function. La domanda riguarda strettamente gli oggetti std::function e gli oggetti nonboost::function.

Inoltre, non è possibile confrontare due oggetti std::function non vuoti per l'uguaglianza. Compareranno sempre non uguali per lo standard. Quindi, i link nei commenti alle soluzioni che fanno proprio questo (e usano gli oggetti boost::function per l'avvio) sono palesemente errati nel contesto di questa domanda.

+5

Si potrebbe fare ciò che MS ha fatto con i punti di connessione: restituire un cookie (indice nel vettore) che il chiamante fornisce alla cancellazione. – WhozCraig

+2

Spesso memorizzo i puntatori all'interfaccia come richiami e li inserisco in un set. Quindi posso accedere in base al valore per annullare la registrazione. –

+0

@RogerRowland bella idea. – WhozCraig

risposta

11

Poiché non è possibile testare l'identità dell'elemento nel contenitore, è probabilmente preferibile utilizzare un contenitore (come std::list) i cui iteratori non invalidano quando il contenitore viene modificato e restituire gli iteratori alla registrazione dei chiamanti che possono essere usato per annullare la registrazione.

Se si desidera utilizzare lo vector (o deque), è possibile restituire l'indice integrale nel vettore/deque quando viene aggiunto il callback. Questa strategia richiederebbe naturalmente che gli indici siano utilizzabili in questo modo per identificare la posizione della funzione nella sequenza. Se i callback e/o l'annullamento della registrazione sono rari, ciò potrebbe significare semplicemente non riusare i punti. Oppure, è possibile implementare una lista gratuita per riutilizzare gli spazi vuoti. Oppure, solo recuperare spazi vuoti dalle estremità della sequenza e mantenere un offset dell'indice di base che viene aumentato quando gli slot vengono recuperati dall'inizio.

Se il modello di accesso di richiamata non richiede l'attraversamento dell'accesso casuale, la memorizzazione delle richiamate in un std::list e l'utilizzo di iteratori non elaborati per annullare la registrazione sembra più semplice.

Problemi correlati