/*! \file piobject.h * \ingroup Core * \~\brief * \~english Base object * \~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 This is base class for any classes which use events -> handlers mechanism. //! \~russian Этот класс является базовым для использования механизма события -> обработчики. class PIP_EXPORT PIObject { #ifndef MICRO_PIP friend class PIObjectManager; friend void dumpApplication(bool); friend class PIIntrospection; #endif typedef PIObject __PIObject__; typedef void __Parent__; public: NO_COPY_CLASS(PIObject); //! \~english Contructs %PIObject with name "name" //! \~russian Создает %PIObject с именем "name" explicit PIObject(const PIString & name = PIString()); virtual ~PIObject(); //! \ingroup Core //! \~\brief //! \~english Helper class for obtain info about if connection successful and disconnect single connection. //! \~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 Contructs invalid %Connection //! \~russian Создает недействительный %Connection Connection(); //! \~english Returns if %Connection is valid //! \~russian Возвращает успешен ли %Connection bool isValid() const { return signal; } //! \~english Returns source object //! \~russian Возвращает объект-источник PIObject * sourceObject() const { return src_o; } //! \~english Returns destination object or "nullptr" if this is lambda connection //! \~russian Возвращает объект-приемник или "nullptr" если это соединение на лямбда-функцию PIObject * destinationObject() const { return dest_o; } //! \~english Returns performer object or "nullptr" if this is non-queued connection //! \~russian Возвращает объект-исполнитель или "nullptr" если это соединение не отложенное PIObject * performerObject() const { return performer; } //! \~english Disconnect this %Connection, returns if operation successful //! \~russian Разрывает этот %Connection, возвращает успешен ли разрыв bool disconnect(); }; private: uint _signature_; public: //! \~english Returns object name //! \~russian Возвращает имя объекта PIString name() const { return property("name").toString(); } //! \~english Returns object class name //! \~russian Возвращает имя класса объекта virtual const char * className() const { return "PIObject"; } 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 parent class name //! \~russian Возвращает имя родительского класса virtual const char * parentClassName() const { return ""; } //! \~english Return if \a piCoutObj of this object is active //! \~russian Возвращает включен ли вывод \a piCoutObj для этого объекта bool debug() const { return property("debug").toBool(); } //! \~english Set object name //! \~russian Устанавливает имя объекта void setName(const PIString & name) { setProperty("name", name); } //! \~english Set object \a piCoutObj active //! \~russian Включает или отключает вывод \a piCoutObj для этого объекта void setDebug(bool debug) { setProperty("debug", debug); } //! \~english Returns property with name "name" //! \~russian Возвращает свойство объекта по имени "name" PIVariant property(const char * name) const { return properties_.value(piHashData((const uchar *)name, strlen(name))); } //! \~english Set property with name "name" to "value". If there is no such property in object it will be added //! \~russian Устанавливает у объекта свойство по имени "name" в "value". Если такого свойства нет, оно добавляется void setProperty(const char * name, const PIVariant & value) { properties_[piHashData((const uchar *)name, strlen(name))] = value; propertyChanged(name); } //! \~english Returns if property with name "name" exists //! \~russian Возвращает присутствует ли свойство по имени "name" bool isPropertyExists(const char * name) const { return properties_.contains(piHashData((const uchar *)name, strlen(name))); } void setThreadSafe(bool yes) { thread_safe_ = yes; } bool isThreadSafe() const { return thread_safe_; } bool execute(const PIString & method, const PIVector & vl); bool execute(const PIString & method) { return execute(method, PIVector()); } bool execute(const PIString & method, const PIVariantSimple & v0) { return execute(method, PIVector() << v0); } bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return execute(method, PIVector() << v0 << v1); } bool execute(const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) { return execute(method, PIVector() << v0 << v1 << v2); } 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); } bool executeQueued(PIObject * performer, const PIString & method, const PIVector & vl); bool executeQueued(PIObject * performer, const PIString & method) { return executeQueued(performer, method, PIVector()); } bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0) { return executeQueued(performer, method, PIVector() << v0); } bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return executeQueued(performer, method, PIVector() << v0 << v1); } bool executeQueued(PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1, const PIVariantSimple & v2) { return executeQueued(performer, method, PIVector() << v0 << v1 << v2); } 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); } static bool execute(PIObject * o, const PIString & method, const PIVector & vl) { return o->execute(method, vl); } static bool execute(PIObject * o, const PIString & method) { return execute(o, method, PIVector()); } static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0) { return execute(o, method, PIVector() << v0); } static bool execute(PIObject * o, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return execute(o, method, PIVector() << v0 << v1); } 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); } 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); } static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVector & vl) { return o->executeQueued(performer, method, vl); } static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method) { return executeQueued(o, performer, method, PIVector()); } static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0) { return executeQueued(o, performer, method, PIVector() << v0); } static bool executeQueued(PIObject * o, PIObject * performer, const PIString & method, const PIVariantSimple & v0, const PIVariantSimple & v1) { return executeQueued(o, performer, method, PIVector() << v0 << v1); } 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); } 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); } void dump(const PIString & line_prefix = PIString()) const; //! \~english Returns subclass scope of this object (including this class name) //! \~russian Возвращает цепочку наследования объекта (вместе с классом самого объекта) PIStringList scopeList() const; PIStringList methodsEH() const; bool isMethodEHContains(const PIString & name) const; PIString methodEHArguments(const PIString & name) const; PIString methodEHFullFormat(const PIString & name) const; PIString methodEHFromAddr(const void * addr) const; // / Direct 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); static PIObject::Connection piConnectU(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, const PIString & hname, const char * loc, PIObject * performer = 0); 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 Disconnect object from all connections with event name "sig", connected to destination object "dest" and handler "ev_h" //! \~russian Разрывает все соединения от события "sig" к объекту "dest" и обработчику "ev_h" void piDisconnect(const PIString & sig, PIObject * dest, void * ev_h) { piDisconnect(this, sig, dest, ev_h); } //! \~english Disconnect object from all connections with event name "sig", connected to destination object "dest" //! \~russian Разрывает все соединения от события "sig" к объекту "dest" void piDisconnect(const PIString & sig, PIObject * dest) { piDisconnect(this, sig, dest); } //! \~english Disconnect object from all connections with event name "sig" //! \~russian Разрывает все соединения от события "sig" void piDisconnect(const PIString & sig) { piDisconnect(this, sig); } //! \~english Disconnect object "src" from all connections with event name "sig", connected to destination object "dest" and handler //! "ev_h" //! \~russian Разрывает все соединения от события "sig" объекта "src" к объекту "dest" и обработчику "ev_h" static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h); //! \~english Disconnect object "src" from all connections with event name "sig", connected to destination object "dest" //! \~russian Разрывает все соединения от события "sig" объекта "src" к объекту "dest" static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest); //! \~english Disconnect object "src" from all connections with event name "sig" //! \~russian Разрывает все соединения от события "sig" объекта "src" static void piDisconnect(PIObject * src, const PIString & sig); // / Raise events 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; } } 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; } } 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; } } 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; } } 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; } } //! Returns PIObject* with name "name" or 0, if there is no object found static PIObject * findByName(const PIString & name) { PIMutexLocker _ml(mutexObjects()); piForeach(PIObject * i, PIObject::objects()) { if (i->name() != name) continue; return i; } return nullptr; } //! \~english Returns if this is valid %PIObject (check signature) //! \~russian Возвращает действительный ли это %PIObject (проверяет подпись) bool isPIObject() const { return isPIObject(this); } //! \~english Returns if this is valid %PIObject subclass "T" (check signature and classname) //! \~russian Возвращает действительный ли это наследник %PIObject типа "T" (проверяет подпись и имя класса) template bool isTypeOf() const { if (!isPIObject()) return false; PIMutexLocker ml(__meta_mutex()); return __meta_data()[classNameID()].scope_id.contains(T::__classNameIDS()); } //! \~english Returns cast to T if this is valid subclass "T" (check by \a isTypeOf()) or "nullptr" //! \~russian Возвращает преобразование к типу T если это действительный наследник типа "T" (проверяет через \a isTypeOf()), или //! "nullptr" template T * cast() const { if (!isTypeOf()) return (T *)nullptr; return (T *)this; } //! \~english Returns if "o" is valid %PIObject (check signature) //! \~russian Возвращает действительный ли "o" %PIObject (проверяет подпись) static bool isPIObject(const PIObject * o); static bool isPIObject(const void * o) { return isPIObject((PIObject *)o); } //! \~english Returns if "o" is valid %PIObject subclass "T" (check signature and classname) //! \~russian Возвращает действительный ли "o" наследник %PIObject типа "T" (проверяет подпись и имя класса) template static bool isTypeOf(const PIObject * o) { return o->isTypeOf(); } template static bool isTypeOf(const void * o) { return isTypeOf((PIObject *)o); } 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 Execute all posted events from CONNECTU_QUEUED connections //! \~russian Выполнить все отложенные события от CONNECTU_QUEUED соединений void callQueuedEvents(); //! \~english //! \brief Check if any CONNECTU_QUEUED connections to this object and execute them //! \details This function is more optimized than \a callQueuedEvents() for objects that doesn`t //! appears as \"performer\" target at CONNECTU_QUEUED //! \~russian //! \brief Если было хотя бы одно CONNECTU_QUEUED соединение с исполнителем this, то выполнить события //! \details Этот метод более оптимален, чем \a callQueuedEvents(), для объектов, которые не были в роли //! \"performer\" в макросе CONNECTU_QUEUED bool maybeCallQueuedEvents() { if (proc_event_queue) callQueuedEvents(); return proc_event_queue; } //! \~english Mark object to delete //! \~russian Пометить объект на удаление void deleteLater(); static PIMutex & __meta_mutex(); static PIMap & __meta_data(); // [hash(classname)]=__MetaData protected: //! \~english Returns %PIObject* which has raised an event. This value is correct only in definition of some event handler //! \~russian Возвращает %PIObject* который вызвал это событие. Значение допустимо только из методов обработчиков событий PIObject * emitter() const { return emitter_; } //! \~english Virtual function executes after property with name "name" has been changed //! \~russian Виртуальная функция, вызывается после изменения любого свойства. virtual void propertyChanged(const char * name) {} EVENT1(deleted, PIObject *, o); //! \events //! \{ //! \fn void deleted(PIObject * o) //! \brief //! \~english Raise before object delete //! \~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", не надо кастовать его в другие типы! //! \} 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 PIP_EXPORT void dumpApplication(bool with_objects = true); PIP_EXPORT bool dumpApplicationToFile(const PIString & path, bool with_objects = true); #endif #endif // PIOBJECT_H