-
-
Save jmjatlanta/36cb230f93126468783b59301574bc96 to your computer and use it in GitHub Desktop.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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