1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html 2 3 #ifndef SPECTMORPH_SIGNAL_HH 4 #define SPECTMORPH_SIGNAL_HH 5 6 #include "smutils.hh" 7 #include <assert.h> 8 #include <functional> 9 #include <vector> 10 #include <list> 11 12 namespace SpectMorph 13 { 14 15 template<class... Args> 16 class Signal; 17 18 struct SignalBase 19 { 20 static uint64 next_signal_idSpectMorph::SignalBase21 next_signal_id() 22 { 23 static uint64 next_id = 1; 24 25 return next_id++; 26 } 27 virtual void disconnect_impl (uint64 id) = 0; 28 virtual ~SignalBaseSpectMorph::SignalBase29 ~SignalBase() 30 { 31 } 32 }; 33 34 class SignalReceiver 35 { 36 struct SignalSource 37 { 38 SignalBase *signal; 39 uint64 id; 40 }; 41 struct SignalReceiverData 42 { 43 int ref_count = 1; 44 45 SignalReceiverData * refSpectMorph::SignalReceiver::SignalReceiverData46 ref() 47 { 48 assert (ref_count > 0); 49 ref_count++; 50 return this; 51 } 52 void unrefSpectMorph::SignalReceiver::SignalReceiverData53 unref (bool cleanup) 54 { 55 assert (ref_count > 0); 56 ref_count--; 57 58 if (cleanup && ref_count == 1) /* ensure nobody is iterating over the data */ 59 { 60 sources.remove_if ([](SignalSource& signal_source) -> bool 61 { 62 return signal_source.id == 0; 63 }); 64 } 65 else if (ref_count == 0) 66 delete this; 67 } 68 std::list<SignalSource> sources; 69 }; 70 struct SignalReceiverData *signal_receiver_data; 71 72 public: 73 template<class... Args, class CbFunction> 74 uint64 connect(Signal<Args...> & signal,const CbFunction & callback)75 connect (Signal<Args...>& signal, const CbFunction& callback) 76 { 77 assert (signal_receiver_data); 78 79 SignalReceiverData *data = signal_receiver_data->ref(); 80 81 auto id = signal.connect_impl (this, callback); 82 data->sources.push_back ({ &signal, id }); 83 data->unref (true); 84 85 return id; 86 } 87 template<class... Args, class Instance, class Method> 88 uint64 connect(Signal<Args...> & signal,Instance * instance,const Method & method)89 connect (Signal<Args...>& signal, Instance *instance, const Method& method) 90 { 91 return SignalReceiver::connect (signal, [instance, method](Args&&... args) 92 { 93 (instance->*method) (std::forward<Args>(args)...); 94 }); 95 } 96 void disconnect(uint64 id)97 disconnect (uint64 id) 98 { 99 assert (signal_receiver_data); 100 101 SignalReceiverData *data = signal_receiver_data->ref(); 102 103 for (auto& signal_source : data->sources) 104 { 105 if (signal_source.id == id) 106 { 107 signal_source.signal->disconnect_impl (id); 108 signal_source.id = 0; 109 } 110 } 111 data->unref (true); 112 } SignalReceiver()113 SignalReceiver() : 114 signal_receiver_data (new SignalReceiverData()) 115 { 116 } 117 virtual ~SignalReceiver()118 ~SignalReceiver() 119 { 120 assert (signal_receiver_data); 121 122 for (auto& signal_source : signal_receiver_data->sources) 123 { 124 if (signal_source.id) 125 { 126 signal_source.signal->disconnect_impl (signal_source.id); 127 signal_source.id = 0; 128 } 129 } 130 signal_receiver_data->unref (false); 131 signal_receiver_data = nullptr; 132 } 133 void dead_signal(uint64 id)134 dead_signal (uint64 id) 135 { 136 SignalReceiverData *data = signal_receiver_data->ref(); 137 138 for (auto& signal_source : data->sources) 139 { 140 if (signal_source.id == id) 141 signal_source.id = 0; 142 } 143 144 data->unref (true); 145 } 146 }; 147 148 template<class... Args> 149 class Signal : public SignalBase 150 { 151 typedef std::function<void (Args...)> CbFunction; 152 153 struct Connection 154 { 155 CbFunction func; 156 uint64 id; 157 SignalReceiver *receiver; 158 }; 159 struct Data 160 { 161 int ref_count = 1; 162 163 Data * refSpectMorph::Signal::Data164 ref() 165 { 166 assert (ref_count > 0); 167 ref_count++; 168 169 return this; 170 } 171 void unrefSpectMorph::Signal::Data172 unref (bool cleanup) 173 { 174 assert (ref_count > 0); 175 ref_count--; 176 177 if (cleanup && ref_count == 1) /* ensure nobody is iterating over the data */ 178 { 179 connections.remove_if ([](Connection& conn) -> bool 180 { 181 return conn.id == 0; 182 }); 183 } 184 else if (ref_count == 0) 185 delete this; 186 } 187 188 std::list<Connection> connections; 189 }; 190 Data *signal_data; 191 public: 192 uint64 connect_impl(SignalReceiver * receiver,const CbFunction & callback)193 connect_impl (SignalReceiver *receiver, const CbFunction& callback) 194 { 195 assert (signal_data); 196 197 Data *data = signal_data->ref(); 198 uint64 id = next_signal_id(); 199 data->connections.push_back ({callback, id, receiver}); 200 data->unref (true); 201 202 return id; 203 } 204 void disconnect_impl(uint64 id)205 disconnect_impl (uint64 id) override 206 { 207 assert (signal_data); 208 209 Data *data = signal_data->ref(); 210 for (auto& conn : data->connections) 211 { 212 if (conn.id == id) 213 conn.id = 0; 214 } 215 data->unref (true); 216 } 217 void operator ()(Args...args)218 operator()(Args... args) 219 { 220 assert (signal_data); 221 222 Data *data = signal_data->ref(); 223 224 for (auto& conn : data->connections) 225 { 226 if (conn.id) 227 conn.func (args...); 228 } 229 230 data->unref (true); 231 } Signal()232 Signal() : 233 signal_data (new Data()) 234 { 235 } ~Signal()236 ~Signal() 237 { 238 assert (signal_data); 239 240 for (auto& conn : signal_data->connections) 241 { 242 if (conn.id) 243 { 244 conn.receiver->dead_signal (conn.id); 245 conn.id = 0; 246 } 247 } 248 249 signal_data->unref (false); 250 signal_data = nullptr; 251 } 252 }; 253 254 } 255 256 #endif 257