Nun rufen, MyClass
muss wissen etwas über Foo
, nämlich die Unterschrift Egal welche Methode Sie planen, als Rückruf zur Verwendung; Wie würde es sonst wissen, was als Parameter übergeben werden soll oder welchen Typ erwartet man als Ausgabe? Wenn der Callback-Signatur bekannt und fixiert ist, zum Beispiel int(int,int,int)
wie Sie oben haben, könnten Sie eine Konstruktion wie folgt verwenden:
class MyClass {
std::function<int(int,int,int)> callback;
public:
int run() {
return callback(1,2,3); // or whatever
}
template <typename Class>
void SetCallback (Class& o, int (Class::*m) (int,int,int)) {
callback = [&o,m] (int a, int b, int c) { return (o.*m)(a,b,c); };
}
template <typename Class>
void SetCallback (Class const& o, int (Class::*m) (int,int,int) const) {
callback = [&o,m] (int a, int b, int c) { return (o.*m)(a,b,c); };
}
};
Die obige Umsetzung MyClass
funktioniert wie folgt: callback
ein Funktionsobjekt ist zunächst nicht definiert, Das dauert drei int
s und gibt eine int
zurück. SetCallback
benötigt zwei Parameter: Ein Objekt o
, auf dem der Callback ausgeführt werden soll, und eine Methode m
für dieses Objekt, die der Signatur callback
entspricht. Es ist egal, was der Typ o
ist; Dank Type-Erasure, MyClass
muss nie wissen, was es eigentlich anruft.
Beachten Sie die beiden Versionen SetCallback
- je eine für const
und nicht const
Objekte. In Wirklichkeit sollten Sie auch für volatile
und const volatile
Überladungen schreiben, aber diese sind vergleichsweise viel seltener als const
. Sobald Ausnahmenspezifikationen und Transaktionen Teil des Typsystems werden, müssen wir uns auch um noexcept
und die Synchronisierung kümmern, und die resultierende kombinatorische Explosion von Typen wird schwierig sein, ohne eine sehr clevere Sprachunterstützung effektiv zu bewältigen. Aber dieses Beispiel zeigt Ihnen, wie diese Art von Code geschrieben wird, und es ist wahrscheinlich gut genug für Ihre Zwecke.
Die Implementierung sieht hässlich aus, aber sie bietet tatsächlich eine sehr saubere Schnittstelle; gegeben Foo
wie Sie oben geschrieben haben, würden Sie MyClass
‚s Callback-Funktionalität wie folgt verwenden:
MyClass test;
Foo foo;
foo.d = 4;
test.SetCallback (foo, &Foo::the_func);
int result = test.run(); // result = 10
Der obige Code mit jeder Art arbeiten, die int(int,int,int)
eine Methode mit der Signatur hat. Beachten Sie, dass Sie SetCallback
aufrufen müssen, bevor Sie run
aufrufen, andernfalls erhalten Sie eine std::bad_function_call
Ausnahme, da der Rückruf noch nicht definiert worden ist.
Ich habe eine Problemumgehung durch Erstellen einer virtuellen Klasse erstellt. MyClass weiß davon, Foo implementiert es. Aber ich hätte gerne eine Antwort näher zu dem, was ich gefragt habe –
Wir haben jetzt 'std :: function' und' std :: bind'. –