C ++ callback using class member

I know this has been asked so many times, and because of this, it is difficult for him to break through the crackle and find a simple example of what works.

I have this, it is simple and works for 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 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); }; 

How can this be rewritten, so EventHandler::addHandler() will work with both MyClass and YourClass . Sorry, but this is how my brain works. I need to see a simple example of what works before I can understand why and how it works. If you have a favorite way of doing this work now in time to show it, please mark up this code and send it back.

[edit]

It was answered, but the answer was deleted before I can tick. The answer in my case was a boilerplate function. Changed addHandler for this ...

 class EventHandler { public: template<typename T> void addHandler(T* owner) { cout << "Handler added..." << endl; //Let pretend an event just occured owner->Callback(owner,1); } }; 
+59
c ++ function callback member
Jan 07 '13 at 3:24
source share
6 answers

Instead of static methods and walking around a pointer to an instance of a class, you can use the functionality in the new C ++ 11 standard: std::function and std::bind :

 #include <functional> class EventHandler { public: void addHandler(std::function<void(int)> callback) { cout << "Handler added..." << endl; // Let pretend an event just occured callback(1); } }; 

Now the addHandler method accepts the argument std::function , and this "function object" has no return value and takes an integer as an argument.

To bind it to a specific function, you use 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; } 

You need to use std::bind when adding a handler, since you explicitly need to specify the implicit this pointer as an argument. If you have a standalone function, you do not need to use std::bind :

 void freeStandingCallback(int x) { // ... } int main() { // ... handler->addHandler(freeStandingCallback); } 

Having an event handler uses std::function objects, and also allows you to use the new C ++ 11 lambda script functions :

 handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; }); 
+132
Jan 07 '13 at 3:41
source share

Here is a short version that works with class method callbacks and regular function callbacks. In this example, to show how parameters are processed, the callback function takes two parameters: bool and int .

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

This limits the code specific to C ++ 11 to the addCallback method and personal data in the Caller class. For me, at least this minimizes the likelihood of errors in its implementation.

+3
Apr 08 '17 at 23:02 on
source share

What you want to do is create an interface that processes this code, and all your classes implement the interface.

 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 { 

Please note that the Callback feature is not static for this, which in my opinion is an improvement. If you want it to be static, you need to do it as JaredC suggests with templates.

+2
Jan 07 '13 at 3:29
source share

MyClass and YourClass can be obtained from SomeonesClass , which has an abstract (virtual) Callback method. Your addHandler will accept objects like SomeonesClass and MyClass and YourClass can override Callback to provide their concrete implementation of callback behavior.

+1
Jan 07 '13 at 3:30
source share

Full working example from the code above .... for 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 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 

The output should be:

 Compiler:201103 Handler added... Result:6 
0
Oct 11 '18 at 21:32
source share

If you have callbacks with different parameters, you can use templates as follows:
// compile with: 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 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(); } 
-one
Oct 09 '14 at 15:36
source share



All Articles