Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #pragma once
- #include <algorithm>
- #include <functional>
- #include <tuple>
- #include <utility>
- #include <vector>
- template<typename... Args>
- class Event {
- public:
- using cb_t = std::function<void(Args...)>;
- struct event_t {
- cb_t cb{};
- std::tuple<Args...> args;
- event_t(const cb_t& cb, Args... args) : cb(cb), args(std::forward_as_tuple<Args...>(std::decay_t<Args>(args)...)) { }
- bool operator==(const event_t& rhs) {
- //Test pointer then signature
- return (this->cb.target_type().hash_code() == rhs.cb.target_type().hash_code())
- || (this->args == rhs.args);
- }
- };
- Event() = default;
- ~Event() = default;
- void Subscribe(const cb_t& cb, Args... args) {
- event_t sub(cb, args...);
- Subscribe(sub);
- }
- void Unsubscribe(const cb_t& cb) {
- _subscribers.erase(
- std::remove_if(
- std::begin(_subscribers),
- std::end(_subscribers),
- [&cb](const event_t& e) {
- return (cb.target_type().hash_code() == e.cb.target_type().hash_code());
- }),
- std::end(_subscribers));
- }
- void Trigger() const {
- for(auto& s : _subscribers) {
- std::apply(s.cb, s.args); //REQUIRES C++17
- }
- }
- protected:
- private:
- std::vector<event_t> _subscribers{};
- void Subscribe(const event_t& cb) {
- auto found = std::find(std::begin(_subscribers), std::end(_subscribers), cb);
- if(found == std::end(_subscribers)) {
- _subscribers.push_back(cb);
- }
- }
- void Unsubscribe(const event_t& cb) {
- auto found = std::find(std::begin(_subscribers), std::end(_subscribers), cb);
- if(found != std::end(_subscribers)) {
- std::iter_swap(found, _subscribers.end() - 1);
- _subscribers.pop_back();
- }
- }
- };
- //Specialization to get Event<void> to work
- template<>
- class Event<void> : public Event<> { };
- #include "Event.hpp"
- #include <iostream>
- #include <string>
- Event<> OnThing1; //Event signature takes no arguments
- Event<void> OnThing2; //Ditto
- Event<std::string> OnThing3; //Event signature takes a std::string
- Event<std::string, int> OnThing4; //Event signature takes a std::string and an int
- //...
- void MyCallbackFunction1() { std::cout << "Hi from MCF1!n"; }
- void MyCallbackFunction2() { std::cout << "Hi from MCF2!n"; }
- void MyCallbackFunction3(const std::string& str) { std::cout << "MCF3 says " << str << "!n"; }
- void MyCallbackFunction4(const std::string& str, int i) { std::cout << "MCF4 says " << str << " and " << i << "!n"; }
- int main() {
- OnThing1.Subscribe(MyCallbackFunction1);
- OnThing1.Subscribe(MyCallbackFunction2); //Uh-oh! Same signature as MCF1, does not get subscribed!
- OnThing2.Subscribe(MyCallbackFunction2); //Okay because it's on a different Event
- OnThing3.Subscribe(MyCallbackFunction3, std::string("Hello World"));
- OnThing3.Subscribe([](const std::string& str){ std::cout << str << 'n'; }, std::string("Hello Lambda")); //Can be Subscribed because the internal representation of the signature is not the same.
- OnThing4.Subscribe(MyCallbackFunction4, std::string("Hello World"), 1);
- //...
- OnThing1.Trigger(); //Calls MCF1 but not MCF2
- OnThing2.Trigger(); //Calls MCF2
- OnThing3.Trigger(); //Calls MCF3 and the lambda
- OnThing4.Trigger(); //Calls MCF4
- //...
- OnThing1.Unsubscribe(MyCallbackFunction1);
- OnThing1.Unsubscribe(MyCallbackFunction2); //MCF2 was never registered in the first place.
- OnThing2.Unsubscribe(MyCallbackFunction2);
- OnThing3.Unsubscribe(MyCallbackFunction3);
- //Lambda can not be unsubscribed because there is no way to get the type information. Re-creating the lambda will result in a different signature.
- OnThing4.Unsubscribe(MyCallbackFunction4);
- }
- Hi from MCF1!
- Hi from MCF2!
- MCF3 says Hello World!
- Hello Lambda
- MCF4 says Hello World and 1
Add Comment
Please, Sign In to add comment