Files
pip/libs/main/core/piobject.cpp
peri4 caa7880cc4 get rid of piForeach
apply some code analyzer recommendations
ICU flag now check if libicu exists
prepare for more accurate growth of containers (limited PoT, then constantly increase size)
2024-11-20 20:01:47 +03:00

901 lines
30 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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 "piconditionvar.h"
#include "pithread.h"
#ifndef MICRO_PIP
# include "pifile.h"
# include "piiostream.h"
# include "pisysteminfo.h"
#endif
//! \~\class PIObject piobject.h
//! \~\details
//! \~english \section PIObject_sec0 Events and Event handlers
//! \~russian \section PIObject_sec0 События и Обработчики событий
//!
//! \~english
//! %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 as common method.
//! Event is a function but declared with special macro \a EVENT() and don`t need definition.
//! To raise event simply execute event function.
//!
//! Event handler is a function but declared with special macro
//! \a EVENT_HANDLER(). It need definition as common method.
//! 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(), \a CONNECTU() and \a CONNECTL() 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.
//!
//! * \a CONNECT() macros family work with explicit subclasses of %PIObject with compile-time check of events and handlers
//! * \a CONNECTU() macro can work implicit subclasses of %PIObject with run-time check of events and handlers
//! * \a CONNECTU_QUEUED() macro similar to \a CONNECTU() macro but queue execution with performer object instead of handler direct call
//! * \a CONNECTL() macro work with implicit subclasses of %PIObject and lambda-function
//!
//! \~russian
//! %PIObject предоставляет механизм событий и их обработчиков, реализованный без
//! дополнительного препроцессора или метакомпилятора. Любой класс, наследованный
//! от %PIObject должен использовать макрос \a PIOBJECT() сразу после объявления
//! класса для корректной работы.
//!
//! Событием является сигнал, который может быть вызван как обычный метод в любое время.
//! Это метод, объявленный с помощью макроса \a EVENT() и не требует описания.
//! Для его вызова просто вызывается метод события.
//!
//! Обработчик события это метод, объявленный с помощью макроса \a EVENT_HANDLER()
//! и он требует описания, как и обычный метод. Можно его использовать как обычный метод.
//!
//! Основной функцией этого механизма является реализация соединений между различными объектами.
//! Её предоставляют макросы \a CONNECT(), \a CONNECTU() и \a CONNECTL(), которые соединяют
//! события одних объектов с обработчиками или событиями других объектов. Каждое событие
//! может быть присоеденино неограниченное количество раз к любым обработчикам.
//!
//! * \a CONNECT() семейство макросов работает с явными наследниками %PIObject, и проверяет соединение во время компиляции
//! * \a CONNECTU() макрос может работать с неявными наследниками %PIObject, и проверяет соединение во время исполнения
//! * \a CONNECTU_QUEUED() макрос подобен \a CONNECTU(), но планирует вызов обработчика у объекта performer вместо прямого вызова
//! * \a CONNECTL() макрос может работать с неявными наследниками %PIObject и лямбда-функцией
//!
//! \~\image html events_handlers.png
//!
//! \~english Example:
//! \~russian Пример:
//!
//! \~\snippet piobject.cpp main
//! \~english Result:
//! \~russian Результат:
//! \~\code{.cpp}
//! handler B: 2 , 0.5
//! handler A: event to handler
//! handler A: event to event
//! event to lambda
//! \endcode
//!
//! \~\class PIObject::Connection piobject.h
//! \~\details
//!
PIObject::__MetaFunc::__MetaFunc() {
for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) {
types[i] = names[i] = nullptr;
types_id[i] = 0;
}
}
int PIObject::__MetaFunc::argumentsCount() const {
for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i)
if (!types[i]) return i;
return __PIOBJECT_MAX_ARGS__;
}
PIString PIObject::__MetaFunc::arguments() const {
PIString ret;
for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) {
if (!types[i]) break;
if (!ret.isEmpty()) ret += ',';
ret += PIStringAscii(types[i]);
}
return ret;
}
PIString PIObject::__MetaFunc::fullFormat() const {
PIString ret = PIStringAscii(type_ret) + " " + PIStringAscii(scope) + "::" + PIStringAscii(func_name) + "(";
for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) {
if (!types[i]) break;
if (i > 0) ret += ", ";
ret += PIStringAscii(types[i]) + " " + PIStringAscii(names[i]);
}
ret += ")";
return ret;
}
void PIObject::__MetaFunc::__setFuncName(const char * n) {
func_name = n;
func_name_id = PIStringAscii(n).hash();
}
void PIObject::__MetaFunc::__addArgument(const char * t, const char * n) {
for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) {
if (types[i]) continue;
types[i] = t;
names[i] = n;
types_id[i] = PIObject::simplifyType(t, false).hash();
break;
}
// PICout(PICoutManipulators::DefaultControls | PICoutManipulators::AddQuotes)
// << "__addArgument" << t << n << PIObject::simplifyType(t) << types_id.back();
}
bool PIObject::__MetaFunc::canConnectTo(const __MetaFunc & dst, int & args_count) const {
for (int i = 0; i < __PIOBJECT_MAX_ARGS__; ++i) {
// piCout << "canConnectTo" << i << types[i] << dst.types[i];
args_count = i;
if (!dst.types[i]) break;
if (!types[i]) return false;
if (types_id[i] != dst.types_id[i]) return false;
}
return true;
}
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();
deleted(this);
piDisconnectAll();
_signature_ = 0;
}
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;
}
PIStringList PIObject::scopeList() const {
PIStringList ret;
ret.reserve(2);
PIMutexLocker ml(__meta_mutex());
const PIVector<const char *> & scope(__meta_data()[classNameID()].scope_list);
for (const char * c: scope)
ret << PIStringAscii(c);
return ret;
}
PIStringList PIObject::methodsEH() const {
PIMutexLocker ml(__meta_mutex());
PIStringList ret;
const __MetaData & ehd(__meta_data()[classNameID()]);
for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++)
ret << eh.value().fullFormat();
return ret;
}
bool PIObject::isMethodEHContains(const PIString & name) const {
uint search_id = name.hash();
PIMutexLocker ml(__meta_mutex());
const __MetaData & ehd(__meta_data()[classNameID()]);
for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) {
if (eh.value().func_name_id == search_id) return true;
}
return false;
}
PIString PIObject::methodEHArguments(const PIString & name) const {
uint search_id = name.hash();
PIMutexLocker ml(__meta_mutex());
const __MetaData & ehd(__meta_data()[classNameID()]);
for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) {
if (eh.value().func_name_id == search_id) return eh.value().arguments();
}
return PIString();
}
PIString PIObject::methodEHFullFormat(const PIString & name) const {
uint search_id = name.hash();
PIMutexLocker ml(__meta_mutex());
const __MetaData & ehd(__meta_data()[classNameID()]);
for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) {
if (eh.value().func_name_id == search_id) 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 {
uint search_id = name.hash();
PIVector<__MetaFunc> ret;
const __MetaData & ehd(__meta_data()[classNameID()]);
for (auto eh = ehd.eh_func.begin(); eh != ehd.eh_func.end(); eh++) {
if (eh.value().func_name_id == search_id) 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);
}
PIObject::Connection 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);
Connection conn(ev_h, e_h, sig, src, dest_o, dest, args);
src->connections << conn;
// 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";
return conn;
}
PIObject::Connection 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 Connection();
if (!src->isPIObject()) {
piCout << "[piConnectU] \"" << sig << "\" -> \"" << hname << "\" error: source object is not PIObject! (" << loc << ")";
return Connection();
}
if (!dest_o->isPIObject()) {
piCout << "[piConnectU] \"" << sig << "\" -> \"" << hname << "\" error: destination object is not PIObject! (" << loc << ")";
return Connection();
}
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 Connection();
}
if (m_dest.isEmpty()) {
piCout << "[piConnectU] Error: can`t find handler \"" << hname << "\" in class \"" << dest_o->className() << "\"! (" << loc << ")";
return Connection();
}
void *addr_src(0), *addr_dest(0);
int args(0);
bool que = (performer != 0);
for (const auto & fs: m_src) {
if (addr_src != 0) break;
for (const auto & fd: m_dest) {
if (addr_src != 0) break;
if (fs.canConnectTo(fd, args)) {
addr_src = fs.addr;
addr_dest = que ? fd.addrV : fd.addr;
}
}
}
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 Connection();
}
Connection conn(addr_dest, addr_src, sig, src, dest_o, dest, args, performer);
src->connections << conn;
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 conn;
}
PIObject::Connection PIObject::piConnectLS(PIObject * src, const PIString & sig, std::function<void()> * f, const char * loc) {
if (src == 0) {
delete f;
return Connection();
}
if (!src->isPIObject()) {
piCout << "[piConnectLS] \"" << sig << "\" -> [lambda] error: source object is not PIObject! (" << loc << ")";
delete f;
return Connection();
}
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 Connection();
}
if (m_src.size() != 1) {
PIString ffm = m_src[0].fullFormat();
for (int i = 1; i < m_src.size_s(); ++i) {
if (ffm != m_src[i].fullFormat()) {
piCout << "[piConnectLS] Error: can`t connect overloaded event \"" << sig << "\" in class \"" << src->className()
<< "\", found " << m_src.size() << " events! (" << loc << ")";
delete f;
return Connection();
}
}
}
PIObject::Connection conn(0, m_src[0].addr, sig, src);
// piCout << "found";
conn.functor = f;
src->connections << conn;
// piCout << "finished";
return conn;
}
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;
src->connections[i].destroy();
src->connections.remove(i);
i--;
if (dest) {
#if !defined(ANDROID) && !defined(MAC_OS) && !defined(MICRO_PIP)
PIMutexLocker _mld(dest->mutex_connect, src != dest);
#endif
dest->updateConnectors();
}
}
}
}
void PIObject::piDisconnectAll() {
PIMutexLocker _ml(mutex_connect);
PIVector<PIObject *> cv = connectors.toVector();
// piCout << "disconnect connectors =" << connectors.size();
for (auto * o: cv) {
// piCout << "disconnect"<< src << o;
if (!o || (o == this)) continue;
if (!o->isPIObject()) continue;
#if !defined(ANDROID) && !defined(MAC_OS) && !defined(MICRO_PIP)
PIMutexLocker _mld(o->mutex_connect, this != 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 == this) {
oc[i].destroy();
oc.remove(i);
--i;
}
}
}
// piCout << "disconnect connections =" << connections.size();
for (const auto & c: connections) {
if (c.functor) continue;
if (!c.dest_o) continue;
if (!c.dest_o->isPIObject()) continue;
c.dest_o->connectors.remove(this);
}
for (int i = 0; i < connections.size_s(); ++i)
connections[i].destroy();
connections.clear();
}
void PIObject::updateConnectors() {
// piCout << "*** updateConnectors" << this;
connectors.clear();
PIMutexLocker _ml(mutexObjects());
for (auto * o: objects()) {
if (o == this) continue;
PIVector<Connection> & oc(o->connections);
for (auto & 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();
for (const auto & 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();
}
}
//! \details
//! \~english
//! On first call background thread started to delete objects.
//! Each object deletes when it`s outside from any events and hadlers.
//! \~russian
//! При первом вызове стартует фоновый поток для удаления объектов.
//! Каждый объект из очереди удаляется только когда выйдет из всех
//! событий и обработок.
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.argumentsCount();
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, bool readable) {
PIString ret = PIStringAscii(a).trim();
if (readable) {
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(" ]", ']');
ret.replaceAll(" <", '<');
ret.replaceAll(" >", '>');
ret.replaceAll(" &", '&');
ret.replaceAll(" *", '*');
ret.replaceAll("[ ", '[');
ret.replaceAll("] ", ']');
ret.replaceAll("< ", '<');
ret.replaceAll("> ", '>');
ret.replaceAll("& ", '&');
ret.replaceAll("* ", '*');
if (ret.startsWith("const ") && ret.endsWith("&")) ret.cutLeft(6).cutRight(1).trim();
} else {
if (ret.startsWith("const ") && ret.endsWith("&")) ret.cutLeft(6).cutRight(1).trim();
ret.removeAll(' ').removeAll('\t').removeAll('\r').removeAll('\n');
}
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());
const char * o_name = "name";
auto it = properties_.makeIterator();
while (it.next()) {
if (it.key() != piHashData((const uchar *)o_name, strlen(o_name)))
PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << it.key() << ": " << it.value();
}
// 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.begin(); eh != ehd.eh_func.end(); 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());
for (const Connection & c: connections) {
PIObject * dst = c.dest_o;
__MetaFunc ef = methodEH(c.signal);
PIString src(c.event);
if (ef.func_name) src = PIStringAscii(ef.func_name) + "(" + ef.arguments() + ")";
if (dst) {
__MetaFunc hf = dst->methodEH(c.slot);
PIString hf_fn;
if (!hf.func_name)
hf_fn = "[BROKEN]";
else
hf_fn = PIStringAscii(hf.func_name) + "(" + hf.arguments() + ")";
PICout(PICoutManipulators::AddNewLine) << line_prefix << " " << src << " -> " << dst->className() << " (" << c.dest
<< ", \"" << dst->name() << "\")::" << hf_fn;
} 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 << "}";
}
#ifndef MICRO_PIP
void dumpApplication(bool with_objects) {
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();
if (with_objects) {
for (const PIObject * o: PIObject::objects())
o->dump(" ");
}
PICout(PICoutManipulators::AddNewLine) << " }";
PICout(PICoutManipulators::AddNewLine) << "}";
// printf("dump application done\n");
}
bool dumpApplicationToFile(const PIString & path, bool with_objects) {
PIFile f(path + "_tmp");
f.setName("_S.DumpFile");
f.clear();
if (!f.open(PIIODevice::WriteOnly)) return false;
auto out_devs = PICout::currentOutputDevices();
PICout::setOutputDevices(PICout::Buffer);
PICout::clearBuffer();
dumpApplication(with_objects);
PIIOTextStream ts(&f);
ts << PICout::getBuffer();
f.close();
PICout::setOutputDevices(out_devs);
PIFile::rename(path + "_tmp", path);
return true;
}
#endif
void PIObject::__MetaData::addScope(const char * s, uint shash) {
if (!scope_id.contains(shash)) {
scope_list << s;
scope_id << shash;
}
}
void PIObject::Connection::destroy() {
if (functor) delete functor;
functor = nullptr;
}
PIObject::Connection::Connection() {
slot = signal = dest = nullptr;
src_o = dest_o = performer = nullptr;
functor = nullptr;
eventID = 0;
args_count = 0;
}
bool PIObject::Connection::disconnect() const {
if (!isValid() || !src_o) return false;
if (!src_o->isPIObject()) return false;
bool ndm = dest_o && (src_o != dest_o), ret = false, found = false;
if (dest_o) {
if (!dest_o->isPIObject()) ndm = false;
}
PIMutexLocker _ml(src_o->mutex_connect);
if (ndm) dest_o->mutex_connect.lock();
for (int i = 0; i < src_o->connections.size_s(); ++i) {
Connection & cc(src_o->connections[i]);
if (cc.eventID == eventID) {
if (dest_o && (cc.dest_o == dest_o)) {
if (cc.slot == slot) found = true;
}
if (functor && (cc.functor == functor)) found = true;
}
if (found) {
src_o->connections[i].destroy();
src_o->connections.remove(i);
ret = true;
break;
}
}
if (dest_o) {
if (dest_o->isPIObject()) dest_o->updateConnectors();
}
if (ndm) dest_o->mutex_connect.unlock();
return ret;
}
PRIVATE_DEFINITION_START(PIObject::Deleter)
PIThread thread;
PIConditionVariable cond_var;
PIVector<PIObject *> obj_queue;
PRIVATE_DEFINITION_END(PIObject::Deleter)
PIObject::Deleter::Deleter() {
// piCout << "Deleter start ...";
PRIVATE->thread.setSlot([this]() {
PIVector<PIObject *> oq;
PRIVATE->thread.lock();
if (PRIVATE->obj_queue.isEmpty()) PRIVATE->cond_var.wait(PRIVATE->thread.mutex());
oq.swap(PRIVATE->obj_queue);
PRIVATE->thread.unlock();
for (PIObject * o: oq)
deleteObject(o);
});
PRIVATE->thread.start();
}
PIObject::Deleter::~Deleter() {
// piCout << "~Deleter ...";
PRIVATE->thread.stop();
PRIVATE->cond_var.notifyAll();
PRIVATE->thread.waitForFinish();
for (PIObject * o: PRIVATE->obj_queue)
deleteObject(o);
// 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->thread.lock();
if (!PRIVATE->obj_queue.contains(o)) {
PRIVATE->obj_queue << o;
PRIVATE->cond_var.notifyAll();
}
PRIVATE->thread.unlock();
// piCout << "[Deleter] post" << o << "done";
}
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())
piMinSleep();
// piCout << "[Deleter] delete" << (uintptr_t)o << "wait atomic done";
delete o;
}
// piCout << "[Deleter] delete" << (uintptr_t)o << "done";
}