libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
signal.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_CORE
13#include <tuple>
14#include <functional>
15#include <promeki/atomic.h>
16#include <promeki/function.h>
17#include <promeki/namespace.h>
18#include <promeki/list.h>
19#include <promeki/mutex.h>
20
21PROMEKI_NAMESPACE_BEGIN
22
23class ObjectBase;
24class Variant;
25class VariantList;
26
55template <typename... Args> class Signal {
56 public:
58 using Function = promeki::Function<void(Args...)>;
59
61 template <typename T> struct removeConstAndRef {
62 using type = std::remove_const_t<std::remove_reference_t<T>>;
63 };
64
66 template <typename T> using RemoveConstAndRef = typename removeConstAndRef<T>::type;
67
86 static VariantList pack(Args... args);
87
93 Signal(void *owner = nullptr, const char *prototype = nullptr) : _owner(owner), _prototype(prototype) {}
94
98 void *owner() const { return _owner; }
99
103 const char *prototype() const { return _prototype; }
104
118 size_t connect(Function slot, void *ptr = nullptr) {
119 size_t slotID = nextSlotId();
120 Mutex::Locker lock(_slotsMutex);
121 _slots += Info(slotID, slot, ptr);
122 return slotID;
123 }
124
182 size_t connect(Function slot, ObjectBase *owner);
183
200 template <typename T> size_t connect(T *obj, void (T::*memberFunction)(Args...)) {
201 size_t slotID = nextSlotId();
202 Mutex::Locker lock(_slotsMutex);
203 _slots += Info(slotID,
204 ([obj, memberFunction](Args... args) { (obj->*memberFunction)(args...); }), obj);
205 return slotID;
206 }
207
218 void disconnect(size_t slotID) {
219 Mutex::Locker lock(_slotsMutex);
220 _slots.removeIf([slotID](const Info &info) { return info.id == slotID; });
221 return;
222 }
223
236 template <typename T> void disconnect(const T *object, void (T::*memberFunction)(Args...)) {
237 Mutex::Locker lock(_slotsMutex);
238 _slots.removeIf([object, memberFunction](const Info &info) {
239 return static_cast<const T *>(info.object) == object && info.func == memberFunction;
240 });
241 return;
242 }
243
250 template <typename T> void disconnectFromObject(const T *object) {
251 Mutex::Locker lock(_slotsMutex);
252 _slots.removeIf(
253 [object](const Info &info) { return static_cast<const T *>(info.object) == object; });
254 return;
255 }
256
265 void emit(Args... args) const {
266 // Snapshot under the lock so iteration runs
267 // against a stable copy. Concurrent
268 // connect / disconnect calls only mutate the
269 // live @c _slots — the snapshot is unaffected,
270 // which keeps reentrant connect / disconnect
271 // from a slot safe and matches Boost.Signals2
272 // semantics.
273 List<Info> snapshot;
274 {
275 Mutex::Locker lock(_slotsMutex);
276 snapshot = _slots;
277 }
278 for (const auto &slot : snapshot) slot.func(args...);
279 return;
280 }
281
282
283 private:
284 struct Info {
285 size_t id = 0;
286 Function func;
287 const void *object = nullptr;
288
289 Info(size_t id, Function f, const void *obj = nullptr) : id(id), func(f), object(obj) {}
290 };
291
292 void *_owner = nullptr;
293 const char *_prototype = nullptr;
294 List<Info> _slots;
295 mutable Mutex _slotsMutex;
296
297 // Process-wide monotonic ID source. Kept as a function-local
298 // static (rather than a member) so that adding it does not
299 // change Signal's class layout or ABI — important because
300 // Signal is embedded in every ObjectBase-derived type via
301 // the @c PROMEKI_SIGNAL macro.
302 static size_t nextSlotId() {
303 static Atomic<size_t> counter{0};
304 return ++counter;
305 }
306};
307
308PROMEKI_NAMESPACE_END
309
310#endif // PROMEKI_ENABLE_CORE