Created
June 17, 2018 19:06
-
-
Save minus7/1674a597b58660b0057b31681a4eec66 to your computer and use it in GitHub Desktop.
EventDispatcher
This file contains 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 <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