781 lines
24 KiB
C++
781 lines
24 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "piobject.h"
|
|
#include "pisysteminfo.h"
|
|
#include "pithread.h"
|
|
#include "piconditionvar.h"
|
|
#ifndef FREERTOS
|
|
# include "pifile.h"
|
|
#endif
|
|
|
|
/** \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
|
|
*/
|
|
|
|
|
|
PIString PIObject::__MetaFunc::arguments() const {
|
|
return types.join(",");
|
|
}
|
|
|
|
|
|
PIString PIObject::__MetaFunc::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), proc_event_queue(false) {
|
|
in_event_cnt = 0;
|
|
setName(name);
|
|
setDebug(true);
|
|
mutexObjects().lock();
|
|
objects() << this;
|
|
mutexObjects().unlock();
|
|
//piCout << "new" << this;
|
|
}
|
|
|
|
|
|
PIObject::~PIObject() {
|
|
in_event_cnt = 0;
|
|
//piCout << "delete" << this;
|
|
mutexObjects().lock();
|
|
objects().removeAll(this);
|
|
mutexObjects().unlock();
|
|
piDisconnect(this);
|
|
}
|
|
|
|
PIMap<PIString, PIVariant> PIObject::properties() const {
|
|
PIMap<PIString, PIVariant> ret;
|
|
piForeachC (PropertyHash p, properties_)
|
|
ret[p.second.first] = p.second.second;
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PIObject::execute(const PIString & method, const PIVector<PIVariantSimple> & vl) {
|
|
if (method.isEmpty()) return false;
|
|
if (!isPIObject()) {
|
|
piCout << "Error: \"execute(" << method << ")\":" << (void*)this << "is not PIObject!";
|
|
return false;
|
|
}
|
|
int ac = 0;
|
|
__MetaFunc func;
|
|
bool ok = findSuitableMethodV(method, vl.size_s(), ac, func);
|
|
if (!ok)
|
|
return false;
|
|
callAddrV(func.addrV, toThis(), ac, vl);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PIObject::executeQueued(PIObject * performer, const PIString & method, const PIVector<PIVariantSimple> & vl) {
|
|
if (!isPIObject()) {
|
|
piCout << "Error: \"executeQueued(" << method << ")\": this(" << (void*)this << ") is not PIObject!";
|
|
return false;
|
|
}
|
|
if (!performer->isPIObject()) {
|
|
piCout << "Error: \"executeQueued(" << method << ")\": performer(" << (void*)performer << ") is not PIObject!";
|
|
return false;
|
|
}
|
|
int ac = 0;
|
|
__MetaFunc func;
|
|
bool ok = findSuitableMethodV(method, vl.size_s(), ac, func);
|
|
if (!ok)
|
|
return false;
|
|
performer->postQueuedEvent(__QueuedEvent(func.addrV, toThis(), this, performer, vl));
|
|
performer->proc_event_queue = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
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::scopeList() const {
|
|
PIMutexLocker ml(__meta_mutex());
|
|
return __meta_data()[classNameID()].scope_list;
|
|
}
|
|
|
|
|
|
PIStringList PIObject::methodsEH() const {
|
|
PIMutexLocker ml(__meta_mutex());
|
|
PIStringList ret;
|
|
const __MetaData & ehd(__meta_data()[classNameID()]);
|
|
for (auto eh = ehd.eh_func.constBegin(); eh != ehd.eh_func.constEnd(); eh++)
|
|
ret << eh.value().fullFormat();
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool PIObject::isMethodEHContains(const PIString & name) const {
|
|
PIMutexLocker ml(__meta_mutex());
|
|
const __MetaData & ehd(__meta_data()[classNameID()]);
|
|
for (auto eh = ehd.eh_func.constBegin(); eh != ehd.eh_func.constEnd(); eh++) {
|
|
if (eh.value().func_name == name)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
PIString PIObject::methodEHArguments(const PIString & name) const {
|
|
PIMutexLocker ml(__meta_mutex());
|
|
const __MetaData & ehd(__meta_data()[classNameID()]);
|
|
for (auto eh = ehd.eh_func.constBegin(); eh != ehd.eh_func.constEnd(); eh++) {
|
|
if (eh.value().func_name == name)
|
|
return eh.value().arguments();
|
|
}
|
|
return PIString();
|
|
}
|
|
|
|
|
|
PIString PIObject::methodEHFullFormat(const PIString & name) const {
|
|
PIMutexLocker ml(__meta_mutex());
|
|
const __MetaData & ehd(__meta_data()[classNameID()]);
|
|
for (auto eh = ehd.eh_func.constBegin(); eh != ehd.eh_func.constEnd(); eh++) {
|
|
if (eh.value().func_name == name)
|
|
return eh.value().fullFormat();
|
|
}
|
|
return PIString();
|
|
}
|
|
|
|
|
|
PIString PIObject::methodEHFromAddr(const void * addr) const {
|
|
return methodEH(addr).func_name;
|
|
}
|
|
|
|
|
|
PIVector<PIObject::__MetaFunc> PIObject::findEH(const PIString & name) const {
|
|
PIVector<__MetaFunc> ret;
|
|
const __MetaData & ehd(__meta_data()[classNameID()]);
|
|
for (auto eh = ehd.eh_func.constBegin(); eh != ehd.eh_func.constEnd(); eh++) {
|
|
if (eh.value().func_name == name)
|
|
ret << eh.value();
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIObject::__MetaFunc PIObject::methodEH(const void * addr) const {
|
|
PIMutexLocker ml(__meta_mutex());
|
|
return __meta_data()[classNameID()].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 & sig, PIObject * dest_o, void * dest, const PIString & hname, const char * loc, PIObject * performer) {
|
|
if (src == 0 || dest_o == 0 || dest == 0) return false;
|
|
if (!src->isPIObject()) {
|
|
piCout << "[piConnectU] \"" << sig << "\" -> \"" << hname << "\" error: source object is not PIObject! (" << loc << ")";
|
|
return false;
|
|
}
|
|
if (!dest_o->isPIObject()) {
|
|
piCout << "[piConnectU] \"" << sig << "\" -> \"" << hname << "\" error: destination object is not PIObject! (" << loc << ")";
|
|
return false;
|
|
}
|
|
PIMutexLocker ml(__meta_mutex());
|
|
PIMutexLocker mls(src->mutex_connect);
|
|
PIMutexLocker mld(dest_o->mutex_connect, src != dest_o);
|
|
PIVector<__MetaFunc> m_src = src->findEH(sig), m_dest = dest_o->findEH(hname);
|
|
if (m_src.isEmpty()) {
|
|
piCout << "[piConnectU] Error: can`t find event \"" << sig << "\" 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);
|
|
bool que = (performer != 0);
|
|
piForeachC (__MetaFunc & fs, m_src) {
|
|
if (addr_src != 0) break;
|
|
piForeachC (__MetaFunc & fd, m_dest) {
|
|
if (addr_src != 0) break;
|
|
if (fs.arguments().startsWith(fd.arguments()) || fd.arguments().isEmpty()) {
|
|
addr_src = fs.addr;
|
|
addr_dest = que ? fd.addrV : fd.addr;
|
|
args = fd.names.size_s();
|
|
}
|
|
}
|
|
}
|
|
if (addr_src == 0) {
|
|
piCout << "[piConnectU] Error: can`t find suitable pair of event \"" << sig << "\" in class \"" << src->className()
|
|
<< "\" and handler \"" << hname << "\" in class \"" << dest_o->className() << "\"! (" << loc << ")";
|
|
return false;
|
|
}
|
|
src->connections << PIObject::__Connection(addr_dest, addr_src, sig, dest_o, dest, args, performer);
|
|
if (que) performer->proc_event_queue = true;
|
|
dest_o->connectors << src;
|
|
//piCout << cc << cq << _ol.size();//"connect" << src << "->" << dest_o << ", dest.connectors.size() =" << dest_o->connectors.size();
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PIObject::piConnectLS(PIObject * src, const PIString & sig, std::function<void()> * f, const char * loc) {
|
|
if (src == 0) {
|
|
delete f;
|
|
return false;
|
|
}
|
|
if (!src->isPIObject()) {
|
|
piCout << "[piConnectLS] \"" << sig << "\" -> [lambda] error: source object is not PIObject! (" << loc << ")";
|
|
delete f;
|
|
return false;
|
|
}
|
|
PIMutexLocker ml(__meta_mutex());
|
|
PIMutexLocker mls(src->mutex_connect);
|
|
//piCout << "locked";
|
|
PIVector<__MetaFunc> m_src = src->findEH(sig);
|
|
if (m_src.isEmpty()) {
|
|
piCout << "[piConnectLS] Error: can`t find event \"" << sig << "\" in class \"" << src->className() << "\"! (" << loc << ")";
|
|
delete f;
|
|
return false;
|
|
}
|
|
if (m_src.size() != 1) {
|
|
piCout << "[piConnectLS] Error: can`t connect overloaded event \"" << sig << "\" in class \"" << src->className() << "\"! (" << loc << ")";
|
|
delete f;
|
|
return false;
|
|
}
|
|
PIObject::__Connection conn(0, m_src[0].addr, sig);
|
|
//piCout << "found";
|
|
conn.functor = f;
|
|
src->connections << conn;
|
|
//piCout << "finished";
|
|
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[i].destroy();
|
|
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[i].destroy();
|
|
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;
|
|
if (!dest) {
|
|
src->connections[i].destroy();
|
|
src->connections.remove(i);
|
|
i--;
|
|
#if !defined(ANDROID) && !defined(MAC_OS) && !defined(FREERTOS)
|
|
PIMutexLocker _mld(dest->mutex_connect, src != dest);
|
|
#endif
|
|
dest->updateConnectors();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void PIObject::piDisconnect(PIObject * src) {
|
|
src->deleted(src);
|
|
PIMutexLocker _ml(src->mutex_connect);
|
|
PIVector<PIObject * > cv = src->connectors.toVector();
|
|
piForeach (PIObject * o, cv) {
|
|
//piCout << "disconnect"<< src->className()<< o->className();
|
|
if (!o || (o == src)) continue;
|
|
if (!o->isPIObject()) continue;
|
|
#if !defined(ANDROID) && !defined(MAC_OS) && !defined(FREERTOS)
|
|
PIMutexLocker _mld(o->mutex_connect, src != o);
|
|
#endif
|
|
PIVector<__Connection> & oc(o->connections);
|
|
for (int i = 0; i < oc.size_s(); ++i) {
|
|
if (oc[i].functor) continue;
|
|
//piCout << " check" << (void*)(oc[i].dest_o) << "==" << (void*)(src);
|
|
if (oc[i].dest_o == src) {
|
|
oc[i].destroy();
|
|
oc.remove(i);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
piForeachC (PIObject::__Connection & c, src->connections) {
|
|
if (c.functor) continue;
|
|
if (!c.dest_o) continue;
|
|
if (!c.dest_o->isPIObject()) continue;
|
|
c.dest_o->connectors.remove(src);
|
|
}
|
|
for (int i = 0; i < src->connections.size_s(); ++i)
|
|
src->connections[i].destroy();
|
|
src->connections.clear();
|
|
}
|
|
|
|
|
|
void PIObject::updateConnectors() {
|
|
//piCout << "*** updateConnectors" << this;
|
|
connectors.clear();
|
|
PIMutexLocker _ml(mutexObjects());
|
|
piForeach (PIObject * o, objects()) {
|
|
if (o == this) continue;
|
|
PIVector<__Connection> & oc(o->connections);
|
|
piForeach (__Connection & c, oc)
|
|
if (c.dest == this)
|
|
connectors << o;
|
|
}
|
|
}
|
|
|
|
|
|
void PIObject::postQueuedEvent(const PIObject::__QueuedEvent & e) {
|
|
mutex_queue.lock();
|
|
events_queue << e;
|
|
mutex_queue.unlock();
|
|
}
|
|
|
|
|
|
void * PIObject::toThis() const {
|
|
//piCout << ptrOffset() << (void*)this << (void*)((char*)this - ptrOffset());
|
|
return (void*)((char*)this - ptrOffset());
|
|
}
|
|
|
|
|
|
PIMutex & PIObject::__meta_mutex() {
|
|
static PIMutex ret;
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIMap<uint, PIObject::__MetaData> & PIObject::__meta_data() {
|
|
static PIMap<uint, PIObject::__MetaData> ret;
|
|
return ret;
|
|
}
|
|
|
|
|
|
void PIObject::callQueuedEvents() {
|
|
mutex_queue.lock();
|
|
PIVector<__QueuedEvent> qe = events_queue;
|
|
events_queue.clear();
|
|
mutex_queue.unlock();
|
|
piForeachC (__QueuedEvent & e, qe) {
|
|
if (e.dest_o->thread_safe_) e.dest_o->mutex_.lock();
|
|
e.dest_o->emitter_ = e.src;
|
|
callAddrV(e.slot, e.dest, e.values.size_s(), e.values);
|
|
e.dest_o->emitter_ = 0;
|
|
if (e.dest_o->thread_safe_) e.dest_o->mutex_.unlock();
|
|
}
|
|
}
|
|
|
|
|
|
void PIObject::deleteLater() {
|
|
Deleter::instance()->post(this);
|
|
}
|
|
|
|
|
|
bool PIObject::findSuitableMethodV(const PIString & method, int args, int & ret_args, PIObject::__MetaFunc & ret) {
|
|
PIVector<__MetaFunc> ml = findEH(method);
|
|
if (ml.isEmpty()) {
|
|
piCoutObj << "Error: no such method \"" << method << "\"!";
|
|
return false;
|
|
}
|
|
int mfi = -1, ac = -1, mac = -1;
|
|
for (int i = 0; i < ml.size_s(); ++i) {
|
|
__MetaFunc & m(ml[i]);
|
|
int j = m.names.size_s();
|
|
if (mac < 0 || mac > j) mac = j;
|
|
if ((j <= args) && (ac < j)) {
|
|
ac = j;
|
|
mfi = i;
|
|
}
|
|
}
|
|
if (mfi < 0) {
|
|
piCoutObj << "Error: no such suitable method \"" << method << "\", need at least" << mac << "arguments!";
|
|
return false;
|
|
}
|
|
ret_args = ac;
|
|
ret = ml[mfi];
|
|
return true;
|
|
}
|
|
|
|
|
|
PIVector<PIObject * > & PIObject::objects() {
|
|
static PIVector<PIObject * > * ret = new PIVector<PIObject * >();
|
|
return *ret;
|
|
}
|
|
|
|
|
|
PIMutex & PIObject::mutexObjects() {
|
|
static PIMutex ret;
|
|
return ret;
|
|
}
|
|
|
|
|
|
void PIObject::callAddrV(void * slot, void * obj, int args, const PIVector<PIVariantSimple> & vl) {
|
|
args = piMini(args, vl.size_s());
|
|
switch (args) {
|
|
case 0: ((void(*)(void *))slot)(obj); break;
|
|
case 1: ((void(*)(void * , const PIVariantSimple & ))slot)(obj, vl[0]); break;
|
|
case 2: ((void(*)(void * , const PIVariantSimple & , const PIVariantSimple & ))slot)(obj, vl[0], vl[1]); break;
|
|
case 3: ((void(*)(void * , const PIVariantSimple & , const PIVariantSimple & , const PIVariantSimple & ))slot)(obj, vl[0], vl[1], vl[2]); break;
|
|
case 4: ((void(*)(void * , const PIVariantSimple & , const PIVariantSimple & , const PIVariantSimple & , const PIVariantSimple & ))slot)(obj, vl[0], vl[1], vl[2], vl[3]); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
|
|
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::isPIObject(const PIObject * o) {
|
|
if (!o) return false;
|
|
return o->_signature_ == __PIOBJECT_SIGNATURE__;
|
|
}
|
|
|
|
|
|
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 << " scope: " << scopeList().join(" -> ");
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " properties {";
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << properties_.size_s();
|
|
//printf("dump %d properties\n", properties_.size());
|
|
piForeachC (PropertyHash p, properties_)
|
|
if (p.first != PIString("name").hash())
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << p.second.first << ": " << p.second.second;
|
|
//printf("dump %d properties ok\n", properties_.size());
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " }";
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " methods {";
|
|
const __MetaData & ehd(__meta_data()[classNameID()]);
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " count: " << ehd.eh_func.size_s();
|
|
//printf("dump %d methods\n", ehd.eh_func.size());
|
|
for (auto eh = ehd.eh_func.constBegin(); eh != ehd.eh_func.constEnd(); eh++) {
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << eh.value().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;
|
|
__MetaFunc ef = methodEH(c.signal);
|
|
PIString src(c.event);
|
|
if (!ef.func_name.isEmpty())
|
|
src = ef.func_name + "(" + ef.arguments() + ")";
|
|
if (dst) {
|
|
__MetaFunc hf = dst->methodEH(c.slot);
|
|
if (hf.func_name.isEmpty()) hf.func_name = "[BROKEN]";
|
|
else hf.func_name += "(" + hf.arguments() + ")";
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << src << " -> " << dst->className() << " (" << c.dest << ", \"" << dst->name() << "\")::" << hf.func_name;
|
|
} else {
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << src << " -> " << "[lambda]";
|
|
}
|
|
}
|
|
//printf("dump %d connections ok\n",connections.size());
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << " }";
|
|
PICout(PICoutManipulators::AddNewLine) << line_prefix << "}";
|
|
}
|
|
|
|
|
|
void dumpApplication() {
|
|
PIMutexLocker _ml(PIObject::mutexObjects());
|
|
//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) << " OS name: \"" << pi->OS_name << "\"";
|
|
PICout(PICoutManipulators::AddNewLine) << " OS version: \"" << pi->OS_version << "\"";
|
|
PICout(PICoutManipulators::AddNewLine) << " processors: " << pi->processorsCount;
|
|
PICout(PICoutManipulators::AddNewLine) << " architecture: \"" << pi->architecture << "\"";
|
|
PICout(PICoutManipulators::AddNewLine) << " hostname: \"" << pi->hostname << "\"";
|
|
PICout(PICoutManipulators::AddNewLine) << " username: \"" << pi->user << "\"";
|
|
PICout(PICoutManipulators::AddNewLine) << " exec command: \"" << pi->execCommand << "\"";
|
|
PICout(PICoutManipulators::AddNewLine) << " started: " << pi->execDateTime.toString();
|
|
PICout(PICoutManipulators::AddNewLine) << " uptime: " << PITime::fromSystemTime(cd.toSystemTime() - pi->execDateTime.toSystemTime()).toString();
|
|
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");
|
|
}
|
|
|
|
|
|
#ifndef FREERTOS
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
|
|
void PIObject::__MetaData::addScope(const PIString & s, uint shash) {
|
|
if (!scope_id.contains(shash)) {
|
|
scope_list << s;
|
|
scope_id << shash;
|
|
}
|
|
}
|
|
|
|
|
|
void PIObject::__Connection::destroy() {
|
|
if (functor) delete functor;
|
|
functor = nullptr;
|
|
}
|
|
|
|
|
|
|
|
|
|
PRIVATE_DEFINITION_START(PIObject::Deleter)
|
|
PIThread thread;
|
|
PIConditionVariable cond_var;
|
|
PIMutex cond_mutex, queue_mutex;
|
|
PIVector<PIObject*> obj_queue;
|
|
PRIVATE_DEFINITION_END(PIObject::Deleter)
|
|
|
|
|
|
PIObject::Deleter::Deleter() {
|
|
//piCout << "Deleter start ...";
|
|
stopping = started = posted = false;
|
|
CONNECTL(&(PRIVATE->thread), started, [this](){proc();});
|
|
PRIVATE->thread.startOnce();
|
|
while (!started) piMSleep(1);
|
|
}
|
|
|
|
|
|
PIObject::Deleter::~Deleter() {
|
|
//piCout << "~Deleter ...";
|
|
stopping = true;
|
|
PRIVATE->cond_var.notifyAll();
|
|
#ifndef WINDOWS
|
|
while (PRIVATE->thread.isRunning()) piMSleep(1);
|
|
#endif
|
|
deleteAll();
|
|
//piCout << "~Deleter ok";
|
|
}
|
|
|
|
|
|
PIObject::Deleter * PIObject::Deleter::instance() {
|
|
static Deleter ret;
|
|
return &ret;
|
|
}
|
|
|
|
|
|
void PIObject::Deleter::post(PIObject * o) {
|
|
if (!o->isPIObject()) return;
|
|
//piCout << "[Deleter] post" << o << "...";
|
|
PRIVATE->queue_mutex.lock();
|
|
if (!PRIVATE->obj_queue.contains(o))
|
|
PRIVATE->obj_queue << o;
|
|
PRIVATE->queue_mutex.unlock();
|
|
PRIVATE->cond_var.notifyAll();
|
|
posted = true;
|
|
//piCout << "[Deleter] post" << o << "done";
|
|
}
|
|
|
|
|
|
void PIObject::Deleter::proc() {
|
|
//piCout << "[Deleter] proc start";
|
|
while (!stopping) {
|
|
//piMSleep(1);
|
|
//piCout << "[Deleter] proc wait ...";
|
|
if (posted) {
|
|
posted = false;
|
|
started = true;
|
|
} else {
|
|
PRIVATE->cond_mutex.lock();
|
|
started = true;
|
|
PRIVATE->cond_var.wait(PRIVATE->cond_mutex);
|
|
PRIVATE->cond_mutex.unlock();
|
|
}
|
|
//piCout << "[Deleter] proc wait done";
|
|
deleteAll();
|
|
}
|
|
//piCout << "[Deleter] proc end ok";
|
|
}
|
|
|
|
|
|
void PIObject::Deleter::deleteAll() {
|
|
PIVector<PIObject*> oq;
|
|
PRIVATE->queue_mutex.lock();
|
|
oq = PRIVATE->obj_queue;
|
|
//piCout << "[Deleter] deleteAll" << oq.size_s() << "...";
|
|
PRIVATE->obj_queue.clear();
|
|
PRIVATE->queue_mutex.unlock();
|
|
piForeach (PIObject * o, oq)
|
|
deleteObject(o);
|
|
}
|
|
|
|
|
|
void PIObject::Deleter::deleteObject(PIObject * o) {
|
|
//piCout << "[Deleter] delete" << (uintptr_t)o << "...";
|
|
if (o->isPIObject()) {
|
|
//piCout << "[Deleter] delete" << (uintptr_t)o << "wait atomic ...";
|
|
while (o->isInEvent()) piMSleep(1);
|
|
//piCout << "[Deleter] delete" << (uintptr_t)o << "wait atomic done";
|
|
if (o->isPIObject()) delete o;
|
|
}
|
|
//piCout << "[Deleter] delete" << (uintptr_t)o << "done";
|
|
}
|