Reputation: 1162
I'm not sure how to look for this online... I think they might be called something different in C++
I want to have a simple event system, somthing like
event myCustomEvent;
myCustomEvent.subscribe( void myHandler(string) );
myCustomEvent.fire("a custom argument");
// myHandler prints out the string passed in the first argument
event myNewCustomEvent;
myNewCustomEvent.subscribe( void myNewHandler(int) );
myNewCustomEvent.fire(10);
// myHandler prints 10
I can do this pretty easily with a simple class -- but when i want to have an event that passes a different type or amount of arguments to the subscriber i have to write, and define an entirely new event class.. I figure there has to be some library, or maybe even something native in Visual C++ 2008 that will work something similar to this. It's basicly just an implementation of the Observer pattern, so it can't be too impossible to do in C++
This really makes me appreciate how nice it is in JavaScript not to have to worry about the arguments you are passing.
Tell me if this is a stupid question.
Upvotes: 24
Views: 50457
Reputation: 51
This is a simple C++ delegate implementation I used on an Arduino project. It relies on a smart pointer to allow for a cleaner syntax when attaching event handlers. It supports only one handler but this can easily be extended by implementing some container of EventDelegates.
template<typename TSender, typename TArgument>
class EventDelegate {
private:
struct Callable {
virtual ~Callable() {}
virtual void Call(TSender* sender, TArgument* argument) = 0;
};
template<typename TClass>
struct CallableImpl : public Callable {
TClass* _instance;
void (TClass::*_method)(TSender*, TArgument*);
CallableImpl(TClass* instance, void (TClass::*method)(TSender*, TArgument*))
: _instance(instance), _method(method) {}
void Call(TSender* sender, TArgument* argument) {
(_instance->*_method)(sender, argument);
}
};
protected:
RefCountedPtr<Callable> _callable; // smart pointer
public:
template<typename TClass>
EventDelegate(const TClass* instance, void (TClass::*method)(TSender* sender, TArgument* argument))
: _callable(new CallableImpl<TClass>(instance, method)) {}
EventDelegate()
: _callable(0) {}
~EventDelegate() {
}
void operator()(TSender* sender, TArgument* argument) {
if (_callable) {
_callable->Call(sender, argument);
}
}
};
Here is how you can use it
class Button {
public:
typedef EventDelegate<Button, ButtonClickArgs> ClickEvent;
ClickEvent OnClick;
protected:
RaiseClick() {
ButtonClickArgs args(...);
OnClick(this, &args)
}
};
class App {
public:
App() {
// Here the smart pointer ensures that the Callable isn't
// destroyed when the Button::ClickEvent object on the stack
// goes out of scope
g_button->OnClick = Button::ClickEvent(this, &App::ClickHandler)
}
~App() {
g_button->OnClick = Button::ClickEvent();
}
protected:
void ClickHandler(Button* button, ButtonClickArgs* args) {
...
}
};
Upvotes: 1
Reputation:
I use libsigc++. It's native for gtkmm.
A simple example losely adapted from the tutorial:
#include <iostream>
#include <sigc++/sigc++.h>
using namespace std;
class AlienDetector {
public:
void run ();
sigc::signal<void> signal_detected;
};
void warn_people () {
cout << "There are aliens in the carpark!" << endl;
}
void AlienDetector::run () {
signal_detected.emit ();
}
int main () {
AlienDetector mydetector;
mydetector.signal_detected.connect (sigc::ptr_fun (warn_people));
mydetector.run ();
}
It also provides a mechanism to connect member-functions of specific objects to signals using sigc::mem_fun instead of sigc::ptr_fun:
sigc::mem_fun (someobject, &SomeClass::some_method);
This pretty much provides anything that is possible with GLib-signals.
Upvotes: 3
Reputation: 45493
There is a native Visual C++ event system. It's mostly for COM, but it has native C++ support too.
From here:
[event_source(native)]
class CSource {
public:
__event void MyEvent(int nValue);
};
[event_receiver(native)]
class CReceiver {
public:
void MyHandler1(int nValue) {
printf_s("MyHandler1 was called with value %d.\n", nValue);
}
void MyHandler2(int nValue) {
printf_s("MyHandler2 was called with value %d.\n", nValue);
}
void hookEvent(CSource* pSource) {
__hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
__hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
}
void unhookEvent(CSource* pSource) {
__unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
__unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
}
};
int main() {
CSource source;
CReceiver receiver;
receiver.hookEvent(&source);
__raise source.MyEvent(123);
receiver.unhookEvent(&source);
}
Upvotes: 7
Reputation: 10123
Take a look at the boost signal library. Combined with the function and bind libraries, you can do exactly what you are looking for.
Upvotes: 20
Reputation: 44288
The observer pattern from the GOF is pretty much what you want.
In the book, it has C++ code for this...
Also, as always, Boost has stuff you can make use of as well
Upvotes: 9