//! \~\ingroup Core //! \~\file piobject.h //! \~\brief //! \~english Base object class providing event -> handler mechanism //! \~russian Базовый класс объектов, обеспечивающий механизм событий -> обработчиков /* PIP - Platform Independent Primitives Object, base class of some PIP classes, provide EVENT -> EVENT_HANDLER mechanism Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #ifndef PIOBJECT_H #define PIOBJECT_H #include "piinit.h" #include "pimutex.h" #include "piobject_macros.h" #include "piqueue.h" #include "piset.h" #include "pivariant.h" #include "pivariantsimple.h" //! \~\ingroup Core //! \~\brief //! \~english Base class for objects that declare events, event handlers and registered methods. //! \~russian Базовый класс для объектов, которые объявляют события, обработчики событий и зарегистрированные методы. //! \~\details //! \~english //! PIObject is the base class for all PIP classes that need event-driven communication. //! It provides signal-slot mechanism, property system, and object lifetime management. //! %PIObject stores named properties, keeps connection state and exposes a //! small metaobject table used by \a CONNECTU(), \a execute() and related APIs. //! Queued delivery runs on the performer object and requires explicit draining //! through \a callQueuedEvents() or \a maybeCallQueuedEvents(). //! \~russian //! PIObject является базовым классом для всех классов PIP, которым необходима событийная коммуникация. //! Он обеспечивает механизм сигналов-слотов, систему свойств и управление жизненным циклом объектов. //! %PIObject хранит именованные свойства, состояние соединений и небольшую //! метаобъектную таблицу, которую используют \a CONNECTU(), \a execute() и //! связанные методы. Отложенная доставка выполняется на объекте-исполнителе и //! требует явного опустошения очереди через \a callQueuedEvents() или //! \a maybeCallQueuedEvents(). class PIP_EXPORT PIObject { #ifndef MICRO_PIP friend class PIObjectManager; friend PIP_EXPORT void dumpApplication(bool); friend class PIIntrospection; #endif typedef PIObject __PIObject__; typedef void __Parent__; public: NO_COPY_CLASS(PIObject); //! \~english Constructs an object and initializes its \c name property. //! \~russian Создает объект и инициализирует его свойство \c name. explicit PIObject(const PIString & name = PIString()); //! \~english Destroys the object, raises \a deleted() and disconnects it from the event graph. //! \~russian Уничтожает объект, вызывает \a deleted() и отключает его от событийного графа. virtual ~PIObject(); //! \~\ingroup Core //! \~\brief //! \~english Handle of one connection between a source object and a destination object or functor. //! \~russian Дескриптор одного соединения между объектом-источником и объектом-приемником либо функтором. class PIP_EXPORT Connection { friend class PIObject; Connection(void * sl, void * si, const PIString & e = PIString(), PIObject * s_o = nullptr, PIObject * d_o = nullptr, void * d = nullptr, int ac = 0, PIObject * p = nullptr) { slot = sl; signal = si; event = e; eventID = e.hash(); src_o = s_o; dest_o = d_o; dest = d; args_count = ac; performer = p; functor = 0; } void destroy(); void * slot; void * signal; std::function * functor; PIString event; uint eventID; PIObject *src_o, *dest_o; PIObject * performer; void * dest; int args_count; public: //! \~english Constructs an invalid connection handle. //! \~russian Создает недействительный дескриптор соединения. Connection(); //! \~english Returns \c true when the connection was created successfully. //! \~russian Возвращает \c true, если соединение было успешно создано. bool isValid() const { return signal; } //! \~english Returns the source object that emits the event. //! \~russian Возвращает объект-источник, который испускает событие. PIObject * sourceObject() const { return src_o; } //! \~english Returns the destination object, or \c nullptr for a lambda connection. //! \~russian Возвращает объект-приемник, либо \c nullptr для соединения с лямбда-функцией. PIObject * destinationObject() const { return dest_o; } //! \~english Returns the performer object, or \c nullptr for direct delivery. //! \~russian Возвращает объект-исполнитель, либо \c nullptr для прямой доставки. //! \~\details //! \~english Queued delivery runs only when the performer drains its queue. //! \~russian Отложенная доставка выполняется только когда исполнитель обрабатывает свою очередь. PIObject * performerObject() const { return performer; } //! \~english Disconnects this single connection. //! \~russian Разрывает только это соединение. bool disconnect() const; }; private: uint _signature_; public: //! \~english Returns the \c name property of this object. //! \~russian Возвращает свойство \c name этого объекта. PIString name() const { return property("name").toString(); } //! \~english Returns the registered class name of this object. //! \~russian Возвращает зарегистрированное имя класса этого объекта. virtual const char * className() const { return "PIObject"; } //! \~english Returns the hash of \a className(). //! \~russian Возвращает хэш от \a className(). virtual uint classNameID() const { static uint ret = PIStringAscii("PIObject").hash(); return ret; } static const char * __classNameCC() { return "PIObject"; } static uint __classNameIDS() { static uint ret = PIStringAscii("PIObject").hash(); return ret; } //! \~english Returns the registered parent class name, or an empty string for the root. //! \~russian Возвращает зарегистрированное имя родительского класса, либо пустую строку для корня. virtual const char * parentClassName() const { return ""; } //! \~english Returns whether \a piCoutObj output is enabled for this object. //! \~russian Возвращает, включен ли вывод \a piCoutObj для этого объекта. bool debug() const { return property("debug").toBool(); } //! \~english Sets the \c name property of this object. //! \~russian Устанавливает свойство \c name этого объекта. void setName(const PIString & name) { setProperty("name", name); } //! \~english Enables or disables \a piCoutObj output for this object. //! \~russian Включает или отключает вывод \a piCoutObj для этого объекта. void setDebug(bool debug) { setProperty("debug", debug); } //! \~english Returns the property with name "name". //! \~russian Возвращает свойство объекта по имени "name". PIVariant property(const char * name) const { return properties_.value(piHashData((const uchar *)name, strlen(name))); } //! \~english Sets the property "name" to "value" and creates it if needed. //! \~russian Устанавливает свойство "name" в значение "value" и создаёт его при необходимости. //! \~\details //! \~english Calls \a propertyChanged() after updating the stored value. //! \~russian После обновления сохранённого значения вызывает \a propertyChanged(). void setProperty(const char * name, const PIVariant & value) { properties_[piHashData((const uchar *)name, strlen(name))] = value; propertyChanged(name); } //! \~english Returns whether the property "name" exists. //! \~russian Возвращает, существует ли свойство "name". bool isPropertyExists(const char * name) const { return properties_.contains(piHashData((const uchar *)name, strlen(name))); } //! \~english Enables or disables the internal object mutex during handler execution. //! \~russian Включает или отключает внутренний мьютекс объекта во время выполнения обработчиков. //! \~\details //! \~english This flag affects direct and queued handler invocation for this object, but does not describe full thread-safety of the //! class. //! \~russian Этот флаг влияет на прямой и отложенный вызов обработчиков для данного объекта, но не описывает полную потокобезопасность //! класса. void setThreadSafe(bool yes) { thread_safe_ = yes; } //! \~english Returns whether the internal object mutex is enabled for handler execution. //! \~russian Возвращает, включен ли внутренний мьютекс объекта для выполнения обработчиков. bool isThreadSafe() const { return thread_safe_; } //! \~english Executes a registered method or handler method by name with the supplied arguments. //! \~russian Выполняет зарегистрированный метод или метод-обработчик по имени с переданными аргументами. //! \~\details //! \~english //! This helper works only with the registered-method table built from //! \a EVENT_HANDLER*() and \a EVENT*() declarations. It does not provide //! arbitrary reflection or complex overload resolution: the implementation //! selects a suitable registered method by name and argument count. //! \~russian //! Этот вспомогательный метод работает только с таблицей зарегистрированных //! методов, построенной из объявлений \a EVENT_HANDLER*() и \a EVENT*(). //! Он не предоставляет произвольную рефлексию и сложное разрешение //! перегрузок: реализация выбирает подходящий зарегистрированный метод по //! имени и числу аргументов. bool execute(const PIString & method, const PIVector & vl); //! \~english Overload of \a execute() for a method without arguments. //! \~russian Перегрузка \a execute() для метода без аргументов. bool execute(const PIString & method) { return execute(method, PIVector()); } //! \~english Overload of \a execute() for one argument. //! \~russian Перегрузка \a execute() для одного аргумента. bool execute(const PIString & method, const PIVariantSimple & v0) { return execute(method, PIVector() << v0); } //! \~english Overload of \a execute() for two arguments. //! \~russian Перегрузка \a execute() для двух аргументов. bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return execute(method, PIVector() << v0 << v1); } //! \~english Overload of \a execute() for three arguments. //! \~russian Перегрузка \a execute() для трёх аргументов. bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) { return execute(method, PIVector() << v0 << v1 << v2); } //! \~english Overload of \a execute() for four arguments. //! \~russian Перегрузка \a execute() для четырёх аргументов. bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2, const PIVariantSimple & v3) { return execute(method, PIVector() << v0 << v1 << v2 << v3); } //! \~english Queues execution of a registered method on the performer object. //! \~russian Ставит выполнение зарегистрированного метода в очередь объекта-исполнителя. //! \~\details //! \~english //! Delivery happens only when "performer" later calls \a callQueuedEvents() //! or \a maybeCallQueuedEvents(). Argument values are transported through //! \a PIVariantSimple, so queued arguments should be representable there. //! \~russian //! Доставка происходит только когда "performer" позже вызывает //! \a callQueuedEvents() или \a maybeCallQueuedEvents(). Значения аргументов //! передаются через \a PIVariantSimple, поэтому аргументы очереди должны в //! нём представляться. bool executeQueued(PIObject * performer, const PIString & method, const PIVector & vl); //! \~english Overload of \a executeQueued() for a method without arguments. //! \~russian Перегрузка \a executeQueued() для метода без аргументов. bool executeQueued(PIObject * performer, const PIString & method) { return executeQueued(performer, method, PIVector()); } //! \~english Overload of \a executeQueued() for one argument. //! \~russian Перегрузка \a executeQueued() для одного аргумента. bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0) { return executeQueued(performer, method, PIVector() << v0); } //! \~english Overload of \a executeQueued() for two arguments. //! \~russian Перегрузка \a executeQueued() для двух аргументов. bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return executeQueued(performer, method, PIVector() << v0 << v1); } //! \~english Overload of \a executeQueued() for three arguments. //! \~russian Перегрузка \a executeQueued() для трёх аргументов. bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) { return executeQueued(performer, method, PIVector() << v0 << v1 << v2); } //! \~english Overload of \a executeQueued() for four arguments. //! \~russian Перегрузка \a executeQueued() для четырёх аргументов. bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2, const PIVariantSimple & v3) { return executeQueued(performer, method, PIVector() << v0 << v1 << v2 << v3); } //! \~english Static convenience wrapper for \a execute(). //! \~russian Статическая удобная обёртка над \a execute(). static bool execute(PIObject * o, const PIString & method, const PIVector & vl) { return o->execute(method, vl); } //! \~english Static overload of \a execute() without arguments. //! \~russian Статическая перегрузка \a execute() без аргументов. static bool execute(PIObject * o, const PIString & method) { return execute(o, method, PIVector()); } //! \~english Static overload of \a execute() for one argument. //! \~russian Статическая перегрузка \a execute() для одного аргумента. static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0) { return execute(o, method, PIVector() << v0); } //! \~english Static overload of \a execute() for two arguments. //! \~russian Статическая перегрузка \a execute() для двух аргументов. static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return execute(o, method, PIVector() << v0 << v1); } //! \~english Static overload of \a execute() for three arguments. //! \~russian Статическая перегрузка \a execute() для трёх аргументов. static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) { return execute(o, method, PIVector() << v0 << v1 << v2); } //! \~english Static overload of \a execute() for four arguments. //! \~russian Статическая перегрузка \a execute() для четырёх аргументов. static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2, const PIVariantSimple & v3) { return execute(o, method, PIVector() << v0 << v1 << v2 << v3); } //! \~english Static convenience wrapper for \a executeQueued(). //! \~russian Статическая удобная обёртка над \a executeQueued(). static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVector & vl) { return o->executeQueued(performer, method, vl); } //! \~english Static overload of \a executeQueued() without arguments. //! \~russian Статическая перегрузка \a executeQueued() без аргументов. static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method) { return executeQueued(o, performer, method, PIVector()); } //! \~english Static overload of \a executeQueued() for one argument. //! \~russian Статическая перегрузка \a executeQueued() для одного аргумента. static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0) { return executeQueued(o, performer, method, PIVector() << v0); } //! \~english Static overload of \a executeQueued() for two arguments. //! \~russian Статическая перегрузка \a executeQueued() для двух аргументов. static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return executeQueued(o, performer, method, PIVector() << v0 << v1); } //! \~english Static overload of \a executeQueued() for three arguments. //! \~russian Статическая перегрузка \a executeQueued() для трёх аргументов. static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) { return executeQueued(o, performer, method, PIVector() << v0 << v1 << v2); } //! \~english Static overload of \a executeQueued() for four arguments. //! \~russian Статическая перегрузка \a executeQueued() для четырёх аргументов. static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2, const PIVariantSimple & v3) { return executeQueued(o, performer, method, PIVector() << v0 << v1 << v2 << v3); } //! \~english Dumps object diagnostics to the project output stream. //! \~russian Выводит диагностическую информацию об объекте в проектный поток вывода. void dump(const PIString & line_prefix = PIString()) const; //! \~english Returns the registered inheritance scope of this object, including its own class. //! \~russian Возвращает зарегистрированную цепочку наследования объекта, включая его собственный класс. PIStringList scopeList() const; //! \~english Returns full signatures of all registered event and handler methods for this class scope. //! \~russian Возвращает полные сигнатуры всех зарегистрированных событий и обработчиков для области этого класса. PIStringList methodsEH() const; //! \~english Returns whether a registered event or handler method with this name exists. //! \~russian Возвращает, существует ли зарегистрированное событие или обработчик с таким именем. bool isMethodEHContains(const PIString & name) const; //! \~english Returns the comma-separated argument type list of a registered method. //! \~russian Возвращает список типов аргументов зарегистрированного метода через запятую. PIString methodEHArguments(const PIString & name) const; //! \~english Returns the full registered signature of a method. //! \~russian Возвращает полную зарегистрированную сигнатуру метода. PIString methodEHFullFormat(const PIString & name) const; //! \~english Returns the registered method name for the specified entry-point address. //! \~russian Возвращает имя зарегистрированного метода для указанного адреса точки входа. PIString methodEHFromAddr(const void * addr) const; //! \~english Low-level direct connection helper behind the legacy \c CONNECT* macros. //! \~russian Низкоуровневый помощник прямого соединения, лежащий под устаревшими макросами \c CONNECT*. static PIObject::Connection piConnect(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, void * ev_h, void * e_h, int args, const char * loc); //! \~english Low-level name-based connection helper behind \a CONNECTU() and \a CONNECTU_QUEUED(). //! \~russian Низкоуровневый помощник соединения по имени, лежащий под \a CONNECTU() и \a CONNECTU_QUEUED(). static PIObject::Connection piConnectU(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, const PIString & hname, const char * loc, PIObject * performer = 0); //! \~english Low-level helper that connects an event to a lambda or functor wrapper. //! \~russian Низкоуровневый помощник, который соединяет событие с лямбдой или обёрткой функтора. static PIObject::Connection piConnectLS(PIObject * src, const PIString & sig, std::function * f, const char * loc); template static std::function * __newFunctor(void (*stat_handler)(void *, PITYPES...), PIINPUT functor) { return (std::function *)(new std::function(functor)); } //! \~english Disconnects this source object from a specific destination handler for event "sig". //! \~russian Разрывает соединения этого объекта-источника с конкретным обработчиком объекта-приемника для события "sig". void piDisconnect(const PIString & sig, PIObject * dest, void * ev_h) { piDisconnect(this, sig, dest, ev_h); } //! \~english Disconnects this source object from all connections of event "sig" to destination object "dest". //! \~russian Разрывает все соединения этого объекта-источника от события "sig" к объекту-приемнику "dest". void piDisconnect(const PIString & sig, PIObject * dest) { piDisconnect(this, sig, dest); } //! \~english Disconnects this source object from all connections of event "sig". //! \~russian Разрывает все соединения этого объекта-источника от события "sig". void piDisconnect(const PIString & sig) { piDisconnect(this, sig); } //! \~english Disconnects source object "src" from a specific destination handler for event "sig". //! \~russian Разрывает соединения объекта-источника "src" с конкретным обработчиком объекта-приемника для события "sig". static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h); //! \~english Disconnects source object "src" from all connections of event "sig" to destination object "dest". //! \~russian Разрывает все соединения объекта-источника "src" от события "sig" к объекту-приемнику "dest". static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest); //! \~english Disconnects source object "src" from all connections of event "sig". //! \~russian Разрывает все соединения объекта-источника "src" от события "sig". static void piDisconnect(PIObject * src, const PIString & sig); //! \~english Internal event delivery helper for registered events without arguments. //! \~russian Внутренний помощник доставки для зарегистрированных событий без аргументов. static void raiseEvent(PIObject * sender, const uint eventID) { for (int j = 0; j < sender->connections.size_s(); ++j) { Connection i(sender->connections[j]); if (i.eventID != eventID) continue; if (i.functor) { (*(i.functor))(); } else { if (i.performer) { i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender)); } else { bool ts = sender->thread_safe_; if (ts) i.dest_o->mutex_.lock(); i.dest_o->eventBegin(); sender->eventBegin(); i.dest_o->emitter_ = sender; ((void (*)(void *))i.slot)(i.dest); sender->eventEnd(); if (i.dest_o->isPIObject()) { i.dest_o->emitter_ = 0; if (ts) i.dest_o->mutex_.unlock(); i.dest_o->eventEnd(); } } } if (!sender->isPIObject()) break; } } //! \~english Internal event delivery helper for registered events with one argument. //! \~russian Внутренний помощник доставки для зарегистрированных событий с одним аргументом. template static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0()) { for (int j = 0; j < sender->connections.size_s(); ++j) { Connection i(sender->connections[j]); if (i.eventID != eventID) continue; if (i.functor) { (*((std::function *)i.functor))(v0); } else { if (i.performer) { PIVector vl; if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0); i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl)); } else { bool ts = sender->thread_safe_; if (ts) i.dest_o->mutex_.lock(); i.dest_o->eventBegin(); sender->eventBegin(); i.dest_o->emitter_ = sender; if (i.args_count == 0) ((void (*)(void *))i.slot)(i.dest); else ((void (*)(void *, T0))i.slot)(i.dest, v0); sender->eventEnd(); if (i.dest_o->isPIObject()) { i.dest_o->emitter_ = 0; if (ts) i.dest_o->mutex_.unlock(); i.dest_o->eventEnd(); } } } if (!sender->isPIObject()) break; } } //! \~english Internal event delivery helper for registered events with two arguments. //! \~russian Внутренний помощник доставки для зарегистрированных событий с двумя аргументами. template static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0(), const T1 & v1 = T1()) { for (int j = 0; j < sender->connections.size_s(); ++j) { Connection i(sender->connections[j]); if (i.eventID != eventID) continue; if (i.functor) { (*((std::function *)i.functor))(v0, v1); } else { if (i.performer) { PIVector vl; if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0); if (i.args_count > 1) vl << PIVariantSimple::fromValue(v1); i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl)); } else { bool ts = sender->thread_safe_; if (ts) i.dest_o->mutex_.lock(); i.dest_o->eventBegin(); sender->eventBegin(); i.dest_o->emitter_ = sender; switch (i.args_count) { case 0: ((void (*)(void *))i.slot)(i.dest); break; case 1: ((void (*)(void *, T0))i.slot)(i.dest, v0); break; default: ((void (*)(void *, T0, T1))i.slot)(i.dest, v0, v1); break; } sender->eventEnd(); if (i.dest_o->isPIObject()) { i.dest_o->emitter_ = 0; if (ts) i.dest_o->mutex_.unlock(); i.dest_o->eventEnd(); } } } if (!sender->isPIObject()) break; } } //! \~english Internal event delivery helper for registered events with three arguments. //! \~russian Внутренний помощник доставки для зарегистрированных событий с тремя аргументами. template static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2()) { for (int j = 0; j < sender->connections.size_s(); ++j) { Connection i(sender->connections[j]); if (i.eventID != eventID) continue; if (i.functor) { (*((std::function *)i.functor))(v0, v1, v2); } else { if (i.performer) { PIVector vl; if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0); if (i.args_count > 1) vl << PIVariantSimple::fromValue(v1); if (i.args_count > 2) vl << PIVariantSimple::fromValue(v2); i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl)); } else { bool ts = sender->thread_safe_; if (ts) i.dest_o->mutex_.lock(); i.dest_o->eventBegin(); sender->eventBegin(); i.dest_o->emitter_ = sender; switch (i.args_count) { case 0: ((void (*)(void *))i.slot)(i.dest); break; case 1: ((void (*)(void *, T0))i.slot)(i.dest, v0); break; case 2: ((void (*)(void *, T0, T1))i.slot)(i.dest, v0, v1); break; default: ((void (*)(void *, T0, T1, T2))i.slot)(i.dest, v0, v1, v2); break; } sender->eventEnd(); if (i.dest_o->isPIObject()) { i.dest_o->emitter_ = 0; if (ts) i.dest_o->mutex_.unlock(); i.dest_o->eventEnd(); } } } if (!sender->isPIObject()) break; } } //! \~english Internal event delivery helper for registered events with four arguments. //! \~russian Внутренний помощник доставки для зарегистрированных событий с четырьмя аргументами. template static void raiseEvent(PIObject * sender, const uint eventID, const T0 & v0 = T0(), const T1 & v1 = T1(), const T2 & v2 = T2(), const T3 & v3 = T3()) { for (int j = 0; j < sender->connections.size_s(); ++j) { Connection i(sender->connections[j]); if (i.eventID != eventID) continue; if (i.functor) { (*((std::function *)i.functor))(v0, v1, v2, v3); } else { if (i.performer) { PIVector vl; if (i.args_count > 0) vl << PIVariantSimple::fromValue(v0); if (i.args_count > 1) vl << PIVariantSimple::fromValue(v1); if (i.args_count > 2) vl << PIVariantSimple::fromValue(v2); if (i.args_count > 3) vl << PIVariantSimple::fromValue(v3); i.performer->postQueuedEvent(__QueuedEvent(i.slot, i.dest, i.dest_o, sender, vl)); } else { bool ts = sender->thread_safe_; if (ts) i.dest_o->mutex_.lock(); i.dest_o->eventBegin(); sender->eventBegin(); i.dest_o->emitter_ = sender; switch (i.args_count) { case 0: ((void (*)(void *))i.slot)(i.dest); break; case 1: ((void (*)(void *, T0))i.slot)(i.dest, v0); break; case 2: ((void (*)(void *, T0, T1))i.slot)(i.dest, v0, v1); break; case 3: ((void (*)(void *, T0, T1, T2))i.slot)(i.dest, v0, v1, v2); break; default: ((void (*)(void *, T0, T1, T2, T3))i.slot)(i.dest, v0, v1, v2, v3); break; } sender->eventEnd(); if (i.dest_o->isPIObject()) { i.dest_o->emitter_ = 0; if (ts) i.dest_o->mutex_.unlock(); i.dest_o->eventEnd(); } } } if (!sender->isPIObject()) break; } } //! \~english Returns the first live object with name "name", or \c nullptr. //! \~russian Возвращает первый живой объект с именем "name", либо \c nullptr. static PIObject * findByName(const PIString & name) { PIMutexLocker _ml(mutexObjects()); for (auto * i: PIObject::objects()) { if (i->name() != name) continue; return i; } return nullptr; } //! \~english Returns whether this pointer still refers to a live %PIObject instance. //! \~russian Возвращает, указывает ли этот указатель на ещё существующий экземпляр %PIObject. bool isPIObject() const { return isPIObject(this); } //! \~english Returns whether this object belongs to class "T" or one of its registered descendants. //! \~russian Возвращает, принадлежит ли этот объект классу "T" или одному из его зарегистрированных потомков. template bool isTypeOf() const { if (!isPIObject()) return false; PIMutexLocker ml(__meta_mutex()); return __meta_data()[classNameID()].scope_id.contains(T::__classNameIDS()); } //! \~english Returns this object cast to "T" when \a isTypeOf() succeeds, otherwise \c nullptr. //! \~russian Возвращает этот объект, приведённый к типу "T", если \a isTypeOf() успешно, иначе \c nullptr. template T * cast() const { if (!isTypeOf()) return (T *)nullptr; return (T *)this; } //! \~english Returns whether "o" points to a live %PIObject instance. //! \~russian Возвращает, указывает ли "o" на ещё существующий экземпляр %PIObject. static bool isPIObject(const PIObject * o); //! \~english Overload of \a isPIObject() for an untyped pointer. //! \~russian Перегрузка \a isPIObject() для нетипизированного указателя. static bool isPIObject(const void * o) { return isPIObject((PIObject *)o); } //! \~english Returns whether "o" belongs to class "T" or one of its registered descendants. //! \~russian Возвращает, принадлежит ли "o" классу "T" или одному из его зарегистрированных потомков. template static bool isTypeOf(const PIObject * o) { return o->isTypeOf(); } //! \~english Overload of \a isTypeOf() for an untyped pointer. //! \~russian Перегрузка \a isTypeOf() для нетипизированного указателя. template static bool isTypeOf(const void * o) { return isTypeOf((PIObject *)o); } //! \~english Simplifies a C++ type spelling for registered-method metadata. //! \~russian Упрощает запись типа C++ для метаданных зарегистрированных методов. static PIString simplifyType(const char * a, bool readable = true); struct PIP_EXPORT __MetaFunc { __MetaFunc(); bool isNull() const { return addr == nullptr; } int argumentsCount() const; PIString arguments() const; PIString fullFormat() const; void __setFuncName(const char * n); void __addArgument(const char * t, const char * n); bool canConnectTo(const __MetaFunc & dst, int & args_count) const; void * addr = nullptr; void * addrV = nullptr; uint func_name_id = 0; const char * func_name = nullptr; const char * type_ret = nullptr; const char * scope = nullptr; const char * types[__PIOBJECT_MAX_ARGS__]; const char * names[__PIOBJECT_MAX_ARGS__]; uint types_id[__PIOBJECT_MAX_ARGS__]; }; struct PIP_EXPORT __MetaData { __MetaData() { scope_list << "PIObject"; scope_id << PIStringAscii("PIObject").hash(); } void addScope(const char * s, uint shash); PIVector scope_list; PISet scope_id; PISet eh_set; PIMap eh_func; }; typedef PIPair __EHPair; //! \~english Executes all queued deliveries posted to this performer object. //! \~russian Выполняет все отложенные доставки, поставленные в очередь этому объекту-исполнителю. void callQueuedEvents(); //! \~\brief //! \~english Executes queued deliveries only when this object was used as a performer. //! \~russian Выполняет отложенные доставки только если этот объект использовался как исполнитель. //! \~\details //! \~english This helper is cheaper than unconditional \a callQueuedEvents() for objects that are rarely used as performer targets. //! \~russian Этот помощник дешевле, чем безусловный \a callQueuedEvents(), для объектов, которые редко используются как исполнители. bool maybeCallQueuedEvents() { if (proc_event_queue) callQueuedEvents(); return proc_event_queue; } //! \~english Schedules the object for deferred deletion. //! \~russian Планирует отложенное удаление объекта. void deleteLater(); //! \events //! \{ //! \fn void deleted(PIObject * o) //! \brief //! \~english Raised immediately before object destruction. //! \~russian Вызывается непосредственно перед уничтожением объекта. //! \~\warning //! \~english //! This event raised from destructor, so use only "o" numeric value, //! don`t try to cast deleted object to some subclass! //! \~russian //! Это событие вызывается из деструктора, поэтому используйте //! только численное значение "o", не надо кастовать его в другие типы! EVENT1(deleted, PIObject *, o); //! \} static PIMutex & __meta_mutex(); static PIMap & __meta_data(); protected: //! \~english Returns the source object that raised the current event. //! \~russian Возвращает объект-источник, который вызвал текущее событие. //! \~\details //! \~english This value is valid only while an event handler is running. //! \~russian Это значение корректно только пока выполняется обработчик события. PIObject * emitter() const { return emitter_; } //! \~english Virtual method called after property "name" has been changed by \a setProperty(). //! \~russian Виртуальный метод, вызываемый после изменения свойства "name" через \a setProperty(). virtual void propertyChanged(const char * name) {} private: struct __QueuedEvent { __QueuedEvent(void * sl = 0, void * d = 0, PIObject * d_o = 0, PIObject * s = 0, const PIVector & v = PIVector()) { slot = sl; dest = d; dest_o = d_o; src = s; values = v; } void * slot; void * dest; PIObject * dest_o; PIObject * src; PIVector values; }; class Deleter { public: Deleter(); ~Deleter(); static Deleter * instance(); void post(PIObject * o); private: void deleteObject(PIObject * o); PRIVATE_DECLARATION(PIP_EXPORT) }; bool findSuitableMethodV(const PIString & method, int args, int & ret_args, __MetaFunc & ret); PIVector<__MetaFunc> findEH(const PIString & name) const; __MetaFunc methodEH(const void * addr) const; void updateConnectors(); void piDisconnectAll(); void postQueuedEvent(const __QueuedEvent & e); void eventBegin() { in_event_cnt++; } void eventEnd() { in_event_cnt--; } bool isInEvent() const { return in_event_cnt > 0; } void * toThis() const; virtual int ptrOffset() const { return 0; } static PIVector & objects(); static PIMutex & mutexObjects(); static void callAddrV(void * slot, void * obj, int args, const PIVector & vl); PIVector connections; PIMap properties_; PISet connectors; PIVector<__QueuedEvent> events_queue; PIMutex mutex_, mutex_connect, mutex_queue; PIObject * emitter_; bool thread_safe_, proc_event_queue; std::atomic_int in_event_cnt; }; #ifndef MICRO_PIP //! \~english Dumps application-level %PIObject diagnostics. //! \~russian Выводит диагностическую информацию уровня приложения для %PIObject. PIP_EXPORT void dumpApplication(bool with_objects = true); //! \~english Dumps application-level %PIObject diagnostics to file "path". //! \~russian Выводит диагностическую информацию уровня приложения для %PIObject в файл "path". PIP_EXPORT bool dumpApplicationToFile(const PIString & path, bool with_objects = true); #endif #endif // PIOBJECT_H