#include <iostream> | |
#include <map> | |
#include <algorithm> | |
#include <functional> | |
#include <memory> | |
using namespace std; | |
/*** | |
* A base class for event arguments | |
*/ | |
class EventArgs { | |
public: | |
virtual ~EventArgs() {} | |
}; | |
/*** | |
* An event argument that is a string | |
*/ | |
class StringEventArgs : public EventArgs { | |
string payload_; | |
public: | |
explicit StringEventArgs(const string& payload) : payload_(payload) {} | |
const string& Payload() const { return payload_; } | |
}; | |
/*** | |
* An event | |
*/ | |
class Event { | |
class Callback { | |
void* pSender_; | |
const EventArgs& args_; | |
public: | |
Callback(void* pSender, const EventArgs& args) : pSender_(pSender), args_(args) {} | |
void operator()(pair<long, function<void(void*, const EventArgs&)>> p) const { | |
p.second(pSender_, args_); | |
} | |
}; | |
map<long, function<void(void*, const EventArgs&)>> callbacks_; | |
long token_ = 0; | |
public: | |
void operator()(void* pSender, const EventArgs& args) const { | |
for_each(callbacks_.begin(), callbacks_.end(), Callback(pSender, args)); | |
} | |
long Subscribe(function<void(void*, const EventArgs&)> f) { | |
token_++; | |
callbacks_.insert(make_pair(token_, f)); | |
return token_; | |
} | |
void Unsubscribe(long token) { | |
callbacks_.erase(token); | |
} | |
}; | |
/** | |
* A publisher | |
*/ | |
class Publisher { | |
Event event_; | |
string name_; | |
public: | |
// default constructor | |
explicit Publisher(const string& name) : name_(name) {} | |
// getter that gives the name of the publisher | |
const string& Name() const { return name_; } | |
// publish a new message | |
void Publish(const string& message) { | |
event_(this, StringEventArgs(message)); | |
} | |
// register a method to be called when an event is published | |
long Register(function<void(void*, const EventArgs&)> f) { | |
return event_.Subscribe(f); | |
} | |
// unregister a method given the token that was returned when it subscribed | |
void Unregister(long token) { | |
event_.Unsubscribe(token); | |
} | |
}; | |
/*** | |
* A subscriber | |
*/ | |
class Subscriber { | |
string name_; | |
public: | |
explicit Subscriber(const string& name) : name_(name) {} | |
void OnEventReceived(void* pSender, const EventArgs& args) { | |
const StringEventArgs* const s = dynamic_cast<const StringEventArgs* const>(&args); | |
if (s == nullptr) | |
return; | |
if (pSender == nullptr) | |
return; | |
Publisher* p = static_cast<Publisher*>(pSender); | |
cout << name_.c_str() << " has received " << s->Payload().c_str() << " from " << p->Name().c_str() << endl; | |
} | |
}; | |
namespace { | |
using namespace std::placeholders; | |
// make it easy for subscribers to subscribe to a publisher | |
long Subscribe(Publisher& publisher, Subscriber& subscriber) { | |
return publisher.Register(bind(&Subscriber::OnEventReceived, &subscriber, _1, _2)); | |
} | |
void Unsubscribe(Publisher& publisher, long token) { | |
publisher.Unregister(token); | |
} | |
} | |
/** | |
* A subscription | |
*/ | |
class Subscription { | |
Publisher& publisher_; | |
long token_; | |
public: | |
Subscription(Publisher& publisher, long token) : publisher_(publisher), token_(token) { | |
} | |
~Subscription() { | |
publisher_.Unregister(token_); | |
} | |
}; | |
int main() { | |
// create a publisher | |
Publisher prod("Publisher"); | |
// create 2 subscribers | |
Subscriber sub_1("Subscriber-1"); | |
Subscriber sub_2("Subscriber-2"); | |
// have sub_1 subscribe to the publisher's events | |
long token_1 = Subscribe(prod, sub_1); | |
// have sub_2 subscribe to the publisher's events | |
long token_2 = Subscribe(prod, sub_2); | |
// publish an event, sub_1 and sub_2 will receive the event | |
prod.Publish("42"); | |
Unsubscribe(prod, token_1); | |
// now only sub_2 will receive the event, as sub_1 unsubscribed | |
prod.Publish("43"); | |
Unsubscribe(prod, token_2); | |
// now noone will receive the event, as there are no subscribers | |
prod.Publish("44"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment