Robert
Robert

Reputation: 1162

Events in C++

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

Answers (6)

glut
glut

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

Larik Ishkulov
Larik Ishkulov

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

Eclipse
Eclipse

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

Jim Buck
Jim Buck

Reputation: 20726

I use sigslot for exactly this purpose.

Upvotes: 12

Jere.Jones
Jere.Jones

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

Keith Nicholas
Keith Nicholas

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

Related Questions