Sé que esto se ha preguntado tantas veces, y por eso es difícil profundizar y encontrar un ejemplo simple de lo que funciona.
Tengo esto, es simple y funciona para MyClass
...
#include <iostream>
using std::cout;
using std::endl;
class MyClass
{
public:
MyClass();
static void Callback(MyClass* instance, int x);
private:
int private_x;
};
class EventHandler
{
public:
void addHandler(MyClass* owner)
{
cout << "Handler added..." << endl;
//Let's pretend an event just occured
owner->Callback(owner,1);
}
};
EventHandler* handler;
MyClass::MyClass()
{
private_x = 5;
handler->addHandler(this);
}
void MyClass::Callback(MyClass* instance, int x)
{
cout << x + instance->private_x << endl;
}
int main(int argc, char** argv)
{
handler = new EventHandler();
MyClass* myClass = new MyClass();
}
class YourClass
{
public:
YourClass();
static void Callback(YourClass* instance, int x);
};
¿Cómo se puede reescribir para EventHandler::addHandler()
que funcione con ambos MyClass
y YourClass
. Lo siento, pero así es como funciona mi cerebro, necesito ver un ejemplo simple de lo que funciona antes de poder comprender por qué / cómo funciona. Si tiene una forma favorita de hacer que esto funcione, ahora es el momento de mostrarlo, marque ese código y publíquelo.
[editar]
Se respondió, pero la respuesta se eliminó antes de que pudiera marcar. La respuesta en mi caso fue una función basada en plantilla. Se cambió addHandler a esto ...
class EventHandler
{
public:
template<typename T>
void addHandler(T* owner)
{
cout << "Handler added..." << endl;
//Let's pretend an event just occured
owner->Callback(owner,1);
}
};
Respuestas:
En lugar de tener métodos estáticos y pasar un puntero a la instancia de la clase, puede usar la funcionalidad en el nuevo estándar C ++ 11:
std::function
ystd::bind
:#include <functional> class EventHandler { public: void addHandler(std::function<void(int)> callback) { cout << "Handler added..." << endl; // Let's pretend an event just occured callback(1); } };
El
addHandler
método ahora acepta unstd::function
argumento, y este "objeto de función" no tiene valor de retorno y toma un número entero como argumento.Para vincularlo a una función específica, usa
std::bind
:class MyClass { public: MyClass(); // Note: No longer marked `static`, and only takes the actual argument void Callback(int x); private: int private_x; }; MyClass::MyClass() { using namespace std::placeholders; // for `_1` private_x = 5; handler->addHandler(std::bind(&MyClass::Callback, this, _1)); } void MyClass::Callback(int x) { // No longer needs an explicit `instance` argument, // as `this` is set up properly cout << x + private_x << endl; }
Debe usarlo
std::bind
al agregar el controlador, ya que necesita especificar explícitamente lo implícitothis
puntero como argumento. Si tiene una función independiente, no tiene que usarstd::bind
:void freeStandingCallback(int x) { // ... } int main() { // ... handler->addHandler(freeStandingCallback); }
El hecho de que el controlador de eventos utilice
std::function
objetos también permite utilizar las nuevas funciones lambda de C ++ 11 :handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
fuente
std::bind
solo devuelve un objeto (no especificado), y cuando haya terminado con él, puede dejarlo fuera de alcance. Si el objeto enlazado se destruye e intenta llamar a la función, obtendrá un comportamiento indefinido .handler->addHandler()
, significa que en algún lugar creas un objetoEventHandler
? Buena respuesta por cierto, +1....., _1, _2)
y así sucesivamente.Aquí hay una versión concisa que funciona con devoluciones de llamada de método de clase y con devoluciones de llamada de función regulares. En este ejemplo, para mostrar cómo se manejan los parámetros, la función de devolución de llamada toma dos parámetros:
bool
yint
.class Caller { template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int)) { using namespace std::placeholders; callbacks_.emplace_back(std::bind(mf, object, _1, _2)); } void addCallback(void(* const fun)(bool,int)) { callbacks_.emplace_back(fun); } void callCallbacks(bool firstval, int secondval) { for (const auto& cb : callbacks_) cb(firstval, secondval); } private: std::vector<std::function<void(bool,int)>> callbacks_; } class Callee { void MyFunction(bool,int); } //then, somewhere in Callee, to add the callback, given a pointer to Caller `ptr` ptr->addCallback(this, &Callee::MyFunction); //or to add a call back to a regular function ptr->addCallback(&MyRegularFunction);
Esto restringe el código específico de C ++ 11 al método addCallback y los datos privados en la clase Caller. Para mí, al menos, esto minimiza la posibilidad de cometer errores al implementarlo.
fuente
Lo que quiere hacer es crear una interfaz que maneje este código y todas sus clases implementen la interfaz.
class IEventListener{ public: void OnEvent(int x) = 0; // renamed Callback to OnEvent removed the instance, you can add it back if you want. }; class MyClass :public IEventListener { ... void OnEvent(int x); //typically such a function is NOT static. This wont work if it is static. }; class YourClass :public IEventListener {
Tenga en cuenta que para que esto funcione, la función "Devolución de llamada" no es estática, lo que creo que es una mejora. Si desea que sea estático, debe hacerlo como sugiere JaredC con plantillas.
fuente
Un ejemplo de trabajo completo del código anterior ... para C ++ 11:
#include <stdlib.h> #include <stdio.h> #include <functional> #if __cplusplus <= 199711L #error This file needs at least a C++11 compliant compiler, try using: #error $ g++ -std=c++11 .. #endif using namespace std; class EventHandler { public: void addHandler(std::function<void(int)> callback) { printf("\nHandler added..."); // Let's pretend an event just occured callback(1); } }; class MyClass { public: MyClass(int); // Note: No longer marked `static`, and only takes the actual argument void Callback(int x); private: EventHandler *pHandler; int private_x; }; MyClass::MyClass(int value) { using namespace std::placeholders; // for `_1` pHandler = new EventHandler(); private_x = value; pHandler->addHandler(std::bind(&MyClass::Callback, this, _1)); } void MyClass::Callback(int x) { // No longer needs an explicit `instance` argument, // as `this` is set up properly printf("\nResult:%d\n\n", (x+private_x)); } // Main method int main(int argc, char const *argv[]) { printf("\nCompiler:%ld\n", __cplusplus); new MyClass(5); return 0; } // where $1 is your .cpp file name... this is the command used: // g++ -std=c++11 -Wall -o $1 $1.cpp // chmod 700 $1 // ./$1
La salida debe ser:
Compiler:201103 Handler added... Result:6
fuente
MyClass
yYourClass
ambos podrían derivarse de loSomeonesClass
que tiene unCallback
método abstracto (virtual) . SuaddHandler
aceptarían objetos del tipoSomeonesClass
yMyClass
yYourClass
puede anularCallback
para proporcionar su aplicación específica del comportamiento de devolución de llamada.fuente
Si tiene devoluciones de llamada con diferentes parámetros, puede usar plantillas de la siguiente manera:
// compilar con: g ++ -std = c ++ 11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbacksApp
#include <functional> // c++11 #include <iostream> // due to: cout using std::cout; using std::endl; class MyClass { public: MyClass(); static void Callback(MyClass* instance, int x); private: int private_x; }; class OtherClass { public: OtherClass(); static void Callback(OtherClass* instance, std::string str); private: std::string private_str; }; class EventHandler { public: template<typename T, class T2> void addHandler(T* owner, T2 arg2) { cout << "\nHandler added..." << endl; //Let's pretend an event just occured owner->Callback(owner, arg2); } }; MyClass::MyClass() { EventHandler* handler; private_x = 4; handler->addHandler(this, private_x); } OtherClass::OtherClass() { EventHandler* handler; private_str = "moh "; handler->addHandler(this, private_str ); } void MyClass::Callback(MyClass* instance, int x) { cout << " MyClass::Callback(MyClass* instance, int x) ==> " << 6 + x + instance->private_x << endl; } void OtherClass::Callback(OtherClass* instance, std::string private_str) { cout << " OtherClass::Callback(OtherClass* instance, std::string private_str) ==> " << " Hello " << instance->private_str << endl; } int main(int argc, char** argv) { EventHandler* handler; handler = new EventHandler(); MyClass* myClass = new MyClass(); OtherClass* myOtherClass = new OtherClass(); }
fuente
YourClass
. Parece que eliminó esa clase y agregó una diferenteOtherClass
. Además, la pregunta ya tiene una respuesta bien recibida. ¿En qué medida es mejor su solución para que valga la pena publicarla?