Skip to content

Instantly share code, notes, and snippets.

@jemand2001
Created March 31, 2025 17:57
Show Gist options
  • Select an option

  • Save jemand2001/177abb29b5a2c73bf30798b273553c8f to your computer and use it in GitHub Desktop.

Select an option

Save jemand2001/177abb29b5a2c73bf30798b273553c8f to your computer and use it in GitHub Desktop.
event handling returning collected results of events
#include "signals.hpp"
#include <iostream>
struct MyEvent {
using Signature = void(int);
};
struct OtherEvent {
using Signature = int(int);
};
class Emitter1 : public EventEmitter<Emitter1, MyEvent> {};
class Emitter2 : public EventEmitter<Emitter2, OtherEvent> {};
template <int N> int add_n(int x) { return x + N; }
int main() {
Emitter1 e;
auto log_number = [](int x) { std::cout << x << '\n'; };
e.add_event_listener<MyEvent>(log_number);
e.emit<MyEvent>(12);
Emitter2 e2;
e2.add_event_listener<OtherEvent>(add_n<0>);
e2.add_event_listener<OtherEvent>(add_n<1>);
e2.add_event_listener<OtherEvent>(add_n<2>);
e2.add_event_listener<OtherEvent>(add_n<3>);
e2.add_event_listener<OtherEvent>(add_n<4>);
auto x = e2.emit<OtherEvent>(1);
for (int i : x) {
std::cout << "e2 returned " << i << '\n';
}
}
#include "traits.hpp"
#include <concepts>
#include <functional>
#include <tuple>
#include <type_traits>
#include <vector>
template <typename Fn, typename... Args>
concept returns_void = std::invocable<Fn, Args...> &&
std::same_as<std::invoke_result_t<Fn, Args...>, void>;
template <typename T>
concept event_type = requires() { typename T::Signature; };
template <typename Me, event_type... Es> class EventEmitter;
template <typename Me> class EventEmitter<Me> {
static_assert(false, "must have at least one associated event!");
};
template <typename Me, event_type E, event_type... Es>
class EventEmitter<Me, E, Es...> {
static_assert(unique<E, Es...>,
"this class can't work if you have multiple identical types "
"of events");
template <typename S> using Fn = std::function<S>;
std::tuple<std::vector<Fn<typename E::Signature>>,
std::vector<Fn<typename Es::Signature>>...>
handlers;
public:
template <one_of<E, Es...> ET>
void add_event_listener(const Fn<typename ET::Signature> &fn) {
std::get<std::vector<Fn<typename ET::Signature>>>(handlers).push_back(fn);
}
template <one_of<E, Es...> ET, typename... Args>
void emit(Args &&...args)
requires(returns_void<typename ET::Signature, Args...>)
{
for (auto &f :
std::get<std::vector<Fn<typename ET::Signature>>>(handlers)) {
f(args...);
}
}
template <one_of<E, Es...> ET, typename... Args>
auto emit(Args &&...args)
requires(!returns_void<typename ET::Signature, Args...>)
{
using T = std::invoke_result_t<typename ET::Signature, Args...>;
std::vector<T> v;
for (auto &f :
std::get<std::vector<Fn<typename ET::Signature>>>(handlers)) {
v.push_back(f(args...));
}
return v;
}
};
#include <concepts>
template <typename T, typename... Ts> struct is_one_of;
template <typename T, typename... Ts>
struct is_one_of<T, T, Ts...> : std::true_type {};
template <typename T> struct is_one_of<T> : std::false_type {};
template <typename T, typename R, typename... Ts>
struct is_one_of<T, R, Ts...> : is_one_of<T, Ts...> {};
template <typename T, typename... Ts>
concept one_of = is_one_of<T, Ts...>::value;
template <typename...> struct is_unique;
template <typename T> struct is_unique<T> : std::true_type {};
template <typename T, typename... Ts> struct is_unique<T, Ts...> {
constexpr static bool value =
(!std::same_as<T, Ts> && ... && is_unique<Ts...>::value);
};
template <typename... Ts>
concept unique = is_unique<Ts...>::value;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment