/* PIP - Platform Independent Primitives Object, base class of some PIP classes, provide EVENT -> EVENT_HANDLER mechanism Copyright (C) 2016 Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "piobject.h" #include "pifile.h" #include "pisysteminfo.h" /** \class PIObject * \brief This is base class for any classes which use events -> handlers mechanism. * \details * \section PIObject_sec0 Events and Event handlers * %PIObject provide notification mechanism similar Qt but implemented * on language capabilities without any special preprocessors or compilers. * Any class inherits PIObject should use macro \a PIOBJECT() immediate * after declaration to proper compile. * * Event is a some abstract event that can be raised at any time. * Event is a function but declared with special macro \a EVENT(). * To raise event simply execute event function. * * Event handler is a function but declared with special macro * \a EVENT_HANDLER(). You can use event handlers as ordinary functions. * * Main goal of this mechanism is perform abstract connections between * various objects. This functionality provide macro \a CONNECT() which * connect some event of first object to some event handler or event of * second object. Each event can be connected any times to any event handlers. * * \image html events_handlers.png * * Example: \snippet piobject.cpp main * Result: \code{.cpp} handler B: 2 , 0.5 handler A: event to handler handler A: event to event \endcode */ PIVector PIObject::objects; PIMap PIObject::__eh_data; PIString PIObject::__EHFunc::arguments() const { return types.join(","); } PIString PIObject::__EHFunc::fullFormat() const { PIString ret = type_ret + " " + scope + "::" + func_name +"("; for (int i = 0; i < types.size_s(); ++i) { if (i > 0) ret += ", "; ret += types[i] + " " + names[i]; } ret += ")"; return ret; } PIObject::PIObject(const PIString & name): _signature_(__PIOBJECT_SIGNATURE__), emitter_(0), thread_safe_(false) { piMonitor.objects++; //__PIVariantInitBuiltin__(); setName(name); setDebug(true); objects << this; //piCout << "new" << this; } PIObject::~PIObject() { //piCout << "delete" << this; piMonitor.objects--; objects.removeAll(this); piDisconnect(this); } void PIObject::piConnect(const PIString & src, const PIString & sig, void * dest, void * ev_h) { PIObject * o = findByName(src); if (o == 0) { piCout << "[PIObject] Can`t find object with name \"" << src << "\"!"; return; } PIMutexLocker _ml(o->mutex_connect); PIMutexLocker _mld(((PIObject*)dest)->mutex_connect, ((PIObject*)dest) != o); o->connections << Connection(ev_h, 0, sig, (PIObject*)dest, dest); ((PIObject*)dest)->connectors << o; } void PIObject::piConnect(PIObject * src, const PIString & sig, const PIString & dest, void * ev_h) { PIObject * o = findByName(dest); if (o == 0) { piCout << "[PIObject] Can`t find object with name \"" << dest << "\"!"; return; } PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(o->mutex_connect, src != o); src->connections << Connection(ev_h, 0, sig, o, o); ((PIObject*)o)->connectors << src; } void PIObject::piConnect(const PIString & src, const PIString & sig, const PIString & dest, void * ev_h) { PIObject * s = findByName(src); if (s == 0) { piCout << "[PIObject] Can`t find object with name \"" << src << "\"!"; return; } PIObject * d = findByName(dest); if (d == 0) { piCout << "[PIObject] Can`t find object with name \"" << dest << "\"!"; return; } PIMutexLocker _ml(s->mutex_connect); PIMutexLocker _mld(d->mutex_connect, s != d); s->connections << Connection(ev_h, 0, sig, d, d); d->connectors << s; } /* PIStringList PIObject::events() { PIStringList l; for (PIMap::const_iterator i = signals_.begin(); i != signals_.end(); i++) l << (*i).first; return l; } */ PIStringList PIObject::methodsEH() { PIMutexLocker ml(__eh_mutex()); PIStringList ret; __EHData & ehd(__eh_data[className()]); piForeachC (__EHPair & eh, ehd.eh_func) ret << eh.second.fullFormat(); return ret; } bool PIObject::isMethodEHContains(const PIString & name) const { PIMutexLocker ml(__eh_mutex()); __EHData & ehd(__eh_data[className()]); piForeachC (__EHPair & eh, ehd.eh_func) if (eh.second.func_name == name) return true; return false; } PIString PIObject::methodEHArguments(const PIString & name) const { PIMutexLocker ml(__eh_mutex()); __EHData & ehd(__eh_data[className()]); piForeachC (__EHPair & eh, ehd.eh_func) if (eh.second.func_name == name) return eh.second.arguments(); return PIString(); } PIString PIObject::methodEHFullFormat(const PIString & name) const { PIMutexLocker ml(__eh_mutex()); __EHData & ehd(__eh_data[className()]); piForeachC (__EHPair & eh, ehd.eh_func) if (eh.second.func_name == name) return eh.second.fullFormat(); return PIString(); } PIString PIObject::methodEHFromAddr(const void * addr) const { return methodEH(addr).func_name; } PIVector PIObject::findEH(const PIString & name) const { PIVector<__EHFunc> ret; __EHData & ehd(__eh_data[className()]); piForeachC (__EHPair & eh, ehd.eh_func) if (eh.second.func_name == name) ret << eh.second; return ret; } PIObject::__EHFunc PIObject::methodEH(const void * addr) const { PIMutexLocker ml(__eh_mutex()); return __eh_data[className()].eh_func.value(addr); } void PIObject::piConnect(PIObject * src, const PIString & sig, PIObject * dest_o, void * dest, void * ev_h, void * e_h, int args, const char * loc) { //piCout << "piConnect ..."; //piCout << "piConnect" << src << (void*)(dest) << sig; //piCout << "piConnect" << src->className() << "->" << ((PIObject*)dest)->className(); PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(dest_o->mutex_connect, src != dest_o); src->connections << Connection(ev_h, e_h, sig, dest_o, dest, args); //piCout << "piConnect" << ((PIObject*)dest) << sig << ((PIObject*)dest)->connectors.size_s() << "..."; //piCout << "addConnector" << dest_o << src; dest_o->connectors << src; //piCout << "piConnect" << ((PIObject*)dest) << sig << ((PIObject*)dest)->connectors.size_s(); //piCout << "piConnect ok"; } bool PIObject::piConnectU(PIObject * src, const PIString & ename, PIObject * dest_o, void * dest, const PIString & hname, const char * loc) { if (src == 0 || dest_o == 0 || dest == 0) return false; if (!src->isPIObject()) { piCout << "[piConnectU] \"" << ename << "\" -> \"" << hname << "\" error: source object is not PIObject! (" << loc << ")"; return false; } if (!dest_o->isPIObject()) { piCout << "[piConnectU] \"" << ename << "\" -> \"" << hname << "\" error: destination object is not PIObject! (" << loc << ")"; return false; } PIMutexLocker ml(__eh_mutex()); PIMutexLocker mls(src->mutex_connect); PIMutexLocker mld(dest_o->mutex_connect, src != dest_o); PIVector<__EHFunc> m_src = src->findEH(ename), m_dest = dest_o->findEH(hname); if (m_src.isEmpty()) { piCout << "[piConnectU] Error: can`t find event \"" << ename << "\" in class \"" << src->className() << "\"! (" << loc << ")"; return false; } if (m_dest.isEmpty()) { piCout << "[piConnectU] Error: can`t find handler \"" << hname << "\" in class \"" << dest_o->className() << "\"! (" << loc << ")"; return false; } void * addr_src(0), * addr_dest(0); int args(0); piForeachC (__EHFunc & fs, m_src) { if (addr_src != 0) break; piForeachC (__EHFunc & fd, m_dest) { if (addr_src != 0) break; if (fs.arguments().startsWith(fd.arguments()) || fd.arguments().isEmpty()) { addr_src = fs.addr; addr_dest = fd.addr; args = fd.names.size_s(); } } } if (addr_src == 0) { piCout << "[piConnectU] Error: can`t find suitable pair of event \"" << ename << "\" in class \"" << src->className() << "\" and handler \"" << hname << "\" in class \"" << dest_o->className() << "\"! (" << loc << ")"; return false; } //piCout << "connect" << ename << "->" << hname << "with" << args << "args"; src->connections << PIObject::Connection(addr_dest, addr_src, ename, dest_o, dest, args); dest_o->connectors << src; return true; } void PIObject::piDisconnect(PIObject * src, const PIString & sig, PIObject * dest, void * ev_h) { PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(dest->mutex_connect, src != dest); for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); if (cc.event == sig && cc.dest_o == dest && cc.slot == ev_h) { src->connections.remove(i); i--; } } dest->updateConnectors(); } void PIObject::piDisconnect(PIObject * src, const PIString & sig, PIObject * dest) { PIMutexLocker _ml(src->mutex_connect); PIMutexLocker _mld(dest->mutex_connect, src != dest); for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); if (cc.event == sig && cc.dest_o == dest) { src->connections.remove(i); i--; } } dest->updateConnectors(); } void PIObject::piDisconnect(PIObject * src, const PIString & sig) { PIMutexLocker _ml(src->mutex_connect); for (int i = 0; i < src->connections.size_s(); ++i) { Connection & cc(src->connections[i]); if (cc.event == sig) { PIObject * dest = cc.dest_o; src->connections.remove(i); i--; #ifndef ANDROID PIMutexLocker _mld(dest->mutex_connect, src != dest); #endif dest->updateConnectors(); } } } void PIObject::piDisconnect(PIObject * src) { src->deleted(); PIMutexLocker _ml(src->mutex_connect); PIVector cv = src->connectors.toVector(); piForeach (PIObject * o, cv) { if (o == src) continue; #ifndef ANDROID PIMutexLocker _mld(o->mutex_connect, src != o); #endif PIVector & oc(o->connections); for (int i = 0; i < oc.size_s(); ++i) { //piCout << " check" << (void*)(oc[i].dest_o) << "==" << (void*)(src); if (oc[i].dest_o == src) { oc.remove(i); --i; } } } piForeachC (PIObject::Connection & c, src->connections) c.dest_o->connectors.remove(src); src->connections.clear(); } void PIObject::updateConnectors() { //piCout << "*** updateConnectors" << this; connectors.clear(); piForeach (PIObject * o, objects) { if (o == this) continue; PIVector & oc(o->connections); piForeach (Connection & c, oc) if (c.dest == this) connectors << o; } } PIMutex & PIObject::__eh_mutex() { static PIMutex ret; return ret; } PIString PIObject::simplifyType(const char * a) { PIString ret = PIStringAscii(a).trim(); int white = -1; for (int i = 0; i < ret.size_s(); ++i) { bool iw = ret[i] == ' ' || ret[i] == '\t' || ret[i] == '\r' || ret[i] == '\n'; //piCout << i << iw << white; if (white < 0) { if (iw) { white = i; continue; } } else { if (!iw) { ret.replace(white, i - white, " "); i = white; white = -1; //piCout << i; } } } ret.replaceAll(" &", "&"); ret.replaceAll(" *", "*"); if (ret.startsWith("const ") && ret.endsWith("&")) ret.cutLeft(6).cutRight(1).trim(); return ret; } bool PIObject::execute(const PIString & method) { if (method.isEmpty()) return false; PIVector<__EHFunc> ml = findEH(method); piForeachC (__EHFunc & m, ml) { if (!m.names.isEmpty()) continue; ((void(*)(void*))m.addr)(this); return true; } piCoutObj << "Error: can`t find event or handler \"" << (method + "()") << "\" to execute!"; return false; } void PIObject::dump(const PIString & line_prefix) const { //printf("dump %s \"%s\"\n", className(), name().data()); PICout(PICoutManipulators::AddNewLine) << line_prefix << "class " << className() << " (" << (const void*)this << ", \"" << name() << "\") {"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " properties {"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << properties_.size_s(); //printf("dump %d properties\n", properties_.size()); piForeachC (Property p, properties_) if (p.first != "name") PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << p.first << ": " << p.second; //printf("dump %d properties ok\n", properties_.size()); PICout(PICoutManipulators::AddNewLine) << line_prefix << " }"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " methodsEH {"; __EHData & ehd(__eh_data[className()]); PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << ehd.eh_func.size_s(); //printf("dump %d methods\n", ehd.eh_func.size()); piForeachC (__EHPair & eh, ehd.eh_func) { PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << eh.second.fullFormat(); } //printf("dump %d methods ok\n", ehd.eh_func.size()); PICout(PICoutManipulators::AddNewLine) << line_prefix << " }"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " connections {"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << connections.size_s(); //printf("dump %d connections\n",connections.size()); piForeachC (Connection & c, connections) { PIObject * dst = c.dest_o; __EHFunc hf = dst->methodEH(c.slot); __EHFunc ef = methodEH(c.signal); if (hf.func_name.isEmpty()) hf.func_name = "[BROKEN]"; else hf.func_name += "(" + hf.arguments() + ")"; PIString src(c.event); if (!ef.func_name.isEmpty()) src = ef.func_name + "(" + ef.arguments() + ")"; PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << src << " -> " << dst->className() << " (" << c.dest << ", \"" << dst->name() << "\")::" << hf.func_name; } //printf("dump %d connections ok\n",connections.size()); PICout(PICoutManipulators::AddNewLine) << line_prefix << " }"; PICout(PICoutManipulators::AddNewLine) << line_prefix << "}"; } void dumpApplication() { //printf("dump application ...\n"); PIDateTime cd = PIDateTime::current(); PISystemInfo * pi = PISystemInfo::instance(); PICout(PICoutManipulators::AddNewLine) << "application {"; PICout(PICoutManipulators::AddNewLine) << " PIP version: " << PIPVersion(); PICout(PICoutManipulators::AddNewLine) << " processors: " << pi->processorsCount; PICout(PICoutManipulators::AddNewLine) << " hostname: \"" << pi->hostname << "\""; PICout(PICoutManipulators::AddNewLine) << " user: \"" << pi->user << "\""; PICout(PICoutManipulators::AddNewLine) << " exec command: \"" << pi->execCommand << "\""; PICout(PICoutManipulators::AddNewLine) << " started: " << pi->execDateTime.toString(); PICout(PICoutManipulators::AddNewLine) << " uptime, s: " << (cd.toSystemTime() - pi->execDateTime.toSystemTime()).toSeconds(); PICout(PICoutManipulators::AddNewLine) << " PIObjects {"; PICout(PICoutManipulators::AddNewLine) << " count: " << PIObject::objects.size_s(); piForeachC (PIObject * o, PIObject::objects) o->dump(" "); PICout(PICoutManipulators::AddNewLine) << " }"; PICout(PICoutManipulators::AddNewLine) << "}"; //printf("dump application done\n"); } bool dumpApplicationToFile(const PIString & path) { PIFile f(path + "_tmp"); f.setName("__S__DumpFile"); f.clear(); if (!f.open(PIIODevice::WriteOnly)) return false; bool ba = PICout::isBufferActive(); PICout::setBufferActive(true, true); dumpApplication(); f << PICout::buffer(); f.close(); PICout::setBufferActive(ba, true); PIFile::rename(path + "_tmp", path); return true; }