2013-04-10 8 views
13

Die Frage ist streng genommen std::function und nicht boost::function. Weitere Details dazu finden Sie im Abschnitt Update am Ende dieser Frage, insbesondere den Teil, in dem es nicht möglich ist, nicht leere std::function Objekte nach dem C++ 11-Standard zu vergleichen.std :: Funktion als Rückruf, ist die Abmeldung möglich?


Die C++ 11 std::function Klassenvorlage ist für eine Sammlung von Rückrufen beibehalten wird. Man kann sie zum Beispiel in einem vector speichern und bei Bedarf aufrufen. Es ist jedoch unmöglich, diese Objekte beizubehalten und die Aufhebung der Registrierung zuzulassen.

mich spezifisch sein lassen, sich vorstellen, diese Klasse:

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

Beispielverwendungsszenario:

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

Es ist ziemlich klar, wie die Register() Funktion implementiert werden kann. Wie wäre es jedoch mit der Implementierung von Unregister()? Nehmen wir an, dass der Container, der die Funktionsobjekte enthält, vector<std::function<void()>> ist. Wie finden Sie ein bestimmtes Funktionsobjekt, das an den Aufruf Unregister() übergeben wird? std::function liefert eine überladene operator==, die aber nur für ein leeres Funktionsobjekt testet (d. H. Es kann nicht zum Vergleichen zweier nicht leerer Funktionsobjekte verwendet werden, um zu sehen, ob beide auf denselben tatsächlichen Aufruf verweisen).

Ich würde mich über irgendwelche Ideen freuen.

Update:

Ideen bisher vor allem von der Zugabe eines Cookies bestehen zu jedem std::function Objekt zugeordnet werden, die verwendet werden können, um es zu deregistrieren. Ich habe auf etwas gehofft, das für das Objekt selbst nicht exogen ist. Es scheint auch eine große Verwirrung zwischen std::function und boost::function zu geben. Die Frage ist streng genommen std::function Objekte und nichtboost::function Objekte.

Außerdem können Sie zwei nicht leere std::function Objekte für die Gleichheit nicht vergleichen. Sie werden immer ungleiche nach dem Standard vergleichen. Also, Links in den Kommentaren zu Lösungen, die genau das tun (und boost::function Objekte zum Booten verwenden) sind offensichtlich falsch im Zusammenhang mit dieser Frage.

+5

Sie könnten tun, was MS mit Verbindungspunkten gemacht hat: Geben Sie einen Cookie (Index in Ihren Vektor) zurück, den der Anrufer für die Abmeldung bereitstellt. – WhozCraig

+2

Ich speichere oft Schnittstellenzeiger als Callbacks und lege sie in einen Satz. Dann kann ich auf den Wert zugreifen, um die Registrierung aufzuheben. –

+0

@RogerRowland nette Idee. – WhozCraig

Antwort

11

Da Sie im Container keine Elementidentität testen können, empfiehlt es sich, einen Container (z. B. std::list) zu verwenden, dessen Iteratoren beim Ändern des Containers nicht ungültig werden, und Iteratoren zurück zur Registrierung von Anrufern zurückzugeben verwendet, um die Registrierung aufzuheben.

Wenn Sie wirklich vector (oder deque) verwenden möchten, können Sie den Integralindex in den Vektor/Deque zurückgeben, wenn der Rückruf hinzugefügt wird. Diese Strategie würde natürlich erfordern, dass Sie sicherstellen, dass Indizes auf diese Weise verwendbar sind, um die Position der Funktion in der Sequenz zu identifizieren. Wenn Rückrufe und/oder Abmeldungen selten sind, könnte dies einfach bedeuten, dass keine Spots wiederverwendet werden. Oder Sie könnten eine freie Liste implementieren, um leere Slots wiederzuverwenden. Oder fordern Sie nur leere Slots von den Enden der Sequenz zurück und behalten Sie einen Basisindex-Offset bei, der erhöht wird, wenn Slots von Anfang an zurückgewonnen werden.

Wenn Ihr Callback-Zugriffsmuster keinen wahlfreien Zugriff erfordert, ist es am einfachsten für mich, die Callbacks in einem std::list zu speichern und Raw-Iteratoren zu verwenden, um die Registrierung aufzuheben.

Verwandte Themen