Last active
January 27, 2022 07:24
-
-
Save aspschn/8bd3a307009e7beea5081e8f1cbde13e to your computer and use it in GitHub Desktop.
This file contains hidden or 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 <stdio.h> | |
| #include <functional> | |
| #include <type_traits> | |
| #include <vector> | |
| class ConnectionBase; | |
| class SignalBase | |
| { | |
| public: | |
| virtual void disconnect(ConnectionBase& connection) | |
| { | |
| } | |
| }; | |
| class ConnectionBase | |
| { | |
| public: | |
| virtual SignalBase* signal() | |
| { | |
| return nullptr; | |
| } | |
| private: | |
| virtual void disconnect() | |
| { | |
| } | |
| // virtual bool operator==(const ConnectionBase& foo); | |
| }; | |
| template <typename... Args> | |
| class Signal: public SignalBase | |
| { | |
| public: | |
| class Connection: public ConnectionBase | |
| { | |
| friend Signal; | |
| public: | |
| Connection() | |
| { | |
| this->_signal = nullptr; | |
| } | |
| Connection(Signal<Args...> *signal, std::function<void(Args...)>& method) | |
| { | |
| this->_signal = signal; | |
| this->_method = method; | |
| } | |
| SignalBase* signal() | |
| { | |
| return this->_signal; | |
| } | |
| bool operator==(const ConnectionBase& other) | |
| { | |
| return false; | |
| } | |
| private: | |
| void call(Args... args) | |
| { | |
| this->_method(args...); | |
| } | |
| void disconnect() | |
| { | |
| this->_signal = nullptr; | |
| } | |
| private: | |
| Signal<Args...> *_signal; | |
| std::function<void(Args...)> _method; | |
| }; | |
| public: | |
| void emit(Args... args) | |
| { | |
| for (int i = 0; i < this->_connections.size(); ++i) { | |
| this->_connections[i].call(args...); | |
| } | |
| } | |
| template <typename T, typename Child> | |
| Connection connect(T *receiver, void(Child::* slot)(Args...)) | |
| { | |
| auto f = [receiver, slot](Args... args) { | |
| (receiver->*slot)(std::forward<Args>(args)...); | |
| }; | |
| // this->_connections.push_back(f); | |
| // this->_connections.push_back(Connection(this, f)); | |
| return this->connect(f); | |
| } | |
| Connection connect(std::function<void(Args...)> slot) | |
| { | |
| // this->_connections.push_back(slot); | |
| auto connection = Connection(this, slot); | |
| this->_connections.push_back(connection); | |
| return connection; | |
| } | |
| void disconnect(Connection &connection) | |
| { | |
| auto begin = this->_connections.begin(); | |
| auto end = this->_connections.end(); | |
| for (auto it = begin; it != end; ++it) { | |
| if (*it == connection) { | |
| connection.disconnect(); | |
| this->_connections.erase(it); | |
| break; | |
| } | |
| } | |
| } | |
| private: | |
| // std::vector<std::function<void(Args...)>> _connections; | |
| std::vector<Connection> _connections; | |
| }; | |
| class Widget | |
| { | |
| public: | |
| template <typename... Args, typename SignalType, typename Functor> | |
| static auto connect(SignalType signal, | |
| Widget *receiver, Functor method) | |
| { | |
| return signal->connect(method); | |
| } | |
| template <typename... Args, typename SignalType, typename Child> | |
| static auto connect(SignalType signal, | |
| Child *receiver, void (Child::* method)(Args...)) | |
| { | |
| static_assert(std::is_base_of<Widget, Child>::value == true, "TEST"); | |
| return signal->connect(receiver, method); | |
| } | |
| template <typename... Args> | |
| //static void disconnect(typename Signal<Args...>::Connection& connection) | |
| static void disconnect(ConnectionBase& connection) | |
| { | |
| // connection.disconnect(); | |
| connection.signal()->disconnect(connection); | |
| } | |
| }; | |
| class Button : public Widget | |
| { | |
| public: | |
| Signal<int> click; | |
| public: | |
| void conn() | |
| { | |
| Widget::connect(&this->click, this, [](int a) { | |
| printf("A lambda slot: %d\n", a); | |
| }); | |
| Widget::connect(&this->click, this, &Button::test_slot); | |
| } | |
| void test_slot(int a) | |
| { | |
| printf("A test slot: %d\n", a); | |
| } | |
| void simulate_click() | |
| { | |
| this->click.emit(42); | |
| } | |
| void disconn() | |
| { | |
| Widget::disconnect(this->click_connection2); | |
| } | |
| private: | |
| decltype(click)::Connection click_connection1; | |
| decltype(click)::Connection click_connection2; | |
| }; | |
| class PushButton : public Button | |
| { | |
| public: | |
| Signal<int> click; | |
| public: | |
| void conn() | |
| { | |
| Widget::connect(&this->click, this, [](int b) { | |
| printf("PushButton lambda slot: %d\n", b); | |
| }); | |
| } | |
| }; | |
| int main(int argc, char *argv[]) | |
| { | |
| Button button = Button(); | |
| button.conn(); | |
| button.simulate_click(); | |
| PushButton push_button = PushButton(); | |
| button.conn(); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment