Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Fun with C++: implementing a pub/sub scenario using std::bind and other standard facilities. The approach is pretty similar to the well known .NET event mechanism.
#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