/*! @file piobject.h * @brief Base object * * This file declare PIObject class */ /* 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 "pivariant.h" #include "pivariantsimple.h" #include "pimutex.h" #include "piset.h" #include "piqueue.h" #include "piobject_macros.h" typedef void (*Handler)(void * ); class PIP_EXPORT PIObject { #ifndef MICRO_PIP friend class PIObjectManager; friend void dumpApplication(); friend class PIIntrospection; #endif typedef PIObject __PIObject__; typedef void __Parent__; public: NO_COPY_CLASS(PIObject) //! Contructs PIObject with name "name" explicit PIObject(const PIString & name = PIString()); virtual ~PIObject(); //! Helper class for obtain info about if connection successful and disconnect single connection 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: //! Contructs invalid %Connection Connection(); //! Returns if %Connection is valid bool isValid() const {return signal;} //! Returns source object PIObject * sourceObject() const {return src_o;} //! Returns destination object or nullptr if this is lambda connection PIObject * destinationObject() const {return dest_o;} //! Returns performer object or nullptr if this is non-queued connection PIObject * performerObject() const {return performer;} //! Disconnect this %Connection, returns if operation successful bool disconnect(); }; private: uint _signature_; public: //! Returns object name PIString name() const {return property(PIStringAscii("name")).toString();} //! Returns object class name virtual const char * className() const {return "PIObject";} virtual uint classNameID() const {static uint ret = PIStringAscii("PIObject").hash(); return ret;} static const PIString __classNameS() {return PIStringAscii("PIObject");} static uint __classNameIDS() {static uint ret = PIStringAscii("PIObject").hash(); return ret;} //! Returns parent object class name virtual const char * parentClassName() const {return "";} //! Return if debug of this object is active bool debug() const {return property(PIStringAscii("debug")).toBool();} //! Set object name void setName(const PIString & name) {setProperty(PIStringAscii("name"), name);} void setName(const char * name) {setName(PIStringAscii(name));} //! Set object debug active void setDebug(bool debug) {setProperty(PIStringAscii("debug"), debug);} //! Returns properties of the object PIMap properties() const; //! Returns properties count of the object int propertiesCount() const {return properties_.size_s();} //! Returns property with name "name" PIVariant property(const PIString & name) const {return properties_.value(name.hash(), Property(PIString(), PIVariant())).second;} PIVariant property(const char * name) const {return property(PIStringAscii(name));} //! Set property with name "name" to "value". If there is no such property in object it will be added void setProperty(const PIString & name, const PIVariant & value) {properties_[name.hash()] = Property(name, value); propertyChanged(name);} void setProperty(const char * name, const PIVariant & value) {setProperty(PIStringAscii(name), value);} //! Returns if property with name "name" exists bool isPropertyExists(const PIString & name) const {return properties_.contains(name.hash());} bool isPropertyExists(const char * name) const {return isPropertyExists(PIStringAscii(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; 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)); } //! Disconnect object from all connections with event name "sig", connected to destination object "dest" and handler "ev_h" void piDisconnect(const PIString & sig, PIObject * dest, void * ev_h) {piDisconnect(this, sig, dest, ev_h);} //! Disconnect object from all connections with event name "sig", connected to destination object "dest" void piDisconnect(const PIString & sig, PIObject * dest) {piDisconnect(this, sig, dest);} //! Disconnect object from all connections with event name "sig" void piDisconnect(const PIString & sig) {piDisconnect(this, sig);} //! Disconnect object "src" from all connections with event name "sig", connected to destination object "dest" and handler "ev_h" static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h); //! Disconnect object "src" from all connections with event name "sig", connected to destination object "dest" static void piDisconnect(PIObject * src, const PIString & sig, PIObject * dest); //! Disconnect object "src" from all connections with event name "sig" 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 0; } bool isPIObject() const {return isPIObject(this);} template bool isTypeOf() const { if (!isPIObject()) return false; return scopeList().contains(T::__classNameS()); } template T * cast() const { if (!isTypeOf()) return (T*)0; return (T*)this; } static bool isPIObject(const PIObject * o); static bool isPIObject(const void * o) {return isPIObject((PIObject*)o);} 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); struct PIP_EXPORT __MetaFunc { __MetaFunc(): addr(0), addrV(0) {;} bool isNull() const {return addr == 0;} PIString arguments() const; PIString fullFormat() const; void * addr; void * addrV; PIString func_name; PIString type_ret; PIString scope; PIStringList types; PIStringList names; }; struct PIP_EXPORT __MetaData { __MetaData() {scope_list << PIStringAscii("PIObject"); scope_id << PIStringAscii("PIObject").hash();} void addScope(const PIString & s, uint shash); PIStringList scope_list; PISet scope_id; PISet eh_set; PIMap eh_func; }; typedef PIPair __EHPair; //! @brief Execute all posted events from CONNECTU_QUEUED connections void callQueuedEvents(); //! @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 bool maybeCallQueuedEvents() {if (proc_event_queue) callQueuedEvents(); return proc_event_queue;} //! @brief Mark object to delete //! \details On first call background thread started to delete objects. //! Each object deletes when it`s outside from any events and hadlers. void deleteLater(); static PIMutex & __meta_mutex(); static PIMap & __meta_data(); // [hash(classname)]=__MetaData protected: //! Returns PIObject* which has raised an event. This value is correct only in definition of some event handler PIObject * emitter() const {return emitter_;} //! Virtual function executes after property with name "name" has been changed virtual void propertyChanged(const PIString & name) {} EVENT1(deleted, PIObject *, o) //! \events //! \{ /** \fn void deleted(PIObject * o) * @brief Raise before object delete * \note This event raised from destructor, so use only "o" value, * don`t try to cast deleted object to some subclass! */ //! \} 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) }; typedef PIPair Property; typedef PIPair > PropertyHash; 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(); PIP_EXPORT bool dumpApplicationToFile(const PIString & path); #endif #endif // PIOBJECT_H