libpromeki main
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
objectbase.h
Go to the documentation of this file.
1
8#pragma once
9
10#include <tuple>
11#include <functional>
13#include <promeki/core/list.h>
14#include <promeki/core/map.h>
15#include <promeki/core/mutex.h>
16#include <promeki/core/util.h>
17#include <promeki/core/logger.h>
18#include <promeki/core/signal.h>
19#include <promeki/core/slot.h>
20
22
23class EventLoop;
24class Event;
25class TimerEvent;
26
27#define PROMEKI_OBJECT(ObjectName, ParentObjectName) \
28 public: \
29 static const MetaInfo &metaInfo() { \
30 static const MetaInfo __metaInfo( \
31 typeid(ObjectName).name(), \
32 &ParentObjectName::metaInfo() \
33 ); \
34 return __metaInfo; \
35 }
36
37#define PROMEKI_SIGNAL(SIGNALNAME, ...) \
38 static constexpr const char *SIGNALNAME##SignalName = PROMEKI_STRINGIFY(SIGNALNAME) "(" PROMEKI_STRINGIFY_ARGS(__VA_ARGS__) ")"; \
39 static inline SignalMeta SIGNALNAME##SignalMeta = SignalMeta(metaInfo(), SIGNALNAME##SignalName); \
40 Signal<__VA_ARGS__> SIGNALNAME##Signal = Signal<__VA_ARGS__>(this, SIGNALNAME##SignalName);
41
42#define PROMEKI_SLOT(SLOTNAME, ...) \
43 static constexpr const char *SLOTNAME##SlotName = PROMEKI_STRINGIFY(SLOTNAME) "(" PROMEKI_STRINGIFY_ARGS(__VA_ARGS__) ")"; \
44 static inline SlotMeta SLOTNAME##SlotMeta = SlotMeta(metaInfo(), SLOTNAME##SlotName); \
45 void SLOTNAME(__VA_ARGS__); \
46 Slot<__VA_ARGS__> SLOTNAME##Slot = Slot<__VA_ARGS__>( \
47 [this](auto&&... args) { this->SLOTNAME(std::forward<decltype(args)>(args)...); }, \
48 this, SLOTNAME##SlotName \
49 ); \
50 int SLOTNAME##SlotID = registerSlot(&SLOTNAME##Slot);
51
52
53PROMEKI_DEBUG(ObjectBase)
54
55class ObjectBase;
56
58
74 friend class ObjectBase;
75 public:
82 ObjectBasePtr(ObjectBase *object = nullptr) : p(object) { link(); }
83
85 ObjectBasePtr(const ObjectBasePtr &object) : p(object.p.load(std::memory_order_relaxed)) { link(); }
86
88 ~ObjectBasePtr() { unlink(); }
89
92 unlink();
93 p.store(object.p.load(std::memory_order_relaxed), std::memory_order_relaxed);
94 link();
95 return *this;
96 }
97
99 bool isValid() const { return p.load(std::memory_order_acquire) != nullptr; }
100
102 ObjectBase *data() { return p.load(std::memory_order_acquire); }
103
105 const ObjectBase *data() const { return p.load(std::memory_order_acquire); }
106
107 private:
108 std::atomic<ObjectBase *> p{nullptr};
109
110 void link();
111 void unlink();
112};
113
114
130 friend class ObjectBasePtr;
131 friend class EventLoop;
132 public:
133 class SignalMeta;
134 class SlotMeta;
135
139 class MetaInfo {
140 friend class SignalMeta;
141 friend class SlotMeta;
142 public:
150 MetaInfo(const char *n, const MetaInfo *p = nullptr) : _parent(p), _name(n) {}
151
153 const MetaInfo *parent() const { return _parent; }
154
156 const char *name() const;
157
159 const SignalList &signalList() const { return _signalList; }
160
162 const SlotList &slotList() const { return _slotList; }
163
165 void dumpToLog() const;
166
167 private:
168 const MetaInfo *_parent;
169 const char *_name;
170 mutable String _demangledName;
171 mutable SignalList _signalList;
172 mutable SlotList _slotList;
173 };
174
177 public:
183 SignalMeta(const MetaInfo &m, const char *n) :
184 _meta(m), _name(n)
185 {
186 m._signalList += this;
187 }
188
190 const char *name() const { return _name; }
191 private:
192 const MetaInfo &_meta;
193 const char *_name;
194 };
195
197 class SlotMeta {
198 public:
204 SlotMeta(const MetaInfo &m, const char *n) :
205 _meta(m), _name(n)
206 {
207 m._slotList += this;
208 }
209
211 const char *name() const { return _name; }
212 private:
213 const MetaInfo &_meta;
214 const char *_name;
215 };
216
218 static const MetaInfo &metaInfo() {
219 static const MetaInfo __metaInfo(
220 typeid(ObjectBase).name());
221 return __metaInfo;
222 }
223
228 template <typename... Args> static void connect(Signal<Args...> *signal, Slot<Args...> *slot);
229
230
232 using SlotVariantFunc = std::function<void(const VariantList &)>;
233
243 ObjectBase(ObjectBase *p = nullptr);
244
245
247 virtual ~ObjectBase() {
248 aboutToDestroySignal.emit(this);
249 setParent(nullptr);
250 destroyChildren();
251 runCleanup();
252 }
253
259 return _parent;
260 }
261
269 if(_parent != nullptr) _parent->removeChild(this);
270 _parent = p;
271 if(_parent != nullptr) _parent->addChild(this);
272 return;
273 }
274
279 const ObjectBaseList &childList() const {
280 return _childList;
281 }
282
289 template <typename... Args> int registerSlot(Slot<Args...> *slot) {
290 int ret = _slotList.size();
291 slot->setID(ret);
292 _slotList += SlotItem(
293 ret,
294 slot->prototype(),
295 [slot](const VariantList &args) { slot->exec(args); }
296 );
297 return ret;
298 }
299
308 PROMEKI_SIGNAL(aboutToDestroy, ObjectBase *);
309
314 EventLoop *eventLoop() const { return _eventLoop; }
315
326
337 int startTimer(unsigned int intervalMs, bool singleShot = false);
338
343 void stopTimer(int timerId);
344
345 protected:
347 ObjectBase *signalSender() { return _signalSender; }
348
357 virtual void event(Event *e);
358
367 virtual void timerEvent(TimerEvent *e);
368
369
370 private:
371 using CleanupFunc = std::function<void(ObjectBase *)>;
372
373 struct SlotItem {
374 int id;
375 const char *prototype;
376 SlotVariantFunc variantFunc;
377 };
378
379 struct Cleanup {
380 ObjectBasePtr object;
381 CleanupFunc func;
382 };
383
384 ObjectBase *_parent = nullptr;
385 ObjectBase *_signalSender = nullptr;
386 EventLoop *_eventLoop = nullptr;
387 ObjectBaseList _childList;
388 List<SlotItem> _slotList;
389 mutable Mutex _pointerMapMutex;
391 List<Cleanup> _cleanupList;
392
393 void setEventLoopRecursive(EventLoop *loop);
394
395 void addChild(ObjectBase *c) {
396 _childList += c;
397 return;
398 }
399
400 void removeChild(ObjectBase *c) {
401 _childList.removeFirst(c);
402 return;
403 }
404
405 void destroyChildren() {
406 for(auto child : _childList) {
407 child->_parent = nullptr;
408 delete child;
409 }
410 _childList.clear();
411 return;
412 }
413
414 void runCleanup() {
415 // Null out any ObjectBasePtr's that are currently pointing
416 // to this object. Hold the mutex so concurrent unlink()
417 // on another thread won't modify _pointerMap mid-iteration.
418 {
419 Mutex::Locker lock(_pointerMapMutex);
420 for(auto item : _pointerMap) {
421 item.first->p.store(nullptr, std::memory_order_release);
422 }
423 _pointerMap.clear();
424 }
425
426 // Walk down the cleanup list and run any cleanup functions
427 for(auto &item : _cleanupList) {
428 if(!item.object.isValid()) continue;
429 item.func(this);
430 }
431 _cleanupList.clear();
432 return;
433 }
434};
435
436inline void ObjectBasePtr::link() {
437 ObjectBase *obj = p.load(std::memory_order_relaxed);
438 if(obj != nullptr) {
439 Mutex::Locker lock(obj->_pointerMapMutex);
440 obj->_pointerMap[this] = this;
441 }
442 return;
443}
444
445inline void ObjectBasePtr::unlink() {
446 ObjectBase *obj = p.exchange(nullptr, std::memory_order_acq_rel);
447 if(obj != nullptr) {
448 Mutex::Locker lock(obj->_pointerMapMutex);
449 auto it = obj->_pointerMap.find(this);
450 if(it != obj->_pointerMap.end()) {
451 obj->_pointerMap.remove(it);
452 }
453 }
454}
455
457
458// connect() template requires EventLoop to be complete for cross-thread dispatch.
460
462
463template <typename... Args>
465 if(signal == nullptr || slot == nullptr) return;
466
467 signal->connect([signal, slot](Args... args) {
468 ObjectBase *signalObject = static_cast<ObjectBase *>(signal->owner());
469 ObjectBase *slotObject = static_cast<ObjectBase *>(slot->owner());
470
471 EventLoop *slotLoop = slotObject->_eventLoop;
472 EventLoop *currentLoop = EventLoop::current();
473
474 // Same thread or no event loop: direct call (zero overhead path)
475 if(slotLoop == nullptr || slotLoop == currentLoop) {
476 ObjectBase *prevSender = slotObject->_signalSender;
477 slotObject->_signalSender = signalObject;
478 slot->exec(args...);
479 slotObject->_signalSender = prevSender;
480 } else {
481 // Cross-thread: marshal via VariantList and post
482 VariantList packed = Signal<Args...>::pack(args...);
483 ObjectBasePtr senderTracker(signalObject);
484 slotLoop->postCallable(
485 [slot, slotObject, packed = std::move(packed),
486 senderTracker = std::move(senderTracker)]() {
487 ObjectBase *prevSender = slotObject->_signalSender;
488 slotObject->_signalSender =
489 senderTracker.isValid()
490 ? const_cast<ObjectBase *>(senderTracker.data())
491 : nullptr;
492 slot->exec(packed);
493 slotObject->_signalSender = prevSender;
494 }
495 );
496 }
497 }, slot->owner());
498
499 // Register a cleanup
500 ObjectBase *signalObject = static_cast<ObjectBase *>(signal->owner());
501 ObjectBase *slotObject = static_cast<ObjectBase *>(slot->owner());
502 slotObject->_cleanupList += Cleanup(
504 [signal](ObjectBase *ptr){ signal->disconnectFromObject(ptr); }
505 );
506}
507
509
Per-thread event loop providing event dispatch, timers, and posted callables.
Definition eventloop.h:58
Base class for the event system.
Definition event.h:29
Iterator remove(ConstIterator pos)
Removes the element at the given iterator position.
Definition list.h:402
size_t size() const noexcept
Returns the number of elements in the list.
Definition list.h:301
bool removeFirst(const T &value)
Removes the first instance of value from the list.
Definition list.h:439
void clear() noexcept
Removes all elements from the list.
Definition list.h:330
Iterator end() noexcept
Returns a mutable iterator to one past the last element.
Definition list.h:198
RAII scoped locker for Mutex.
Definition mutex.h:41
Mutual exclusion lock wrapping std::mutex.
Definition mutex.h:33
Object that holds a pointer to an ObjectBase (or derived) object. This class will register itself wit...
Definition objectbase.h:73
ObjectBasePtr & operator=(const ObjectBasePtr &object)
Copy assignment operator. Re-links to the new ObjectBase.
Definition objectbase.h:91
bool isValid() const
Returns true if the tracked pointer is not null.
Definition objectbase.h:99
~ObjectBasePtr()
Destructor. Unlinks from the tracked ObjectBase.
Definition objectbase.h:88
const ObjectBase * data() const
Returns a const pointer to the tracked ObjectBase.
Definition objectbase.h:105
ObjectBasePtr(const ObjectBasePtr &object)
Copy constructor. Tracks the same ObjectBase as the source.
Definition objectbase.h:85
ObjectBase * data()
Returns a mutable pointer to the tracked ObjectBase.
Definition objectbase.h:102
Captures all the metadata about this object.
Definition objectbase.h:139
const MetaInfo * parent() const
Returns the parent MetaInfo, or nullptr if this is the root.
Definition objectbase.h:153
const char * name() const
Returns the demangled type name.
const SlotList & slotList() const
Returns the list of slots registered for this type.
Definition objectbase.h:162
void dumpToLog() const
Dumps all MetaInfo fields to the log output.
const SignalList & signalList() const
Returns the list of signals registered for this type.
Definition objectbase.h:159
MetaInfo(const char *n, const MetaInfo *p=nullptr)
Constructs MetaInfo with a type name and optional parent.
Definition objectbase.h:150
Metadata entry describing a signal on an ObjectBase-derived class.
Definition objectbase.h:176
SignalMeta(const MetaInfo &m, const char *n)
Constructs a SignalMeta and registers it with the given MetaInfo.
Definition objectbase.h:183
const char * name() const
Returns the signal prototype string.
Definition objectbase.h:190
Metadata entry describing a slot on an ObjectBase-derived class.
Definition objectbase.h:197
const char * name() const
Returns the slot prototype string.
Definition objectbase.h:211
SlotMeta(const MetaInfo &m, const char *n)
Constructs a SlotMeta and registers it with the given MetaInfo.
Definition objectbase.h:204
Base object for promeki.
Definition objectbase.h:129
void stopTimer(int timerId)
Stops a timer previously started with startTimer().
void moveToThread(EventLoop *loop)
Changes the EventLoop affinity of this object.
void setParent(ObjectBase *p)
Sets the parent of this object. If the object already has a parent, it will be removed as a child fro...
Definition objectbase.h:268
int startTimer(unsigned int intervalMs, bool singleShot=false)
Starts a timer on this object's EventLoop.
virtual void timerEvent(TimerEvent *e)
Called when a timer fires for this object.
EventLoop * eventLoop() const
Returns the EventLoop this object is affiliated with.
Definition objectbase.h:314
int registerSlot(Slot< Args... > *slot)
Registers a slot with this object and assigns it an ID.
Definition objectbase.h:289
static const MetaInfo & metaInfo()
Returns the MetaInfo for the ObjectBase class.
Definition objectbase.h:218
virtual ~ObjectBase()
Destructor. Emits aboutToDestroy, detaches from parent, and destroys children.
Definition objectbase.h:247
static void connect(Signal< Args... > *signal, Slot< Args... > *slot)
connects a signal and slot together. This function assumes both the signal and slot exist in a Object...
virtual void event(Event *e)
Called by EventLoop to deliver events to this object.
std::function< void(const VariantList &)> SlotVariantFunc
Function type for invoking a slot with a list of Variants.
Definition objectbase.h:232
const ObjectBaseList & childList() const
Returns a list of children of this object.
Definition objectbase.h:279
ObjectBase * signalSender()
Returns the ObjectBase that emitted the signal currently being handled.
Definition objectbase.h:347
ObjectBase(ObjectBase *p=nullptr)
Default ObjectBase constructor.
ObjectBase * parent() const
Returns the parent object, if one. nullptr if none.
Definition objectbase.h:258
void emit(Args... args) const
Emits this signal.
Definition signal.h:170
Encoding-aware string class with copy-on-write semantics.
Definition string.h:35
Event delivered when a timer fires.
Definition timerevent.h:22
ObjectBasePtr(ObjectBase *object=nullptr)
Definition objectbase.h:82
#define PROMEKI_NAMESPACE_BEGIN
Starts a promeki namespace block.
Definition namespace.h:14
#define PROMEKI_NAMESPACE_END
Ends a promeki namespace block.
Definition namespace.h:19