Получение системы событий для работы с функциями-членами - PullRequest
0 голосов
/ 24 июня 2018

Я написал систему событий. Есть несколько случаев, когда это не работает, и я просто не могу найти способ заставить их работать. Он отлично работает для любой не лямбда-функции, не являющейся членом, то есть только для одной свободной функции.

Я бы хотел, чтобы все отлично работало.

В настоящее время только одна функция с любой идентичной подписью может быть подписана, несколько было бы неплохо. Лямбда считает "разными" сигнатурами, поэтому вы можете иметь свободную функцию и лямбду, подписанную как, но лямбда не может быть отписана, потому что target_types отличается от ожидаемого.

Просто функции-члены не компилируются, потому что подпись void __thiscall ClassName::*FuncName([signature]) не преобразуется в std::function<void([signature])>.

Убедитесь, что ваш компилятор настроен на включение функций C ++ 17

Event.hpp

#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"));
    //A lambda can be Subscribed because the internal representation of the signature is not the same.
    OnThing3.Subscribe([](const std::string& str){ std::cout << str << '\n'; }, std::string("Hello Lambda"));
    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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...