Branch data Line data Source code
1 : : /* 2 : : ** Copyright (C) 2020 Sylvain Fargier 3 : : ** 4 : : ** This software is provided 'as-is', without any express or implied 5 : : ** warranty. In no event will the authors be held liable for any damages 6 : : ** arising from the use of this software. 7 : : ** 8 : : ** Permission is granted to anyone to use this software for any purpose, 9 : : ** including commercial applications, and to alter it and redistribute it 10 : : ** freely, subject to the following restrictions: 11 : : ** 12 : : ** 1. The origin of this software must not be misrepresented; you must not 13 : : ** claim that you wrote the original software. If you use this software 14 : : ** in a product, an acknowledgment in the product documentation would be 15 : : ** appreciated but is not required. 16 : : ** 2. Altered source versions must be plainly marked as such, and must not be 17 : : ** misrepresented as being the original software. 18 : : ** 3. This notice may not be removed or altered from any source distribution. 19 : : ** 20 : : ** Created on: 2021-11-20T16:42:00+01:00 21 : : ** Author: Sylvain Fargier <fargie_s> <fargier.sylvain@gmail.com> 22 : : ** 23 : : */ 24 : : 25 : : #ifndef SIGNAL_HPP__ 26 : : #define SIGNAL_HPP__ 27 : : 28 : : #include <atomic> 29 : : #include <condition_variable> 30 : : #include <functional> 31 : : #include <list> 32 : : #include <mutex> 33 : : #include <set> 34 : : #include <thread> 35 : : #include <vector> 36 : : 37 : : namespace ccut { 38 : : /** 39 : : * @addtogroup Signal Signal: signal/slot processing framework 40 : : * @{ 41 : : */ 42 : : 43 : : class SignalData; 44 : : class Connection; 45 : : 46 : : /** 47 : : * @brief generic part of a listener 48 : : * @details specialized part is defined in `Signal::Listener` 49 : : */ 50 : : struct BaseListener 51 : : { 52 : : BaseListener(); 53 : : 54 : 2956708 : inline operator bool() const { return active; } 55 : : std::atomic_bool active; 56 : : }; 57 : : 58 : : /** 59 : : * @brief Signal class 60 : : * @ingroup Signal 61 : : */ 62 : : template<typename... Args> 63 : : class Signal 64 : : { 65 : : public: 66 : : Signal(); 67 : : 68 : : /** 69 : : * @details copy signals to duplicate its cache and optimize parallel signal 70 : : * emission from multiple-threads. 71 : : */ 72 : 1 : Signal(const Signal &) = default; 73 : : Signal &operator=(const Signal &) = default; 74 : : 75 : : /** 76 : : * @brief emit the signal 77 : : */ 78 : : void operator()(Args... args); 79 : : 80 : : /** 81 : : * @brief connect a new method 82 : : * @param fun callback to connect 83 : : * @return a connection object 84 : : * @details signal is serviced until the Connection object is destroyed 85 : : */ 86 : : Connection connect(std::function<void(Args...)> fun); 87 : : 88 : : /** 89 : : * @brief connect a new method 90 : : * @param[out] conn created connection pointer 91 : : * @param[in] fun 92 : : * @details this method should be used if Connection is accessed from 93 : : * signal handler 94 : : */ 95 : : Connection &connect(Connection &conn, std::function<void(Args...)> fun); 96 : : 97 : : /** 98 : : * @brief disconnects all listeners 99 : : * @details called in signal's destructor 100 : : * @details this method is re-entrant 101 : : */ 102 : : void disconnect(bool wait = true); 103 : : 104 : : struct Listener : public BaseListener 105 : : { 106 : : explicit Listener(std::function<void(Args...)> fun); 107 : : Listener(const Listener &other) = default; 108 : : 109 : : std::function<void(Args...)> call; 110 : : }; 111 : : 112 : : protected: 113 : : std::vector<std::weak_ptr<BaseListener>> m_cache; 114 : : std::shared_ptr<SignalData> m_data; 115 : : }; 116 : : 117 : : /** 118 : : * @brief signal connection class 119 : : * @ingroup Signal 120 : : */ 121 : : class Connection 122 : : { 123 : : public: 124 : : Connection(); 125 : : Connection(const Connection &) = delete; 126 : : Connection &operator=(const Connection &) = delete; 127 : : #if __GNUC__ <= 4 128 : : /* gcc 4.8.5 is buggy, won't swap the weak pointers */ 129 : : Connection(Connection &&other) 130 : : { 131 : : m_signal.swap(other.m_signal); 132 : : m_listener.swap(other.m_listener); 133 : : } 134 : : 135 : : Connection &operator=(Connection &&other) 136 : : { 137 : : if (&other != this) 138 : : { 139 : : disconnect(); 140 : : m_signal.swap(other.m_signal); 141 : : m_listener.swap(other.m_listener); 142 : : } 143 : : return *this; 144 : : } 145 : : #else 146 : 2023 : Connection(Connection &&) = default; 147 : 1002 : Connection &operator=(Connection &&) = default; 148 : : #endif 149 : : 150 : : ~Connection(); 151 : : 152 : : /** 153 : : * @brief swap connection with another 154 : : */ 155 : : void swap(Connection &other); 156 : : 157 : : /** 158 : : * @brief disconnect the signal 159 : : * @param wait wait for any ongoing handlers to terminate 160 : : * @details this method can be called from a handler (even with wait=true) 161 : : * @details this method is re-entrant but only first caller can actually 162 : : * wait (others will not wait). 163 : : */ 164 : : void disconnect(bool wait = true); 165 : : 166 : : /** 167 : : * @brief check if the associated signal is connected 168 : : * @return true if notifications can be received 169 : : */ 170 : : bool isConnected() const; 171 : : 172 : : protected: 173 : : template<typename... A> 174 : : friend class Signal; 175 : : Connection(const std::shared_ptr<SignalData> &sig, 176 : : const std::shared_ptr<BaseListener> &listener); 177 : : 178 : : std::weak_ptr<SignalData> m_signal; 179 : : std::weak_ptr<BaseListener> m_listener; 180 : : }; 181 : : 182 : : /** @} */ 183 : : } // namespace ccut 184 : : 185 : : #include "Signal.hxx" 186 : : 187 : : #endif