Skip to content

Instantly share code, notes, and snippets.

@Fiona-J-W
Created February 5, 2019 12:59
Show Gist options
  • Save Fiona-J-W/9d0da283feeda4a84a4281e61e6fb486 to your computer and use it in GitHub Desktop.
Save Fiona-J-W/9d0da283feeda4a84a4281e61e6fb486 to your computer and use it in GitHub Desktop.
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <string>
#include <unordered_map>
#include <vector>
template <typename KeyType, KeyType Key, typename Listener>
struct event_t {
using self = event_t<KeyType, Key, Listener>;
using listener_type = std::function<Listener>;
listener_type listener;
};
// Get's the Key of an event_t
template <typename T>
struct get_key_t {};
template <typename KeyType, KeyType Key, typename Listeners>
struct get_key_t<event_t<KeyType, Key, Listeners>> {
static const auto value = Key;
};
template <typename T>
const auto get_key = get_key_t<T>::value;
// Selects the event_t that has Key as key
template <typename KeyType, KeyType Key, typename Head, typename... Tail>
struct select_event_type_t {
static_assert(sizeof...(Tail) > 0u, "No such Event");
using type = typename select_event_type_t<KeyType, Key, Tail...>::type;
};
template <typename KeyType, KeyType Key, typename Fun, typename... Tail>
struct select_event_type_t<KeyType, Key, event_t<KeyType, Key, Fun>, Tail...> {
using type = event_t<KeyType, Key, Fun>;
};
template <typename KeyType, KeyType Key, typename... Events>
using select_event_type = typename select_event_type_t<KeyType, Key, Events...>::type;
// The actual listener
template <typename KeyType, typename... Events>
class event_listener {
private:
template <KeyType Event>
struct storage {
using event_type = select_event_type<KeyType, Event, Events...>;
using listener_type = typename event_type::listener_type;
std::unordered_map<unsigned, listener_type> listeners;
};
template <KeyType Event>
auto& get_listeners() {
return std::get<storage<Event>>(m_events).listeners;
}
template <KeyType Event>
const auto& get_listeners() const {
return std::get<storage<Event>>(m_events).listeners;
}
public:
template <KeyType Event>
unsigned subscribe(typename storage<Event>::listener_type listener) {
const auto listener_id = m_next_id++;
get_listeners<Event>().insert({listener_id, std::move(listener)});
return listener_id;
}
template <KeyType Event, typename... Args>
void execute(const Args&... args) const {
for (const auto& [id, listener] : get_listeners<Event>()) {
std::invoke(listener, args...);
}
}
template <KeyType Event>
void unsubscribe(unsigned id) {
get_listeners<Event>().erase(id);
}
private:
std::tuple<storage<get_key<Events>>...> m_events;
unsigned m_next_id = 0u;
};
///////////////////////////////////////////////////////
// Usage:
enum class event_types { foo, bar, baz, unused };
template <event_types E, typename Listener>
using event = event_t<event_types, E, Listener>;
int main() {
auto el = event_listener<event_types, event<event_types::foo, void(int)>,
event<event_types::bar, void()>,
event<event_types::baz, void(int, double)>>{};
el.subscribe<event_types::foo>([](int i) { std::cout << "foo(" << i << ")\n"; });
el.subscribe<event_types::bar>([] { std::cout << "bar1()\n"; });
const auto id_bar2 = el.subscribe<event_types::bar>([] { std::cout << "bar2()\n"; });
el.execute<event_types::foo>(23);
el.execute<event_types::bar>();
el.execute<event_types::baz>(23, 42.0);
std::cout << '\n';
el.subscribe<event_types::baz>(
[](double d, int i) { std::cout << "baz(" << d << ", " << i << ")\n"; });
el.execute<event_types::foo>(23);
el.execute<event_types::bar>();
el.execute<event_types::baz>(23, 42.0);
std::cout << '\n';
el.unsubscribe<event_types::bar>(id_bar2);
el.execute<event_types::foo>(23);
el.execute<event_types::bar>();
el.execute<event_types::baz>(23, 42.0);
std::cout << '\n';
el.subscribe<event_types::bar>([]() { std::cout << "unused\n"; });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment