Skip to content

Instantly share code, notes, and snippets.

@minus7
Created June 17, 2018 19:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save minus7/1674a597b58660b0057b31681a4eec66 to your computer and use it in GitHub Desktop.
Save minus7/1674a597b58660b0057b31681a4eec66 to your computer and use it in GitHub Desktop.
EventDispatcher
#include <string>
#include <tuple>
#include <functional>
#include <vector>
#include <map>
#include <boost/hana.hpp>
#include <memory>
#include <iostream>
#include <queue>
#include <utility>
namespace hana = boost::hana;
// Dispatcher
//////////////
template<typename EventMap>
class Dispatcher {
public:
using EventDispatcher = std::function<void(void *)>;
using EventFreeer = std::function<void(void *)>;
template<typename... Handlers>
Dispatcher(EventMap eventMap, std::shared_ptr<Handlers>... handlers)
:_eventMap{eventMap} {
// For each available event type, filter handlers to those that handle the specific event and register them
// If no handler is available for any event, fail an assertion
// Then create a dispatcher lambda for each type and handler
auto has_handler = hana::is_valid([](auto &&obj, const auto &event) -> decltype(obj->handleEvent(event)) {});
hana::for_each(eventMap, [&](auto eventMapEntry) {
auto eventType = hana::first(eventMapEntry);
using EventType = typename decltype(eventType)::type;
auto withHandler = hana::filter(hana::make_tuple(handlers...), [&](auto h) {
return has_handler(h, hana::traits::declval(eventType));
});
BOOST_HANA_CONSTANT_ASSERT_MSG(hana::length(withHandler) != hana::size_c < 0 >, "No handlers");
// Create event dispatch stubs
hana::for_each(withHandler, [&](auto &h) {
_dispatchers[hana::second(eventMapEntry)].emplace_back([&h](void *event) {
h->handleEvent(*static_cast<const EventType *>(event));
});
});
// Create event freeing stubs
_freeers.emplace(hana::second(eventMapEntry), [](void *event) {
delete static_cast<const EventType *>(event);
});
});
}
template<typename EventType>
void dispatch(EventType&& event) {
using RealEventType = typename std::remove_reference<EventType>::type;
auto eventTypeId = hana::value_of(_eventMap[hana::type_c < RealEventType > ]);
void *stored = new RealEventType(std::forward<EventType>(event));
_queue.emplace(eventTypeId, stored);
}
//private:
void dispatchFromQueue() {
auto entry = _queue.front();
for (auto &handler : _dispatchers[entry.first]) {
handler(entry.second);
}
_freeers[entry.first](entry.second);
_queue.pop();
}
private:
EventMap _eventMap;
std::map<int, std::vector<EventDispatcher>> _dispatchers;
std::map<int, EventFreeer> _freeers;
std::queue<std::pair<int, void *>> _queue;
};
struct TestEvent1 {
TestEvent1(TestEvent1&& e) : msg(std::move(e.msg)) {
std::cout << "Moving TestEvent1" << std::endl;
}
TestEvent1(const TestEvent1&) = default;
TestEvent1(const std::string& msg) : msg{msg} {};
~TestEvent1() {
std::cout << "Destroying TestEvent1 " << (msg.empty() ? "(empty)" : "(full)") << std::endl;
}
std::string msg;
};
struct TestEvent2 {
int i;
};
struct TestEvent3 {
};
enum EventTypeId {
TestEvent1Id = 1,
TestEvent2Id = 2,
TestEvent3Id = 3,
};
auto CoreEventMap = hana::make_map(
hana::make_pair(hana::type_c<TestEvent1>, hana::int_c<TestEvent1Id>),
hana::make_pair(hana::type_c<TestEvent2>, hana::int_c<TestEvent2Id>)/*,
hana::make_pair(hana::type_c<TestEvent3>, hana::int_c<TestEvent3Id>)*/
);
using CoreDispatcher = Dispatcher<decltype(CoreEventMap)>;
class TestEventListener {
public:
TestEventListener() = delete; // no default constructor
TestEventListener(const TestEventListener &) = delete; // no copy constructor
TestEventListener(int i) {}
void handleEvent(const TestEvent1 &event) {
std::cout << "TestEvent1: " << event.msg << std::endl;
}
void handleEvent(const TestEvent2 &event) {
std::cout << "TestEvent2: " << event.i << std::endl;
}
void handleEvent(const TestEvent3 &event) {
std::cout << "TestEvent3" << std::endl;
}
};
int main() {
auto tel1 = std::make_shared<TestEventListener>(1);
auto tel2 = std::make_shared<TestEventListener>(2);
CoreDispatcher d{CoreEventMap, tel1, tel2};
d.dispatch(TestEvent1{"test"});
d.dispatch(TestEvent2{5});
d.dispatchFromQueue();
d.dispatchFromQueue();
// d.dispatch(TestEvent3{}); // should fail to compile because no handler exists or event type isn't registered
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment