Skip to content

Instantly share code, notes, and snippets.

@jay-tux
Created May 19, 2021 15:53
Show Gist options
  • Save jay-tux/ab547ad6750a34f68ccc9f030ff1c6e5 to your computer and use it in GitHub Desktop.
Save jay-tux/ab547ad6750a34f68ccc9f030ff1c6e5 to your computer and use it in GitHub Desktop.
A simple, customizable, easy-to-use and subscribable event type in C++
#ifndef EVENT_H
#define EVENT_H
#include <vector>
#include <functional>
namespace jay {
//event class. Supports adding handlers and invoking them
template<class ... types>
class Event {
public:
//the cheap callback type; a raw function pointer
typedef void (*cheapCall)(types ...);
//the expensive callback type; an std::function object
typedef std::function<void(types ...)> expCall;
//creates a new event
Event() : cheapCalls{std::vector<cheapCall>()}, expCalls{std::vector<expCall>()} {}
//adds a cheap callback to this event
void add(const cheapCall callback) { cheapCalls.push_back(callback); }
//adds an expensive callback to this event
void add(const expCall callback) { expCalls.push_back(callback); }
//invokes the event, calling all handlers
void invoke(types ... args) {
for(auto callback : cheapCalls) callback(args...);
for(auto callback : expCalls) callback(args...);
}
//invokes the event using the operator
Event &operator()(types ... args) { invoke(args...); return *this; }
private:
std::vector<cheapCall> cheapCalls;
std::vector<expCall> expCalls;
};
//event exposing wrapper. Keeps actual event hidden from implementers
template<class ... types>
class ExposedEvent {
public:
//the event type
typedef Event<types ...> eventType;
//creates a new exposed event
ExposedEvent(eventType &event) : internal{event} {}
//adds a cheap callback to the underlying event
ExposedEvent &operator+=(const typename eventType::cheapCall callback) {
internal.add(callback);
return *this;
}
//adds an expensive callback to the underlying event
ExposedEvent &operator+=(const typename eventType::expCall callback) {
internal.add(callback);
return *this;
}
private:
Event<types...> &internal;
};
}
#endif
@jay-tux
Copy link
Author

jay-tux commented May 19, 2021

In a typical OOP situation (where you'd want to hide the invocation from subscribers), you'll make a private field for the Event and then expose it to the outside world (and subscribers) through a public field containing a matching ExposedEvent:

class EventUser {
  public:
    ExposedEvent<int, double, std::string> imAnEvent = ExposedEvent<int, double, std::string>(imAnInternalEvent);
  private:
    Event<int, double, std::string> imAnInternalEvent;
};

Subscribers can't fire the event, only the class itself can, but they can still subscribe like so:

void callback(int i, double d, std::string s) {
  // do something with i, d and s
}

class SomeSubscriber {
  public:
    SomeSubscriber(EventUser user) { user.imAnEvent += callback; }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment