25 Commits

Author SHA1 Message Date
9e5a5970a3 Improved PIBlockingDeque constructors
- resolve creation from constant (see test construct_from_constant_is_max_size_eq_capacity)
- add tests for constructors
2020-08-11 12:30:28 +03:00
46d93c6c9f Improved PIBlockingDeque behaviour and unit tests for put, offer, take methods
- Methods put, offer, take begins working with move and copy semantics
- Mocking queue condition variables with GMock in Unit tests
- Rewrite part of unit tests
2020-08-07 19:12:09 +03:00
662a2fc464 Refactor PIBlockingDequeue 2020-08-07 10:10:05 +03:00
194389ef6d Refactor templates & submit doc 2020-08-06 13:23:49 +03:00
3cfdda7365 PIThreadPoolExecutor & PIBlockingDequeue improvements
- add support move & copy semantic
- introduce submit method for executor with future result
2020-08-05 22:59:33 +03:00
3ec1ecfb5b refactor concurrent module code 2020-08-04 16:39:08 +03:00
be51728570 Merge remote-tracking branch 'origin/master' into concurrent 2020-08-03 17:48:36 +03:00
41e54e5859 Merge pip2 2020-08-03 17:47:19 +03:00
9cd108cf20 tests binary dir fix 2020-08-03 11:10:27 +03:00
22208fbf51 tests binary dir fix 2020-08-03 11:10:05 +03:00
badaa01deb Merge remote-tracking branch 'origin/master' into concurrent
# Conflicts:
#	lib/main/thread/pithreadpoolexecutor.cpp
#	lib/main/thread/pithreadpoolexecutor.h
#	tests/concurrent/ExecutorIntegrationTest.cpp
#	tests/concurrent/ExecutorUnitTest.cpp
#	tests/concurrent/testutil.h
2020-08-03 10:18:52 +03:00
e8a066abcd doc 2020-08-03 09:04:50 +03:00
427e7411c1 move most old PIMap iterators to new
Documentation of PIVector, PIMap and PIMapIterator
2020-08-03 01:43:23 +03:00
df457a1602 rename "iterate" 2020-08-02 19:18:28 +03:00
1dfcaf4952 Merge branch 'master' of https://git.shs.tools/SHS/pip 2020-08-02 15:57:27 +03:00
b468a6d581 PIMapIterator
small PIString optimize
general PIChunkStream pack optimization
2020-08-02 15:57:21 +03:00
b68e8f7a65 Merge picloud branch 2020-08-02 14:00:46 +03:00
1fb5356825 move picompress.h 2020-08-01 23:34:23 +03:00
01f7b15818 remove " " 2020-08-01 23:27:10 +03:00
284971fe8d Merge branch 'master' of https://git.shs.tools/SHS/pip 2020-08-01 23:24:16 +03:00
edacac54f9 PIByteArray rvalue, some fixes 2020-08-01 23:24:08 +03:00
8efd2cf447 Rewrite executor to template & come back executor unit tests 2020-07-17 18:36:28 +03:00
101164902a Merge branch 'master' into picloud 2020-06-28 09:09:59 +03:00
d3ffc19610 Merge commit '02ac4020d3f7a948c87dd0349db093934c0aed3c' into picloud 2020-06-18 19:09:21 +03:00
3f7f67e198 picloud start develop 2020-06-15 11:09:34 +03:00
39 changed files with 1534 additions and 551 deletions

View File

@@ -224,6 +224,7 @@ if (DEFINED LIBPROJECT)
endif() endif()
if (TESTS) if (TESTS)
set(PIP_ROOT_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
@@ -658,12 +659,14 @@ message(" Modules:")
foreach(_m ${PIP_SRC_MODULES}) foreach(_m ${PIP_SRC_MODULES})
message(" ${_m}: ${PIP_MSG_${_m}}") message(" ${_m}: ${PIP_MSG_${_m}}")
endforeach() endforeach()
if (PIP_TESTS_LIST)
message("") message("")
if (PIP_TESTS_LIST)
message(" Tests:") message(" Tests:")
foreach(_test ${PIP_TESTS_LIST}) foreach(_test ${PIP_TESTS_LIST})
message(" * ${_test}") message(" * ${_test}")
endforeach() endforeach()
else()
message(" Tests: skip (tests off)")
endif() endif()
message("") message("")
message(" Utilites:") message(" Utilites:")

View File

@@ -23,3 +23,18 @@
PICloudClient::PICloudClient() { PICloudClient::PICloudClient() {
} }
PICloudClient::~PICloudClient() {
}
bool PICloudClient::openDevice() {
}
bool PICloudClient::closeDevice() {
}

View File

@@ -26,14 +26,23 @@
#include "pip_cloud_export.h" #include "pip_cloud_export.h"
#include "piiodevice.h" #include "piiodevice.h"
class PIEthernet;
class PIP_CLOUD_EXPORT PICloudClient {
class PIP_CLOUD_EXPORT PICloudClient : public PIIODevice
{
PIIODEVICE(PICloudClient)
public: public:
//! //!
explicit PICloudClient(); explicit PICloudClient();
virtual ~PICloudClient();
protected:
bool openDevice();
bool closeDevice();
private: private:
PIEthernet * eth;
}; };
#endif // PICCLOUDCLIENT_H #endif // PICCLOUDCLIENT_H

View File

@@ -108,7 +108,7 @@ void PICodeParser::parseFile(const PIString & file, bool follow_includes) {
piForeachC (Enum & c, enums) { piForeachC (Enum & c, enums) {
piCout << PIStringAscii("enum") << c.name << c.meta; piCout << PIStringAscii("enum") << c.name << c.meta;
piForeachC (EnumeratorInfo & e, c.members) piForeachC (EnumeratorInfo & e, c.members)
piCout << " " << e.name << "=" << e.value << e.meta; piCout << " " << e.name << '=' << e.value << e.meta;
} }
piCout << "\n\nTypedefs:"; piCout << "\n\nTypedefs:";
piForeachC (Typedef & c, typedefs) piForeachC (Typedef & c, typedefs)
@@ -277,11 +277,11 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) {
int nl = pfc.size_s(); int nl = pfc.size_s();
if (pl == nl) break; if (pl == nl) break;
pl = nl; pl = nl;
if (pfc.left(9) == "namespace") { if (pfc.left(9) == PIStringAscii("namespace")) {
pfc.cutLeft(pfc.find('{') + 1); pfc.cutLeft(pfc.find('{') + 1);
continue; continue;
} }
if (pfc.left(8) == "template") { if (pfc.left(8) == PIStringAscii("template")) {
pfc.cutLeft(8); pfc.cutLeft(8);
pfc.takeRange('<', '>'); pfc.takeRange('<', '>');
bool def = !isDeclaration(pfc, 0, &end); bool def = !isDeclaration(pfc, 0, &end);
@@ -290,11 +290,11 @@ bool PICodeParser::parseFileContent(PIString & fc, bool main) {
else pfc.takeSymbol(); else pfc.takeSymbol();
continue; continue;
} }
if (pfc.left(5) == PIStringAscii("class") || pfc.left(6) == "struct" || pfc.left(5) == "union") { if (pfc.left(5) == PIStringAscii("class") || pfc.left(6) == PIStringAscii("struct") || pfc.left(5) == PIStringAscii("union")) {
int dind = pfc.find('{', 0), find = pfc.find(';', 0); int dind = pfc.find('{', 0), find = pfc.find(';', 0);
if (dind < 0 && find < 0) {pfc.cutLeft(6); continue;} if (dind < 0 && find < 0) {pfc.cutLeft(6); continue;}
if (dind < 0 || find < dind) {pfc.cutLeft(6); continue;} if (dind < 0 || find < dind) {pfc.cutLeft(6); continue;}
ccmn = pfc.left(dind) + "{\n" + pfc.mid(dind).takeRange('{', '}') + "\n}\n"; ccmn = pfc.left(dind) + PIStringAscii("{\n") + pfc.mid(dind).takeRange('{', '}') + PIStringAscii("\n}\n");
pfc.remove(0, ccmn.size()); pfc.remove(0, ccmn.size());
parseClass(0, ccmn); parseClass(0, ccmn);
continue; continue;
@@ -363,7 +363,7 @@ PICodeParser::Entity * PICodeParser::parseClassDeclaration(const PIString & fc)
cur_def_vis = (is_class ? Private : Public); cur_def_vis = (is_class ? Private : Public);
PIString cn = cd.mid(6).trim(); PIString cn = cd.mid(6).trim();
bool has_name = !cn.isEmpty(); bool has_name = !cn.isEmpty();
if (cn.isEmpty()) cn = "<unnamed_" + PIString::fromNumber(anon_num++) + '>'; if (cn.isEmpty()) cn = PIStringAscii("<unnamed_") + PIString::fromNumber(anon_num++) + '>';
//piCout << "found " << typename_ << cn; //piCout << "found " << typename_ << cn;
if (cn.isEmpty()) return 0; if (cn.isEmpty()) return 0;
Entity * e = new Entity(); Entity * e = new Entity();
@@ -393,7 +393,7 @@ PIString PICodeParser::parseClass(Entity * parent, PIString & fc) {
int ps = -1; int ps = -1;
bool def = false; bool def = false;
PIString prev_namespace = cur_namespace, stmp; PIString prev_namespace = cur_namespace, stmp;
cur_namespace = ce->name + "::"; cur_namespace = ce->name + PIStringAscii("::");
//piCout << "parse class" << ce->name << "namespace" << cur_namespace; //piCout << "parse class" << ce->name << "namespace" << cur_namespace;
//piCout << "\nparse class" << ce->name << "namespace" << cur_namespace; //piCout << "\nparse class" << ce->name << "namespace" << cur_namespace;
while (!fc.isEmpty()) { while (!fc.isEmpty()) {
@@ -425,7 +425,7 @@ PIString PICodeParser::parseClass(Entity * parent, PIString & fc) {
} }
if (cw == PIStringAscii("friend")) {fc.cutLeft(fc.find(';') + 1); continue;} if (cw == PIStringAscii("friend")) {fc.cutLeft(fc.find(';') + 1); continue;}
if (cw == PIStringAscii("typedef")) {ce->typedefs << parseTypedef(fc.takeLeft(fc.find(';'))); typedefs << ce->typedefs.back(); typedefs.back().first.insert(0, cur_namespace); if (ce->typedefs.back().first.isEmpty()) ce->typedefs.pop_back(); fc.takeSymbol(); continue;} if (cw == PIStringAscii("typedef")) {ce->typedefs << parseTypedef(fc.takeLeft(fc.find(';'))); typedefs << ce->typedefs.back(); typedefs.back().first.insert(0, cur_namespace); if (ce->typedefs.back().first.isEmpty()) ce->typedefs.pop_back(); fc.takeSymbol(); continue;}
if (cw == "template") { if (cw == PIStringAscii("template")) {
fc.takeRange('<', '>'); fc.takeRange('<', '>');
def = !isDeclaration(fc, 0, &end); def = !isDeclaration(fc, 0, &end);
fc.cutLeft(end); fc.cutLeft(end);
@@ -453,7 +453,7 @@ PICodeParser::MetaMap PICodeParser::parseMeta(PIString & fc) {
if (fc.isEmpty()) return ret; if (fc.isEmpty()) return ret;
PIStringList ml = fc.split(','); PIStringList ml = fc.split(',');
piForeachC (PIString & m, ml) { piForeachC (PIString & m, ml) {
int i = m.find("="); int i = m.find('=');
if (i < 0) continue; if (i < 0) continue;
PIString mv = m.mid(i + 1).trim(); PIString mv = m.mid(i + 1).trim();
if (mv.startsWith('\"')) mv.cutLeft(1); if (mv.startsWith('\"')) mv.cutLeft(1);
@@ -466,6 +466,8 @@ PICodeParser::MetaMap PICodeParser::parseMeta(PIString & fc) {
bool PICodeParser::parseEnum(Entity * parent, const PIString & name, PIString fc, const MetaMap & meta) { bool PICodeParser::parseEnum(Entity * parent, const PIString & name, PIString fc, const MetaMap & meta) {
static const PIString s_ss = PIStringAscii(" ");
static const PIString s_M = PIStringAscii("$M");
//piCout << PIStringAscii("enum") << name << fc; //piCout << PIStringAscii("enum") << name << fc;
Enum e(name); Enum e(name);
e.meta = meta; e.meta = meta;
@@ -474,12 +476,12 @@ bool PICodeParser::parseEnum(Entity * parent, const PIString & name, PIString fc
int cv = -1, ind = 0; int cv = -1, ind = 0;
piForeach (PIString & v, vl) { piForeach (PIString & v, vl) {
MetaMap meta; MetaMap meta;
int mi = v.find(PIStringAscii("$M")); int mi = v.find(s_M);
if (mi >= 0) { if (mi >= 0) {
meta = tmp_meta.value(v.takeMid(mi, 5)); meta = tmp_meta.value(v.takeMid(mi, 5));
v.replaceAll(" ", ' '); v.replaceAll(s_ss, ' ');
} }
vn = v; ind = v.find("="); vn = v; ind = v.find('=');
if (ind > 0) {cv = v.right(v.size_s() - ind - 1).toInt(); vn = v.left(ind);} if (ind > 0) {cv = v.right(v.size_s() - ind - 1).toInt(); vn = v.left(ind);}
if (ind < 0) ++cv; if (ind < 0) ++cv;
e.members << EnumeratorInfo(vn.trim(), cv, meta); e.members << EnumeratorInfo(vn.trim(), cv, meta);
@@ -512,8 +514,27 @@ PICodeParser::Typedef PICodeParser::parseTypedef(PIString fc) {
bool PICodeParser::parseMember(Entity * parent, PIString & fc) { bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
static const PIString s_operator = PIStringAscii("operator");
static const PIString s_ss = PIStringAscii(" ");
static const PIString s_cs = PIStringAscii(", ");
static const PIString s_sb = PIStringAscii(" (");
static const PIString s_sM = PIStringAscii(" $M");
static const PIString s_M = PIStringAscii("$M");
static const PIString s_T = PIStringAscii("$T");
static const PIString s_inline_s = PIStringAscii("inline ");
static const PIString s_static_s = PIStringAscii("static ");
static const PIString s_virtual_s = PIStringAscii("virtual ");
static const PIString s_void = PIStringAscii("void");
static const PIString s_using = PIStringAscii("using");
static const PIString s_s5 = PIStringAscii(" ");
static const PIString s_s_const_s = PIStringAscii(" const ");
static const PIString s_s_static_s = PIStringAscii(" static ");
static const PIString s_s_mutable_s = PIStringAscii(" mutable ");
static const PIString s_s_volatile_s = PIStringAscii(" volatile ");
static const PIString s_s_extern_s = PIStringAscii(" extern ");
if (fc.trim().isEmpty()) return true; if (fc.trim().isEmpty()) return true;
if (fc.find("operator") >= 0) return true; if (fc.find(s_operator) >= 0) return true;
tmp_temp.clear(); tmp_temp.clear();
//piCout << "parse member" << fc; //piCout << "parse member" << fc;
int ts = fc.find('<'), te = 0; int ts = fc.find('<'), te = 0;
@@ -521,22 +542,22 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
while (ts >= 0) { while (ts >= 0) {
ctemp = fc.mid(ts).takeRange('<', '>'); ctemp = fc.mid(ts).takeRange('<', '>');
if (ctemp.isEmpty()) {te = ts + 1; ts = fc.find('<', te); continue;} if (ctemp.isEmpty()) {te = ts + 1; ts = fc.find('<', te); continue;}
crepl = PIStringAscii("$T") + PIString::fromNumber(tmp_temp.size_s()).expandLeftTo(3, '0'); crepl = s_T + PIString::fromNumber(tmp_temp.size_s()).expandLeftTo(3, '0');
fc.replace(ts, ctemp.size_s() + 2, crepl); fc.replace(ts, ctemp.size_s() + 2, crepl);
tmp_temp[crepl] = '<' + ctemp + '>'; tmp_temp[crepl] = '<' + ctemp + '>';
ts = fc.find('<', te); ts = fc.find('<', te);
} }
fc.replaceAll('\n', ' ').replaceAll('\t', ' ').replaceAll(" ", ' ').replaceAll(", ", ',').replaceAll(PIStringAscii(" ("), '(').replaceAll(PIStringAscii(" $M"), PIStringAscii("$M")); fc.replaceAll('\n', ' ').replaceAll('\t', ' ').replaceAll(s_ss, ' ').replaceAll(s_cs, ',').replaceAll(s_sb, '(').replaceAll(s_sM, s_M);
//piCout << "parse member" << fc; //piCout << "parse member" << fc;
PIStringList tl, al; PIStringList tl, al;
Member me; Member me;
//piCout << fc; //piCout << fc;
if (fc.contains('(')) { if (fc.contains('(')) {
MetaMap meta; MetaMap meta;
int ind = fc.find(PIStringAscii("$M")); int ind = fc.find(s_M);
if (ind >= 0) { if (ind >= 0) {
meta = tmp_meta.value(fc.takeMid(ind, 5)); meta = tmp_meta.value(fc.takeMid(ind, 5));
fc.replaceAll(PIStringAscii(" "), ' ').replaceAll(PIStringAscii(" ("), '('); fc.replaceAll(s_ss, ' ').replaceAll(s_sb, '(');
} }
fc.cutRight(fc.size_s() - fc.findLast(')') - 1); fc.cutRight(fc.size_s() - fc.findLast(')') - 1);
te = fc.find('('); te = fc.find('(');
@@ -550,26 +571,26 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
me.arguments_full = fc.takeMid(ts + 2).cutRight(1).split(','); me.arguments_full = fc.takeMid(ts + 2).cutRight(1).split(',');
me.type = fc.cutRight(1).trim(); me.type = fc.cutRight(1).trim();
me.visibility = cur_def_vis; me.visibility = cur_def_vis;
if (me.type.find(PIStringAscii("inline ")) >= 0) { if (me.type.find(s_inline_s) >= 0) {
me.attributes |= Inline; me.attributes |= Inline;
me.type.removeAll(PIStringAscii("inline ")); me.type.removeAll(s_inline_s);
} }
if (me.type.find(PIStringAscii("static ")) >= 0) { if (me.type.find(s_static_s) >= 0) {
me.attributes |= Static; me.attributes |= Static;
me.type.removeAll(PIStringAscii("static ")); me.type.removeAll(s_static_s);
} }
if (me.type.find(PIStringAscii("virtual ")) >= 0) { if (me.type.find(s_virtual_s) >= 0) {
me.attributes |= Virtual; me.attributes |= Virtual;
me.type.removeAll(PIStringAscii("virtual ")); me.type.removeAll(s_virtual_s);
} }
normalizeEntityNamespace(me.type); normalizeEntityNamespace(me.type);
int i = 0; int i = 0;
//piCout << me.arguments_full; //piCout << me.arguments_full;
piForeach (PIString & a, me.arguments_full) piForeach (PIString & a, me.arguments_full)
if ((i = a.find("=")) > 0) if ((i = a.find('=')) > 0)
a.cutRight(a.size_s() - i).trim(); a.cutRight(a.size_s() - i).trim();
for (int j = 0; j < me.arguments_full.size_s(); ++j) for (int j = 0; j < me.arguments_full.size_s(); ++j)
if (me.arguments_full[j] == PIStringAscii("void")) { if (me.arguments_full[j] == s_void) {
me.arguments_full.remove(j); me.arguments_full.remove(j);
--j; --j;
} }
@@ -590,7 +611,7 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
parent->functions << me; parent->functions << me;
} else { } else {
if (fc.endsWith(';')) fc.cutRight(1); if (fc.endsWith(';')) fc.cutRight(1);
if (fc.startsWith(PIStringAscii("using")) || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true; if (fc.startsWith(s_using) || !(fc.contains(' ') || fc.contains('\t') || fc.contains('\n'))) return true;
int bits = extractMemberBits(fc); int bits = extractMemberBits(fc);
tl = fc.split(','); tl = fc.split(',');
//piCout << "member" << fc << tl; //piCout << "member" << fc << tl;
@@ -599,8 +620,8 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
bool vn = true; bool vn = true;
ctemp = tl.front().trim(); ctemp = tl.front().trim();
PIString meta_t; PIString meta_t;
if (ctemp.contains(PIStringAscii("$M"))) if (ctemp.contains(s_M))
meta_t = ctemp.takeMid(ctemp.find(PIStringAscii("$M"))); meta_t = ctemp.takeMid(ctemp.find(s_M));
for (ts = ctemp.size_s() - 1; ts > 0; --ts) { for (ts = ctemp.size_s() - 1; ts > 0; --ts) {
if (vn) {if (!_isCChar(ctemp[ts]) && !ctemp[ts].isDigit() && ctemp[ts] != '[' && ctemp[ts] != ']') vn = false;} if (vn) {if (!_isCChar(ctemp[ts]) && !ctemp[ts].isDigit() && ctemp[ts] != '[' && ctemp[ts] != ']') vn = false;}
else {if (_isCChar(ctemp[ts]) || ctemp[ts].isDigit()) break;} else {if (_isCChar(ctemp[ts]) || ctemp[ts].isDigit()) break;}
@@ -609,26 +630,26 @@ bool PICodeParser::parseMember(Entity * parent, PIString & fc) {
me.visibility = cur_def_vis; me.visibility = cur_def_vis;
ctemp += meta_t; ctemp += meta_t;
restoreTmpTemp(&me); restoreTmpTemp(&me);
PIString type = PIStringAscii(" ") + me.type; PIString type = s_s5 + me.type;
if (type.find(PIStringAscii(" const ")) >= 0) { if (type.find(s_s_const_s) >= 0) {
me.attributes |= Const; me.attributes |= Const;
type.replaceAll(PIStringAscii(" const "), ' '); type.replaceAll(s_s_const_s, ' ');
} }
if (type.find(PIStringAscii(" static ")) >= 0) { if (type.find(s_s_static_s) >= 0) {
me.attributes |= Static; me.attributes |= Static;
type.replaceAll(PIStringAscii(" static "), ' '); type.replaceAll(s_s_static_s, ' ');
} }
if (type.find(PIStringAscii(" mutable ")) >= 0) { if (type.find(s_s_mutable_s) >= 0) {
me.attributes |= Mutable; me.attributes |= Mutable;
type.replaceAll(PIStringAscii(" mutable "), ' '); type.replaceAll(s_s_mutable_s, ' ');
} }
if (type.find(PIStringAscii(" volatile ")) >= 0) { if (type.find(s_s_volatile_s) >= 0) {
me.attributes |= Volatile; me.attributes |= Volatile;
type.replaceAll(PIStringAscii(" volatile "), ' '); type.replaceAll(s_s_volatile_s, ' ');
} }
if (type.find(PIStringAscii(" extern ")) >= 0) { if (type.find(s_s_extern_s) >= 0) {
me.attributes |= Extern; me.attributes |= Extern;
type.replaceAll(PIStringAscii(" extern "), ' '); type.replaceAll(s_s_extern_s, ' ');
} }
type.trim(); type.trim();
normalizeEntityNamespace(type); normalizeEntityNamespace(type);
@@ -677,6 +698,14 @@ int PICodeParser::extractMemberBits(PIString & fc) {
void PICodeParser::normalizeEntityNamespace(PIString & n) { void PICodeParser::normalizeEntityNamespace(PIString & n) {
static const PIString s_const_s = PIStringAscii("const ");
static const PIString s_static_s = PIStringAscii("static ");
static const PIString s_mutable_s = PIStringAscii("mutable ");
static const PIString s_volatile_s = PIStringAscii("volatile ");
static const PIString s_s_const_s = PIStringAscii(" const ");
static const PIString s_s_static_s = PIStringAscii(" static ");
static const PIString s_s_mutable_s = PIStringAscii(" mutable ");
static const PIString s_s_volatile_s = PIStringAscii(" volatile ");
PIString suff, pref; PIString suff, pref;
for (int i = n.size_s() - 1; i > 0; --i) for (int i = n.size_s() - 1; i > 0; --i)
if (_isCChar(n[i]) || n[i].isDigit()) { if (_isCChar(n[i]) || n[i].isDigit()) {
@@ -685,10 +714,10 @@ void PICodeParser::normalizeEntityNamespace(PIString & n) {
break; break;
} }
n.push_front(' '); n.push_front(' ');
if (n.find(PIStringAscii(" static ")) >= 0) {n.replaceAll(PIStringAscii(" static "), ""); pref += PIStringAscii("static ");} if (n.find(s_s_const_s) >= 0) {n.replaceAll(s_s_const_s, ""); pref += s_const_s;}
if (n.find(PIStringAscii(" const ")) >= 0) {n.replaceAll(PIStringAscii(" const "), ""); pref += PIStringAscii("const ");} if (n.find(s_s_static_s) >= 0) {n.replaceAll(s_s_static_s, ""); pref += s_static_s;}
if (n.find(PIStringAscii(" mutable ")) >= 0) {n.replaceAll(PIStringAscii(" mutable "), ""); pref += PIStringAscii("mutable ");} if (n.find(s_s_mutable_s) >= 0) {n.replaceAll(s_s_mutable_s, ""); pref += s_mutable_s;}
if (n.find(PIStringAscii(" volatile ")) >= 0) {n.replaceAll(PIStringAscii(" volatile "), ""); pref += PIStringAscii("volatile ");} if (n.find(s_s_volatile_s) >= 0) {n.replaceAll(s_s_volatile_s, ""); pref += s_volatile_s;}
n.trim(); n.trim();
int f = 0; int f = 0;
piForeachC (Entity * e, entities) { piForeachC (Entity * e, entities) {
@@ -697,28 +726,30 @@ void PICodeParser::normalizeEntityNamespace(PIString & n) {
return; return;
} }
if ((f = e->name.find(n)) >= 0) if ((f = e->name.find(n)) >= 0)
if (e->name.mid(f - 1, 1) == PIChar(':')) if (e->name.at(f - 1) == PIChar(':'))
if (e->name.find(cur_namespace) >= 0) { if (e->name.find(cur_namespace) >= 0) {
n = pref + e->name + suff; n = pref + e->name + suff;
return; return;
} }
} }
piForeachC (Enum & e, enums) piForeachC (Enum & e, enums) {
if ((f = e.name.find(n)) >= 0) if ((f = e.name.find(n)) >= 0)
if (e.name.mid(f - 1, 1) == PIChar(':')) if (e.name.at(f - 1) == PIChar(':'))
if (e.name.find(cur_namespace) >= 0) { if (e.name.find(cur_namespace) >= 0) {
//piCout << "change" << n << "to" << e.name + suff; //piCout << "change" << n << "to" << e.name + suff;
n = pref + e.name + suff; n = pref + e.name + suff;
return; return;
} }
piForeachC (Typedef & e, typedefs) }
piForeachC (Typedef & e, typedefs) {
if ((f = e.first.find(n)) >= 0) if ((f = e.first.find(n)) >= 0)
if (e.first.mid(f - 1, 1) == PIChar(':')) if (e.first.at(f - 1) == PIChar(':'))
if (e.first.find(cur_namespace) >= 0) { if (e.first.find(cur_namespace) >= 0) {
//piCout << "change" << n << "to" << e.name + suff; //piCout << "change" << n << "to" << e.name + suff;
n = pref + e.first + suff; n = pref + e.first + suff;
return; return;
} }
}
n = (pref + n + suff).trim(); n = (pref + n + suff).trim();
} }
@@ -864,9 +895,9 @@ bool PICodeParser::isDeclaration(const PIString & fc, int start, int * end) {
bool PICodeParser::isMainFile(const PIString & fc) { bool PICodeParser::isMainFile(const PIString & fc) {
int si = 0; int si = 0;
while (si >= 0) { while (si >= 0) {
int csi = fc.find(" main", si); int csi = fc.find(PIStringAscii(" main"), si);
if (csi < 0) csi = fc.find("\tmain", si); if (csi < 0) csi = fc.find(PIStringAscii("\tmain"), si);
if (csi < 0) csi = fc.find("\nmain", si); if (csi < 0) csi = fc.find(PIStringAscii("\nmain"), si);
if (csi < 0) return false; if (csi < 0) return false;
si = csi; si = csi;
int fi = fc.find('(', si + 5); int fi = fc.find('(', si + 5);
@@ -874,7 +905,7 @@ bool PICodeParser::isMainFile(const PIString & fc) {
if (fi - si < 10) { if (fi - si < 10) {
PIString ms(fc.mid(si, fi - si + 1)); PIString ms(fc.mid(si, fi - si + 1));
ms.removeAll(' ').removeAll('\t').removeAll('\n'); ms.removeAll(' ').removeAll('\t').removeAll('\n');
if (ms == "main(") return true; if (ms == PIStringAscii("main(")) return true;
} }
si += 5; si += 5;
} }
@@ -925,7 +956,7 @@ PIString PICodeParser::procMacros(PIString fc) {
} }
continue; continue;
} }
if (mif.left(4) == "else" && ifcnt == 0) { if (mif.left(4) == PIStringAscii("else") && ifcnt == 0) {
//piCout << "main else" << skip << grab; //piCout << "main else" << skip << grab;
if (grab) pfc << procMacros(nfc); if (grab) pfc << procMacros(nfc);
if (skip && !cond_ok) {skip = false; grab = true;} if (skip && !cond_ok) {skip = false; grab = true;}
@@ -959,7 +990,7 @@ bool PICodeParser::parseDirective(PIString d) {
if (d.isEmpty()) return true; if (d.isEmpty()) return true;
PIString dname = d.takeCWord(); PIString dname = d.takeCWord();
//piCout << "parseDirective" << d; //piCout << "parseDirective" << d;
if (dname == "include") { if (dname == PIStringAscii("include")) {
d.replaceAll('<', '\"').replaceAll('>', '\"'); d.replaceAll('<', '\"').replaceAll('>', '\"');
PIString cf = cur_file, ifc = d.takeRange('\"', '\"'); PIString cf = cur_file, ifc = d.takeRange('\"', '\"');
if (with_includes) { if (with_includes) {
@@ -982,7 +1013,7 @@ bool PICodeParser::parseDirective(PIString d) {
} }
return true; return true;
} }
if (dname == "undef") { if (dname == PIStringAscii("undef")) {
PIString mname = d.takeCWord(); PIString mname = d.takeCWord();
for (int i = 0; i < defines.size_s(); ++i) for (int i = 0; i < defines.size_s(); ++i)
if (defines[i].first == mname) {defines.remove(i); --i;} if (defines[i].first == mname) {defines.remove(i); --i;}

View File

@@ -23,10 +23,11 @@
#ifndef PICOMPRESS_H #ifndef PICOMPRESS_H
#define PICOMPRESS_H #define PICOMPRESS_H
#include "pip_compress_export.h"
#include "pibytearray.h" #include "pibytearray.h"
PIP_EXPORT PIByteArray piCompress(const PIByteArray & ba, int level = 6); PIP_COMPRESS_EXPORT PIByteArray piCompress(const PIByteArray & ba, int level = 6);
PIP_EXPORT PIByteArray piDecompress(const PIByteArray & zba); PIP_COMPRESS_EXPORT PIByteArray piDecompress(const PIByteArray & zba);
#endif // PICOMPRESS_H #endif // PICOMPRESS_H

View File

@@ -28,147 +28,347 @@
* \fn PIVector::PIVector(); * \fn PIVector::PIVector();
* Contructs an empty vector * Contructs an empty vector
* \fn PIVector::PIVector(ullong size, const Type & value = Type()); * \fn PIVector::PIVector(size_t size, const T & value = T());
* \brief Contructs vector with size "size" filled elements "value" * \brief Contructs vector with size "size" filled elements "value"
* \details Example: \snippet picontainers.cpp PIVector::PIVector * \details Example: \snippet picontainers.cpp PIVector::PIVector
* \fn const Type & PIVector::at(ullong index) const; * \fn const T & PIVector::at(size_t index) const;
* \brief Read-only access to element by index "index" * \brief Read-only access to element by index "index"
* \details Example: \snippet picontainers.cpp PIVector::at_c * \details Example: \snippet picontainers.cpp PIVector::at_c
* \sa \a operator[] * \sa \a operator[]
* \fn Type & PIVector::at(ullong index); * \fn T & PIVector::at(size_t index);
* \brief Full access to element by index "index" * \brief Full access to element by index "index"
* \details Example: \snippet picontainers.cpp PIVector::at * \details Example: \snippet picontainers.cpp PIVector::at
* \sa \a operator[] * \sa \a operator[]
* \fn const Type * PIVector::data(ullong index = 0) const; * \fn const T * PIVector::data(size_t index = 0) const;
* \brief Read-only pointer to element by index "index" * \brief Read-only pointer to element by index "index"
* \details Example: \snippet picontainers.cpp PIVector::data_c * \details Example: \snippet picontainers.cpp PIVector::data_c
* \fn Type * PIVector::data(ullong index = 0); * \fn T * PIVector::data(size_t index = 0);
* \brief Pointer to element by index "index" * \brief Pointer to element by index "index"
* \details Example: \snippet picontainers.cpp PIVector::data * \details Example: \snippet picontainers.cpp PIVector::data
* \fn ullong PIVector::size() const; * \fn size_t PIVector::size() const;
* \brief Elements count * \brief Elements count
* \fn int PIVector::size_s() const; * \fn ssize_t PIVector::size_s() const;
* \brief Elements count * \brief Elements count
* \fn bool PIVector::isEmpty() const; * \fn bool PIVector::isEmpty() const;
* \brief Return \c "true" if vector is empty, i.e. size = 0 * \brief Return \c "true" if vector is empty, i.e. size = 0
* \fn bool PIVector::has(const Type & t) const; * \fn bool PIVector::has(const T & t) const;
* \fn bool PIVector::contains(const Type & v) const; * \fn bool PIVector::contains(const T & v) const;
* \brief Return \c "true" if vector has at least one element equal "t" * \brief Return \c "true" if vector has at least one element equal "t"
* \fn int PIVector::etries(const Type & t) const; * \fn int PIVector::etries(const T & t) const;
* \brief Return how many times element "t" appears in vector * \brief Return how many times element "t" appears in vector
* \fn static int PIVector::compare_func(const Type * t0, const Type * t1); * \fn ssize_t PIVector::indexOf(const T & t) const;
* \brief Standard compare function for type "Type". Return 0 if t0 = t1, -1 if t0 < t1 and 1 if t0 > t1. * \brief Return index of first element equal "t" or -1 if there is no such element
* \fn void PIVector::resize(ullong size, const Type & new_type = Type()); * \fn ssize_t PIVector::lastIndexOf(const T & t) const;
* \brief Return index of last element equal "t" or -1 if there is no such element
* \fn static int PIVector::compare_func(const T * t0, const T * t1);
* \brief Standard compare function for type "T". Return 0 if t0 = t1, -1 if t0 < t1 and 1 if t0 > t1.
* \fn void PIVector::resize(size_t size, const T & new_type = T());
* \brief Resize vector to size "size" * \brief Resize vector to size "size"
* \details Elements removed from end of vector if new size < old size, or added new elements = "new_type" if new size > old size.\n * \details Elements removed from end of vector if new size < old size, or added new elements = "new_type" if new size > old size.\n
* Example: \snippet picontainers.cpp PIVector::resize * Example: \snippet picontainers.cpp PIVector::resize
* \sa \a size(), \a clear() * \sa \a size(), \a clear()
* \fn PIVector<T> & PIVector::enlarge(ullong size); * \fn PIVector & PIVector::enlarge(size_t size);
* \brief Increase vector size with "size" elements * \brief Increase vector size with "size" elements
* \fn void PIVector::clear(); * \fn void PIVector::clear();
* \brief Clear vector. Equivalent to call <tt>"resize(0)"</tt> * \brief Clear vector. Equivalent to call <tt>"resize(0)"</tt>
* \fn PIVector<T> & PIVector::sort(CompareFunc compare = compare_func); * \fn PIVector & PIVector::sort(CompareFunc compare = compare_func);
* \brief Sort vector using quick sort algorithm and standard compare function * \brief Sort vector using quick sort algorithm and standard compare function
* \details Example: \snippet picontainers.cpp PIVector::sort_0 * \details Example: \snippet picontainers.cpp PIVector::sort_0
* With custom compare function: \snippet picontainers.cpp PIVector::sort_1 * With custom compare function: \snippet picontainers.cpp PIVector::sort_1
* \fn PIVector<T> & PIVector::fill(const Type & t); * \fn PIVector & PIVector::fill(const T & t);
* \brief Fill vector with elements "t" leave size is unchanged and return reference to vector * \brief Fill vector with elements "t" leave size is unchanged and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::fill * \details Example: \snippet picontainers.cpp PIVector::fill
* \fn Type & PIVector::back(); * \fn PIVector & PIVector::assign(const T & t = T());
* \brief Synonym of \a fill(t)
* \fn PIVector & PIVector::assign(size_t new_size, const T & t);
* \brief Resize to "new_size", then fill with "t"
* \fn T & PIVector::back();
* \brief Last element of the vector * \brief Last element of the vector
* \fn const Type & PIVector::back() const; * \fn const T & PIVector::back() const;
* \brief Last element of the vector * \brief Last element of the vector
* \fn Type & PIVector::front(); * \fn T & PIVector::front();
* \brief First element of the vector * \brief First element of the vector
* \fn const Type & PIVector::front() const; * \fn const T & PIVector::front() const;
* \brief First element of the vector * \brief First element of the vector
* \fn PIVector<T> & PIVector::push_back(const Type & t); * \fn PIVector & PIVector::push_back(const T & t);
* \brief Add new element "t" at the end of vector and return reference to vector * \brief Add new element "t" at the end of vector and return reference to vector
* \fn PIVector<T> & PIVector::push_front(const Type & t); * \fn PIVector & PIVector::push_front(const T & t);
* \brief Add new element "t" at the beginning of vector and return reference to vector * \brief Add new element "t" at the beginning of vector and return reference to vector
* \fn PIVector<T> & PIVector::pop_back(); * \fn PIVector & PIVector::pop_back();
* \brief Remove one element from the end of vector and return reference to vector * \brief Remove one element from the end of vector and return reference to vector
* \fn PIVector<T> & PIVector::pop_front(); * \fn PIVector & PIVector::pop_front();
* \brief Remove one element from the beginning of vector and return reference to vector * \brief Remove one element from the beginning of vector and return reference to vector
* \fn Type PIVector::take_back(); * \fn T PIVector::take_back();
* \brief Remove one element from the end of vector and return it * \brief Remove one element from the end of vector and return it
* \fn Type PIVector::take_front(); * \fn T PIVector::take_front();
* \brief Remove one element from the beginning of vector and return it * \brief Remove one element from the beginning of vector and return it
* \fn PIVector<T> & PIVector::remove(uint index); * \fn PIVector & PIVector::remove(size_t index);
* \brief Remove one element by index "index" and return reference to vector * \brief Remove one element by index "index" and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::remove_0 * \details Example: \snippet picontainers.cpp PIVector::remove_0
* \sa \a removeOne(), \a removeAll() * \sa \a removeOne(), \a removeAll()
* \fn PIVector<T> & PIVector::remove(uint index, uint count); * \fn PIVector & PIVector::remove(size_t index, size_t count);
* \brief Remove "count" elements by first index "index" and return reference to vector * \brief Remove "count" elements by first index "index" and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::remove_1 * \details Example: \snippet picontainers.cpp PIVector::remove_1
* \sa \a removeOne(), \a removeAll() * \sa \a removeOne(), \a removeAll()
* \fn PIVector<T> & PIVector::removeOne(const Type & v); * \fn PIVector & PIVector::removeOne(const T & v);
* \brief Remove no more than one element equal "v" and return reference to vector * \brief Remove no more than one element equal "v" and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::removeOne * \details Example: \snippet picontainers.cpp PIVector::removeOne
* \sa \a remove(), \a removeAll() * \sa \a remove(), \a removeAll()
* \fn PIVector<T> & PIVector::removeAll(const Type & v); * \fn PIVector & PIVector::removeAll(const T & v);
* \brief Remove all elements equal "v" and return reference to vector * \brief Remove all elements equal "v" and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::removeAll * \details Example: \snippet picontainers.cpp PIVector::removeAll
* \sa \a remove(), \a removeOne() * \sa \a remove(), \a removeOne()
* \fn PIVector<T> & PIVector::insert(uint pos, const Type & t); * \fn PIVector & PIVector::insert(size_t pos, const T & t);
* \brief Insert element "t" after index "pos" and return reference to vector * \brief Insert element "t" after index "pos" and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::insert_0 * \details Example: \snippet picontainers.cpp PIVector::insert_0
* \fn PIVector<T> & PIVector::insert(uint pos, const PIVector<T> & t); * \fn PIVector & PIVector::insert(size_t pos, const PIVector & t);
* \brief Insert other vector "t" after index "pos" and return reference to vector * \brief Insert other vector "t" after index "pos" and return reference to vector
* \details Example: \snippet picontainers.cpp PIVector::insert_1 * \details Example: \snippet picontainers.cpp PIVector::insert_1
* \fn Type & PIVector::operator [](uint index); * \fn T & PIVector::operator [](size_t index);
* \brief Full access to element by index "index" * \brief Full access to element by index "index"
* \details Example: \snippet picontainers.cpp PIVector::() * \details Example: \snippet picontainers.cpp PIVector::()
* \sa \a at() * \sa \a at()
* \fn const Type & PIVector::operator [](uint index) const; * \fn const T & PIVector::operator [](size_t index) const;
* \brief Read-only access to element by index "index" * \brief Read-only access to element by index "index"
* \details Example: \snippet picontainers.cpp PIVector::()_c * \details Example: \snippet picontainers.cpp PIVector::()_c
* \sa \a at() * \sa \a at()
* \fn PIVector<T> & PIVector::operator <<(const Type & t); * \fn PIVector & PIVector::operator <<(const T & t);
* \brief Add new element "t" at the end of vector and return reference to vector * \brief Add new element "t" at the end of vector and return reference to vector
* \fn PIVector<T> & PIVector::operator <<(const PIVector<T> & t); * \fn PIVector & PIVector::operator <<(const PIVector & t);
* \brief Add vector "t" at the end of vector and return reference to vector * \brief Add vector "t" at the end of vector and return reference to vector
* \fn bool PIVector::operator ==(const PIVector<T> & t); * \fn bool PIVector::operator ==(const PIVector & t);
* \brief Compare with vector "t" * \brief Compare with vector "t"
* \fn bool PIVector::operator !=(const PIVector<T> & t); * \fn bool PIVector::operator !=(const PIVector & t);
* \brief Compare with vector "t" * \brief Compare with vector "t"
* */ * */
/** \class PIMap
* \brief Associative array
* \details This class used to store Key = Value array of any
* type of data. \a value() returns value for key and leave map
* unchaged in any case. \a operator [] create entry in map if
* there is no entry for given key. You can retrieve all
* keys by method \a keys() and all values by methos \a values().
* To iterate all entries use class PIMapIterator, or methods
* \a makeIterator() and \a makeReverseIterator().
* \fn PIMap::PIMap();
* \brief Contructs an empty map
* \fn PIMap::PIMap(const PIMap & other);
* \brief Contructs a copy of "other"
* \fn PIMap & PIMap::operator =(const PIMap & other);
* \brief Copy operator
* \fn PIMap::PIMap(const PIMap & other);
* \brief Contructs a copy of "other"
* \fn PIMapIterator PIMap::makeIterator() const
* \brief Returns PIMapIterator for this map
* \fn PIMapIterator PIMap::makeReverseIterator() const
* \brief Returns reverse PIMapIterator for this map
* \fn size_t PIMap::size() const
* \brief Returns entries count
* \fn int PIMap::size_s() const
* \brief Returns entries count
* \fn size_t PIMap::length() const
* \brief Returns entries count
* \fn bool PIMap::isEmpty() const
* \brief Returns if map is empty
* \fn T & PIMap::operator [](const Key & key)
* \brief Returns value for key "key". If there is no key in map, create one.
* \fn const T PIMap::operator [](const Key & key) const
* \brief Returns value for key "key". If there is no key in map, returns default T().
* \fn T & PIMap::at(const Key & key)
* \brief Equivalent to operator []
* \fn const T PIMap::at(const Key & key) const
* \brief Equivalent to operator []
* \fn PIMap & PIMap::operator <<(const PIMap & other)
* \brief Insert all etries of "other" to this map. Override existing values.
* \fn bool PIMap::operator ==(const PIMap & t) const
* \brief Compare operator
* \fn bool PIMap::operator !=(const PIMap & t) const
* \brief Compare operator
* \fn bool PIMap::contains(const Key & key) const
* \brief Returns "true" if map contains entry with key "key"
* \fn PIMap & PIMap::reserve(size_t new_size)
* \brief Reserve space for "new_size" entries
* \fn PIMap & PIMap::removeOne(const Key & key)
* \brief Remove entry with key "key"
* \fn PIMap & PIMap::remove(const Key & key)
* \brief Equivalent \a removeOne(key)
* \fn PIMap & PIMap::erase(const Key & key)
* \brief Equivalent \a removeOne(key)
* \fn PIMap & PIMap::clear()
* \brief Clear map
* \fn void PIMap::swap(PIMap & other)
* \brief Swap map with "other"
* \fn PIMap & PIMap::insert(const Key & key, const T & value)
* \brief Insert or rewrite entry with key "key" and value "value"
* \fn const T PIMap::value(const Key & key, const T & default = T())
* \brief Returns value for key "key". If there is no key in map, returns "default".
* \fn PIVector<T> PIMap::values() const
* \brief Returns all values as PIVector
* \fn Key PIMap::key(const T & value, const Key & default = Key()) const
* \brief Returns key for first founded value "value". If there is no such value in map, returns "default".
* \fn PIVector<Key> PIMap::keys() const
* \brief Returns all keys as PIVector
* */
/** \class PIMapIterator
* \brief Helper class to iterate over PIMap
* \details This class used to access keys and values in PIMap.
* You can use constructor to create iterator, or use \a PIMap::makeIterator()
* and \a PIMap::makeReverseIterator() methods.
*
* First usage variant:
* \code
* PIMap<int, PIString> m;
* m[1] = "one";
* m[2] = "two";
* m[4] = "four";
*
* auto it = m.makeIterator();
* while (it.next()) {
* piCout << it.key() << it.value();
* }
* // 1 one
* // 2 two
* // 4 four
* \endcode
*
* Using hasNext():
* \code
* while (it.hasNext()) {
* it.next();
* \endcode
*
* Using constructor:
* \code
* PIMapIterator<int, PIString> it(m);
* \endcode
*
* Write access:
* \code
* while (it.next()) {
* it.valueRef().append("_!");
* piCout << it.key() << it.value();
* }
*
* // 1 one_!
* // 2 two_!
* // 4 four_!
* \endcode
*
* Reverse iterator:
* \code
* auto it = m.makeReverseIterator();
* while (it.next()) {
* piCout << it.key() << it.value();
* }
*
* // 4 four
* // 2 two
* // 1 one
* \endcode
* \fn PIMapIterator(const PIMap & map, bool reverse = false)
* \brief Contructs iterator for "map". Current position is invalid.
* \fn const Key & PIMapIterator::key() const
* \brief Returns current entry key
* \fn const T & PIMapIterator::value() const
* \brief Returns current entry value
* \fn T & PIMapIterator::valueRef() const
* \brief Returns reference to current entry value
* \fn bool PIMapIterator::hasNext()
* \brief Returns if iterator can jump to next entry
* \fn bool PIMapIterator::next()
* \brief Jump to next entry and return if new position is valid.
* */

View File

@@ -67,10 +67,16 @@ void piQuickSort(T * a, ssize_t N) {
} }
} }
template <typename Key, typename T>
class PIMapIterator;
template <typename Key, typename T> template <typename Key, typename T>
class PIMap { class PIMap {
template <typename Key1, typename T1> friend PIByteArray & operator >>(PIByteArray & s, PIMap<Key1, T1> & v); template <typename Key1, typename T1> friend PIByteArray & operator >>(PIByteArray & s, PIMap<Key1, T1> & v);
template <typename Key1, typename T1> friend PIByteArray & operator <<(PIByteArray & s, const PIMap<Key1, T1> & v); template <typename Key1, typename T1> friend PIByteArray & operator <<(PIByteArray & s, const PIMap<Key1, T1> & v);
template <typename Key1, typename T1> friend class PIMapIterator;
public: public:
PIMap() {;} PIMap() {;}
PIMap(const PIMap<Key, T> & other) {*this = other;} PIMap(const PIMap<Key, T> & other) {*this = other;}
@@ -183,6 +189,9 @@ public:
const_reverse_iterator constRbegin() const {return const_reverse_iterator(this, size() - 1);} const_reverse_iterator constRbegin() const {return const_reverse_iterator(this, size() - 1);}
const_reverse_iterator constRend() const {return const_reverse_iterator(this, -1);} const_reverse_iterator constRend() const {return const_reverse_iterator(this, -1);}
PIMapIterator<Key, T> makeIterator() const {return PIMapIterator<Key, T>(*this);}
PIMapIterator<Key, T> makeReverseIterator() const {return PIMapIterator<Key, T>(*this, true);}
size_t size() const {return pim_content.size();} size_t size() const {return pim_content.size();}
int size_s() const {return pim_content.size_s();} int size_s() const {return pim_content.size_s();}
size_t length() const {return pim_content.size();} size_t length() const {return pim_content.size();}
@@ -327,6 +336,41 @@ protected:
}; };
template <typename Key, typename T>
class PIMapIterator {
typedef PIMap<Key, T> MapType;
public:
PIMapIterator(const PIMap<Key, T> & map, bool reverse = false): m(map), pos(-1), rev(reverse) {
if (rev) pos = m.size_s();
}
const Key & key() const {return const_cast<MapType & >(m)._key(pos);}
const T & value() const {return const_cast<MapType & >(m)._value(pos);}
T & valueRef() const {return const_cast<MapType & >(m)._value(pos);}
inline bool hasNext() const {
if (rev) {
return pos > 0;
} else {
return pos < (m.size_s() - 1);
}
return false;
}
inline bool next() {
if (rev) {
--pos;
return pos >= 0;
} else {
++pos;
return pos < m.size_s();
}
return false;
}
private:
const MapType & m;
ssize_t pos;
bool rev;
};
#ifdef PIP_STD_IOSTREAM #ifdef PIP_STD_IOSTREAM
template<typename Key, typename Type> template<typename Key, typename Type>
inline std::ostream & operator <<(std::ostream & s, const PIMap<Key, Type> & v) { inline std::ostream & operator <<(std::ostream & s, const PIMap<Key, Type> & v) {

View File

@@ -56,6 +56,10 @@ public:
//! Constructs an empty byte array //! Constructs an empty byte array
PIByteArray() {;} PIByteArray() {;}
PIByteArray(const PIByteArray & o): PIDeque<uchar>(o) {}
PIByteArray(PIByteArray && o): PIDeque<uchar>(std::move(o)) {}
//! Constructs 0-filled byte array with size "size" //! Constructs 0-filled byte array with size "size"
PIByteArray(const uint size) {resize(size);} PIByteArray(const uint size) {resize(size);}
@@ -124,6 +128,10 @@ public:
void operator =(const PIDeque<uchar> & d) {resize(d.size()); memcpy(data(), d.data(), d.size());} void operator =(const PIDeque<uchar> & d) {resize(d.size()); memcpy(data(), d.data(), d.size());}
PIByteArray & operator =(const PIByteArray & o) {if (this == &o) return *this; clear(); append(o); return *this;}
PIByteArray & operator =(PIByteArray && o) {swap(o); return *this;}
static PIByteArray fromUserInput(PIString str); static PIByteArray fromUserInput(PIString str);
static PIByteArray fromHex(PIString str); static PIByteArray fromHex(PIString str);
static PIByteArray fromBase64(const PIByteArray & base64); static PIByteArray fromBase64(const PIByteArray & base64);

View File

@@ -54,12 +54,18 @@ public:
int id; int id;
T data; T data;
}; };
template <typename T>
struct ChunkConst {
ChunkConst(int i, const T & d): id(i), data(d) {}
int id;
const T & data;
};
//! Returns chunk with ID "id" and value "data" for write to stream //! Returns chunk with ID "id" and value "data" for write to stream
template <typename T> static Chunk<T> chunk(int id, const T & data) {return Chunk<T>(id, data);} template <typename T> static ChunkConst<T> chunk(int id, const T & data) {return ChunkConst<T>(id, data);}
//! Add data to this chunk strean with ID "id" and value "data" //! Add data to this chunk strean with ID "id" and value "data"
template <typename T> PIChunkStream & add(int id, const T & data) {*this << Chunk<T>(id, data); return *this;} template <typename T> PIChunkStream & add(int id, const T & data) {*this << ChunkConst<T>(id, data); return *this;}
void setSource(const PIByteArray & data); void setSource(const PIByteArray & data);
void setSource(PIByteArray * data); void setSource(PIByteArray * data);
@@ -112,6 +118,7 @@ private:
PIMap<int, PIByteArray> data_map; PIMap<int, PIByteArray> data_map;
template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c); template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c);
template <typename T> friend PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::ChunkConst<T> & c);
}; };
template <typename T> template <typename T>
@@ -133,5 +140,24 @@ PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::Chunk<T> & c
} }
return s; return s;
} }
template <typename T>
PIChunkStream & operator <<(PIChunkStream & s, const PIChunkStream::ChunkConst<T> & c) {
PIByteArray ba;
ba << c.data;
switch (s.version_) {
case PIChunkStream::Version_1:
(*(s.data_)) << c.id << ba;
break;
case PIChunkStream::Version_2:
if (s.data_->isEmpty())
(*(s.data_)) << uchar(uchar(s.version_) | 0x80);
PIChunkStream::writeVInt(*(s.data_), c.id);
PIChunkStream::writeVInt(*(s.data_), ba.size());
s.data_->append(ba);
break;
default: break;
}
return s;
}
#endif // PICHUNKSTREAM_H #endif // PICHUNKSTREAM_H

View File

@@ -658,6 +658,26 @@ PIString & PIString::replaceAll(const PIString & what, const PIString & with) {
} }
PIString & PIString::replaceAll(const PIString & what, const char with) {
if (what.isEmpty()) return *this;
int l = what.length(), dl = what.length() - 1;
for (int i = 0; i < length() - l + 1; ++i) {
bool match = true;
for (int j = 0; j < l; ++j) {
if (at(j + i) != what[j]) {
match = false;
break;
}
}
if (!match) continue;
if (dl > 0) PIDeque<PIChar>::remove(i, dl);
at(i) = PIChar(with);
//i -= l;
}
return *this;
}
PIString & PIString::replaceAll(const char what, const char with) { PIString & PIString::replaceAll(const char what, const char with) {
int l = length(); int l = length();
for (int i = 0; i < l; ++i) { for (int i = 0; i < l; ++i) {

View File

@@ -283,6 +283,11 @@ public:
* \sa \a replace(), \a replaced() */ * \sa \a replace(), \a replaced() */
PIString & replaceAll(const PIString & what, const PIString & with); PIString & replaceAll(const PIString & what, const PIString & with);
/*! \brief Replace all founded substrings "what" with symbol "with" and return this string
* \details Example: \snippet pistring.cpp PIString::replaceAll
* \sa \a replace(), \a replaced() */
PIString & replaceAll(const PIString & what, const char with);
/*! \brief Replace all founded symbols "what" with symbol "with" and return this string /*! \brief Replace all founded symbols "what" with symbol "with" and return this string
* \details Example: \snippet pistring.cpp PIString::replaceAll * \details Example: \snippet pistring.cpp PIString::replaceAll
* \sa \a replace(), \a replaced() */ * \sa \a replace(), \a replaced() */

View File

@@ -214,9 +214,10 @@ PIByteArray PIIntrospection::packThreads() {
if (p) { if (p) {
p->mutex.lock(); p->mutex.lock();
PIMap<PIThread*, PIIntrospectionThreads::ThreadInfo> & tm(p->threads); PIMap<PIThread*, PIIntrospectionThreads::ThreadInfo> & tm(p->threads);
for (PIMap<PIThread*, PIIntrospectionThreads::ThreadInfo>::iterator i = tm.begin(); i != tm.end(); ++i) { auto it = tm.makeIterator();
i.value().classname = PIStringAscii(i.key()->className()); while (it.next()) {
i.value().name = i.key()->name(); it.valueRef().classname = PIStringAscii(it.key()->className());
it.valueRef().name = it.key()->name();
} }
ret << tm.values(); ret << tm.values();
p->mutex.unlock(); p->mutex.unlock();

View File

@@ -235,7 +235,7 @@ PIByteArray PIFile::readAll(bool forceRead) {
llong s = size(); llong s = size();
if (s < 0) return a; if (s < 0) return a;
a.resize(s); a.resize(s);
s = readAll(a.data()); fread(a.data(), 1, s, PRIVATE->fd);
seek(cp); seek(cp);
if (s >= 0) a.resize(s); if (s >= 0) a.resize(s);
return a; return a;

View File

@@ -388,8 +388,9 @@ bool PIConnection::removeDevice(const PIString & full_path) {
} }
bounded_extractors.remove(dev); bounded_extractors.remove(dev);
channels_.remove(dev); channels_.remove(dev);
for (auto it = channels_.begin(); it != channels_.end(); it++) auto it = channels_.makeIterator();
it.value().removeAll(dev); while (it.next())
it.valueRef().removeAll(dev);
__device_pool__->lock(); __device_pool__->lock();
if (diags_.value(dev, 0) != 0) if (diags_.value(dev, 0) != 0)
delete diags_.value(dev); delete diags_.value(dev);
@@ -411,8 +412,9 @@ void PIConnection::removeAllDevices() {
s.value()->unlock(); s.value()->unlock();
} }
channels_.remove(d); channels_.remove(d);
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it) auto it = channels_.makeIterator();
it.value().removeAll(d); while (it.next())
it.valueRef().removeAll(d);
if (diags_.value(d, 0) != 0) if (diags_.value(d, 0) != 0)
delete diags_.value(d); delete diags_.value(d);
diags_.remove(d); diags_.remove(d);
@@ -566,8 +568,9 @@ void PIConnection::removeAllFilters() {
for (auto i = extractors.constBegin(); i != extractors.constEnd(); i++) { for (auto i = extractors.constBegin(); i != extractors.constEnd(); i++) {
if (i.value() == 0) continue; if (i.value() == 0) continue;
channels_.remove(i.value()->extractor); channels_.remove(i.value()->extractor);
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it) auto it = channels_.makeIterator();
it.value().removeAll(i.value()->extractor); while (it.next())
it.valueRef().removeAll(i.value()->extractor);
if (diags_.value(i.value()->extractor, 0) != 0) if (diags_.value(i.value()->extractor, 0) != 0)
delete diags_.value(i.value()->extractor); delete diags_.value(i.value()->extractor);
diags_.remove(i.value()->extractor); diags_.remove(i.value()->extractor);
@@ -658,8 +661,9 @@ bool PIConnection::removeChannel(const PIString & name0) {
if (pe0 != 0) dev0 = pe0; if (pe0 != 0) dev0 = pe0;
if (dev0 == 0) return false; if (dev0 == 0) return false;
channels_.remove(dev0); channels_.remove(dev0);
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it) auto it = channels_.makeIterator();
it.value().removeAll(dev0); while (it.next())
it.valueRef().removeAll(dev0);
return true; return true;
} }
@@ -1240,8 +1244,9 @@ void PIConnection::Sender::tick(void * , int) {
void PIConnection::unboundExtractor(PIPacketExtractor * pe) { void PIConnection::unboundExtractor(PIPacketExtractor * pe) {
if (pe == 0) return; if (pe == 0) return;
channels_.remove(pe); channels_.remove(pe);
for (PIMap<PIIODevice * , PIVector<PIIODevice * > >::iterator it = channels_.begin(); it != channels_.end(); ++it) auto it = channels_.makeIterator();
it.value().removeAll(pe); while (it.next())
it.valueRef().removeAll(pe);
bounded_extractors.remove(pe); bounded_extractors.remove(pe);
PIVector<PIIODevice * > k = bounded_extractors.keys(); PIVector<PIIODevice * > k = bounded_extractors.keys();
piForeach (PIIODevice * i, k) { piForeach (PIIODevice * i, k) {

View File

@@ -36,13 +36,12 @@ PIByteArray PIResources::get(const PIString & name) {
void PIResources::dump() { void PIResources::dump() {
PIMap<PIString, PIResourcesStorage::Section * > & sm(PIResourcesStorage::instance()->sections); auto si = PIResourcesStorage::instance()->sections.makeIterator();
PIMap<PIString, PIResourcesStorage::Section * >::iterator si; while (si.next()) {
for (si = sm.begin(); si != sm.end(); ++si) {
piCout << "Section [" << si.key() << "]"; piCout << "Section [" << si.key() << "]";
if (!si.value()) continue; if (!si.value()) continue;
PIMap<PIString, PIByteArray * >::iterator fi; auto fi = si.value()->entries.makeIterator();
for (fi = si.value()->entries.begin(); fi != si.value()->entries.end(); ++fi) { while (fi.next()) {
PIString s = fi.key() + ": "; PIString s = fi.key() + ": ";
s << (fi.value() ? fi.value()->size_s() : 0) << " b"; s << (fi.value() ? fi.value()->size_s() : 0) << " b";
piCout << " " << s; piCout << " " << s;

View File

@@ -31,8 +31,8 @@ PIResourcesStorage::Section::~Section() {
void PIResourcesStorage::Section::add(const PIResourcesStorage::Section & s) { void PIResourcesStorage::Section::add(const PIResourcesStorage::Section & s) {
PIMap<PIString, PIByteArray * >::const_iterator i; auto i = s.entries.makeIterator();
for (i = s.entries.begin(); i != s.entries.end(); ++i) { while (i.next()) {
if (!i.value()) continue; if (!i.value()) continue;
if (entries.value(i.key(), 0)) continue; if (entries.value(i.key(), 0)) continue;
entries[i.key()] = i.value(); entries[i.key()] = i.value();
@@ -83,10 +83,10 @@ void PIResourcesStorage::registerSection(const uchar * rc_data, const uchar * rc
piForeachC (PIResourcesStorage::__RCEntry & e, el) { piForeachC (PIResourcesStorage::__RCEntry & e, el) {
ebs[e.section] << e; ebs[e.section] << e;
} }
PIMap<PIString, PIVector<PIResourcesStorage::__RCEntry> >::iterator it; auto it = ebs.makeIterator();
for (it = ebs.begin(); it != ebs.end(); ++it) { while (it.next()) {
PIResourcesStorage::Section s; PIResourcesStorage::Section s;
PIVector<PIResourcesStorage::__RCEntry> & itv(it.value()); const PIVector<PIResourcesStorage::__RCEntry> & itv(it.value());
piForeachC (PIResourcesStorage::__RCEntry & e, itv) { piForeachC (PIResourcesStorage::__RCEntry & e, itv) {
//piCout << "add" << e.name << e.alias << PIString::readableSize(e.size); //piCout << "add" << e.name << e.alias << PIString::readableSize(e.size);
PIByteArray * eba = new PIByteArray(&(rc_data[e.offset]), e.size); PIByteArray * eba = new PIByteArray(&(rc_data[e.offset]), e.size);
@@ -114,8 +114,8 @@ PIByteArray PIResourcesStorage::get(const PIString & section_name, const PIStrin
PIByteArray PIResourcesStorage::get(const PIString & entry_name) const { PIByteArray PIResourcesStorage::get(const PIString & entry_name) const {
PIMap<PIString, Section * >::const_iterator i; auto i = sections.makeIterator();
for (i = sections.begin(); i != sections.end(); ++i) { while (i.next()) {
if (!i.value()) continue; if (!i.value()) continue;
PIByteArray * ba = i.value()->entries.value(entry_name, 0); PIByteArray * ba = i.value()->entries.value(entry_name, 0);
if (!ba) continue; if (!ba) continue;

View File

@@ -331,9 +331,10 @@ void PISystemMonitor::run() {
tstat.cpu_load_system = piClampf(tstat.cpu_load_system, 0.f, 100.f); tstat.cpu_load_system = piClampf(tstat.cpu_load_system, 0.f, 100.f);
tstat.cpu_load_user = piClampf(tstat.cpu_load_user , 0.f, 100.f); tstat.cpu_load_user = piClampf(tstat.cpu_load_user , 0.f, 100.f);
for (PIMap<llong, ThreadStats>::iterator i = cur_tm.begin(); i != cur_tm.end(); ++i) { auto i = cur_tm.makeIterator();
while (i.next()) {
if (!last_tm.contains(i.key())) continue; if (!last_tm.contains(i.key())) continue;
ThreadStats & ts_new(i.value()); ThreadStats & ts_new(i.valueRef());
ThreadStats & ts_old(last_tm[i.key()]); ThreadStats & ts_old(last_tm[i.key()]);
ts_new.cpu_load_kernel = calcThreadUsage(ts_new.kernel_time, ts_old.kernel_time); ts_new.cpu_load_kernel = calcThreadUsage(ts_new.kernel_time, ts_old.kernel_time);
ts_new.cpu_load_user = calcThreadUsage(ts_new.user_time, ts_old.user_time); ts_new.cpu_load_user = calcThreadUsage(ts_new.user_time, ts_old.user_time);

View File

@@ -20,43 +20,43 @@
#ifndef PIBLOCKINGDEQUEUE_H #ifndef PIBLOCKINGDEQUEUE_H
#define PIBLOCKINGDEQUEUE_H #define PIBLOCKINGDEQUEUE_H
#include "pideque.h" #include <queue>
#include "piconditionvar.h" #include "piconditionvar.h"
/** /**
* @brief A Queue that supports operations that wait for the queue to become non-empty when retrieving an element, and * @brief A Queue that supports operations that wait for the queue to become non-empty when retrieving an element, and
* wait for space to become available in the queue when storing an element. * wait for space to become available in the queue when storing an element.
*/ */
template <typename T> template <typename T, template<typename = T, typename...> class Queue_ = std::deque, typename ConditionVariable_ = PIConditionVariable>
class PIBlockingDequeue: private PIDeque<T> { class PIBlockingDequeue {
public: public:
typedef Queue_<T> QueueType;
/** /**
* @brief Constructor * @brief Constructor
*/ */
explicit inline PIBlockingDequeue(size_t capacity = SIZE_MAX, explicit PIBlockingDequeue(size_t capacity = SIZE_MAX)
PIConditionVariable* cond_var_add = new PIConditionVariable(), : cond_var_add(new ConditionVariable_()), cond_var_rem(new ConditionVariable_()), max_size(capacity) { }
PIConditionVariable* cond_var_rem = new PIConditionVariable())
: cond_var_add(cond_var_add), cond_var_rem(cond_var_rem), max_size(capacity) { }
/** /**
* @brief Copy constructor. Initialize queue with copy of other queue elements. Not thread-safe for other queue. * @brief Copy constructor. Initialize queue with copy of other container elements. Not thread-safe for other queue.
*/ */
explicit inline PIBlockingDequeue(const PIDeque<T>& other) : cond_var_add(new PIConditionVariable()), cond_var_rem(new PIConditionVariable()) { template<typename Iterable,
typename std::enable_if<!std::is_arithmetic<Iterable>::value, int>::type = 0>
explicit PIBlockingDequeue(const Iterable& other): PIBlockingDequeue() {
mutex.lock(); mutex.lock();
max_size = SIZE_MAX; for (const T& t : other) data_queue.push_back(t);
PIDeque<T>::append(other);
mutex.unlock(); mutex.unlock();
} }
/** /**
* @brief Thread-safe copy constructor. Initialize queue with copy of other queue elements. * @brief Thread-safe copy constructor. Initialize queue with copy of other queue elements.
*/ */
inline PIBlockingDequeue(PIBlockingDequeue<T> & other) : cond_var_add(new PIConditionVariable()), cond_var_rem(new PIConditionVariable()) { explicit PIBlockingDequeue(PIBlockingDequeue<T>& other): PIBlockingDequeue() {
other.mutex.lock(); other.mutex.lock();
mutex.lock(); mutex.lock();
max_size = other.max_size; max_size = other.max_size;
PIDeque<T>::append(static_cast<PIDeque<T>&>(other)); data_queue = other.data_queue;
mutex.unlock(); mutex.unlock();
other.mutex.unlock(); other.mutex.unlock();
} }
@@ -71,10 +71,11 @@ public:
* *
* @param v the element to add * @param v the element to add
*/ */
void put(const T & v) { template<typename Type>
void put(Type && v) {
mutex.lock(); mutex.lock();
cond_var_rem->wait(mutex, [&]() { return PIDeque<T>::size() < max_size; }); cond_var_rem->wait(mutex, [&]() { return data_queue.size() < max_size; });
PIDeque<T>::push_back(v); data_queue.push_back(std::forward<Type>(v));
mutex.unlock(); mutex.unlock();
cond_var_add->notifyOne(); cond_var_add->notifyOne();
} }
@@ -86,13 +87,14 @@ public:
* @param v the element to add * @param v the element to add
* @return true if the element was added to this queue, else false * @return true if the element was added to this queue, else false
*/ */
bool offer(const T & v) { template<typename Type>
bool offer(Type && v) {
mutex.lock(); mutex.lock();
if (PIDeque<T>::size() >= max_size) { if (data_queue.size() >= max_size) {
mutex.unlock(); mutex.unlock();
return false; return false;
} }
PIDeque<T>::push_back(v); data_queue.push_back(std::forward<Type>(v));
mutex.unlock(); mutex.unlock();
cond_var_add->notifyOne(); cond_var_add->notifyOne();
return true; return true;
@@ -106,10 +108,11 @@ public:
* @param timeoutMs how long to wait before giving up, in milliseconds * @param timeoutMs how long to wait before giving up, in milliseconds
* @return true if successful, or false if the specified waiting time elapses before space is available * @return true if successful, or false if the specified waiting time elapses before space is available
*/ */
bool offer(const T & v, int timeoutMs) { template<typename Type>
bool offer(Type && v, int timeoutMs) {
mutex.lock(); mutex.lock();
bool isOk = cond_var_rem->waitFor(mutex, timeoutMs, [&]() { return PIDeque<T>::size() < max_size; } ); bool isOk = cond_var_rem->waitFor(mutex, timeoutMs, [&]() { return data_queue.size() < max_size; } );
if (isOk) PIDeque<T>::push_back(v); if (isOk) data_queue.push_back(std::forward<Type>(v));
mutex.unlock(); mutex.unlock();
if (isOk) cond_var_add->notifyOne(); if (isOk) cond_var_add->notifyOne();
return isOk; return isOk;
@@ -121,10 +124,10 @@ public:
* @return the head of this queue * @return the head of this queue
*/ */
T take() { T take() {
T t;
mutex.lock(); mutex.lock();
cond_var_add->wait(mutex, [&]() { return !PIDeque<T>::isEmpty(); }); cond_var_add->wait(mutex, [&]() { return data_queue.size() != 0; });
t = T(PIDeque<T>::take_front()); T t = std::move(data_queue.front());
data_queue.pop_front();
mutex.unlock(); mutex.unlock();
cond_var_rem->notifyOne(); cond_var_rem->notifyOne();
return t; return t;
@@ -140,11 +143,17 @@ public:
* return value is retrieved value * return value is retrieved value
* @return the head of this queue, or defaultVal if the specified waiting time elapses before an element is available * @return the head of this queue, or defaultVal if the specified waiting time elapses before an element is available
*/ */
T poll(int timeoutMs, const T & defaultVal = T(), bool * isOk = nullptr) { template<typename Type = T>
T t; T poll(int timeoutMs, Type && defaultVal = Type(), bool * isOk = nullptr) {
mutex.lock(); mutex.lock();
bool isNotEmpty = cond_var_add->waitFor(mutex, timeoutMs, [&]() { return !PIDeque<T>::isEmpty(); }); bool isNotEmpty = cond_var_add->waitFor(mutex, timeoutMs, [&]() { return data_queue.size() != 0; });
t = isNotEmpty ? T(PIDeque<T>::take_front()) : defaultVal; T t;
if (isNotEmpty) {
t = std::move(data_queue.front());
data_queue.pop_front();
} else {
t = std::forward<Type>(defaultVal);
}
mutex.unlock(); mutex.unlock();
if (isNotEmpty) cond_var_rem->notifyOne(); if (isNotEmpty) cond_var_rem->notifyOne();
if (isOk) *isOk = isNotEmpty; if (isOk) *isOk = isNotEmpty;
@@ -160,11 +169,17 @@ public:
* return value is retrieved value * return value is retrieved value
* @return the head of this queue, or defaultVal if the specified waiting time elapses before an element is available * @return the head of this queue, or defaultVal if the specified waiting time elapses before an element is available
*/ */
T poll(const T & defaultVal = T(), bool * isOk = nullptr) { template<typename Type = T>
T poll(Type && defaultVal = Type(), bool * isOk = nullptr) {
T t; T t;
mutex.lock(); mutex.lock();
bool isNotEmpty = !PIDeque<T>::isEmpty(); bool isNotEmpty = data_queue.size() != 0;
t = isNotEmpty ? PIDeque<T>::take_front() : defaultVal; if (isNotEmpty) {
t = std::move(data_queue.front());
data_queue.pop_front();
} else {
t = std::forward<Type>(defaultVal);
}
mutex.unlock(); mutex.unlock();
if (isNotEmpty) cond_var_rem->notifyOne(); if (isNotEmpty) cond_var_rem->notifyOne();
if (isOk) *isOk = isNotEmpty; if (isOk) *isOk = isNotEmpty;
@@ -193,7 +208,7 @@ public:
*/ */
size_t remainingCapacity() { size_t remainingCapacity() {
mutex.lock(); mutex.lock();
size_t c = max_size - PIDeque<T>::size(); size_t c = max_size - data_queue.size();
mutex.unlock(); mutex.unlock();
return c; return c;
} }
@@ -203,7 +218,7 @@ public:
*/ */
size_t size() { size_t size() {
mutex.lock(); mutex.lock();
size_t s = PIDeque<T>::size(); size_t s = data_queue.size();
mutex.unlock(); mutex.unlock();
return s; return s;
} }
@@ -211,10 +226,14 @@ public:
/** /**
* @brief Removes all available elements from this queue and adds them to other given queue. * @brief Removes all available elements from this queue and adds them to other given queue.
*/ */
size_t drainTo(PIDeque<T>& other, size_t maxCount = SIZE_MAX) { template<typename Appendable>
size_t drainTo(Appendable& other, size_t maxCount = SIZE_MAX) {
mutex.lock(); mutex.lock();
size_t count = ((maxCount > PIDeque<T>::size()) ? PIDeque<T>::size() : maxCount); size_t count = maxCount > data_queue.size() ? data_queue.size() : maxCount;
for (size_t i = 0; i < count; ++i) other.push_back(PIDeque<T>::take_front()); for (size_t i = 0; i < count; ++i) {
other.push_back(std::move(data_queue.front()));
data_queue.pop_front();
}
mutex.unlock(); mutex.unlock();
return count; return count;
} }
@@ -225,18 +244,23 @@ public:
size_t drainTo(PIBlockingDequeue<T>& other, size_t maxCount = SIZE_MAX) { size_t drainTo(PIBlockingDequeue<T>& other, size_t maxCount = SIZE_MAX) {
mutex.lock(); mutex.lock();
other.mutex.lock(); other.mutex.lock();
size_t count = maxCount > PIDeque<T>::size() ? PIDeque<T>::size() : maxCount; size_t count = maxCount > data_queue.size() ? data_queue.size() : maxCount;
size_t otherRemainingCapacity = other.max_size - static_cast<PIDeque<T> >(other).size(); size_t otherRemainingCapacity = other.max_size - data_queue.size();
if (count > otherRemainingCapacity) count = otherRemainingCapacity; if (count > otherRemainingCapacity) count = otherRemainingCapacity;
for (size_t i = 0; i < count; ++i) other.push_back(PIDeque<T>::take_front()); for (size_t i = 0; i < count; ++i) {
other.data_queue.push_back(std::move(data_queue.front()));
data_queue.pop_front();
}
other.mutex.unlock(); other.mutex.unlock();
mutex.unlock(); mutex.unlock();
return count; return count;
} }
private: protected:
PIMutex mutex; PIMutex mutex;
PIConditionVariable * cond_var_add, * cond_var_rem; // TODO change to type without point
ConditionVariable_ *cond_var_add, *cond_var_rem;
QueueType data_queue;
size_t max_size; size_t max_size;
}; };

View File

View File

@@ -0,0 +1,200 @@
/*
PIP - Platform Independent Primitives
Stephan Fomenko
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/>.
*/
#ifndef PIEXECUTOR_H
#define PIEXECUTOR_H
#include "piblockingdequeue.h"
#include <atomic>
#include <future>
/**
* @brief Wrapper for custom invoke operator available function types.
* @note Source from: "Энтони Уильямс, Параллельное программирование на С++ в действии. Практика разработки многопоточных
* программ. Пер. с англ. Слинкин А. А. - M.: ДМК Пресс, 2012 - 672c.: ил." (page 387)
*/
class FunctionWrapper {
struct ImplBase {
virtual void call() = 0;
virtual ~ImplBase() = default;
};
std::unique_ptr<ImplBase> impl;
template<typename F>
struct ImplType: ImplBase {
F f;
explicit ImplType(F&& f): f(std::forward<F>(f)) {}
void call() final { f(); }
};
public:
template<typename F, typename = std::enable_if<!std::is_same<F, FunctionWrapper>::value> >
explicit FunctionWrapper(F&& f): impl(new ImplType<F>(std::forward<F>(f))) {}
void operator()() { impl->call(); }
explicit operator bool() const noexcept { return static_cast<bool>(impl); }
FunctionWrapper() = default;
FunctionWrapper(FunctionWrapper&& other) noexcept : impl(std::move(other.impl)) {}
FunctionWrapper& operator=(FunctionWrapper&& other) noexcept {
impl = std::move(other.impl);
return *this;
}
FunctionWrapper(const FunctionWrapper& other) = delete;
FunctionWrapper& operator=(const FunctionWrapper&) = delete;
};
template <typename Thread_ = PIThread, typename Dequeue_ = PIBlockingDequeue<FunctionWrapper>>
class PIThreadPoolExecutorTemplate {
public:
NO_COPY_CLASS(PIThreadPoolExecutorTemplate)
explicit PIThreadPoolExecutorTemplate(size_t corePoolSize = 1) : isShutdown_(false) { makePool(corePoolSize); }
virtual ~PIThreadPoolExecutorTemplate() {
shutdownNow();
while (threadPool.size() > 0) delete threadPool.take_back();
}
template<typename FunctionType>
std::future<typename std::result_of<FunctionType()>::type> submit(FunctionType&& callable) {
typedef typename std::result_of<FunctionType()>::type ResultType;
if (!isShutdown_) {
std::packaged_task<ResultType()> callable_task(std::forward<FunctionType>(callable));
auto future = callable_task.get_future();
FunctionWrapper functionWrapper(callable_task);
taskQueue.offer(std::move(functionWrapper));
return future;
} else {
return std::future<ResultType>();
}
}
template<typename FunctionType>
void execute(FunctionType&& runnable) {
if (!isShutdown_) {
FunctionWrapper function_wrapper(std::forward<FunctionType>(runnable));
taskQueue.offer(std::move(function_wrapper));
}
}
void shutdown() {
isShutdown_ = true;
}
void shutdownNow() {
isShutdown_ = true;
for (size_t i = 0; i < threadPool.size(); ++i) threadPool[i]->stop();
}
bool isShutdown() const {
return isShutdown_;
}
bool awaitTermination(int timeoutMs) {
PITimeMeasurer measurer;
for (size_t i = 0; i < threadPool.size(); ++i) {
int dif = timeoutMs - (int)measurer.elapsed_m();
if (dif < 0) return false;
if (!threadPool[i]->waitForFinish(dif)) return false;
}
return true;
}
protected:
std::atomic_bool isShutdown_;
Dequeue_ taskQueue;
PIVector<Thread_*> threadPool;
template<typename Function>
PIThreadPoolExecutorTemplate(size_t corePoolSize, Function&& onBeforeStart) : isShutdown_(false) {
makePool(corePoolSize, std::forward<Function>(onBeforeStart));
}
void makePool(size_t corePoolSize, std::function<void(Thread_*)>&& onBeforeStart = [](Thread_*){}) {
for (size_t i = 0; i < corePoolSize; ++i) {
auto* thread = new Thread_([&, i](){
auto runnable = taskQueue.poll(100);
if (runnable) {
runnable();
}
if (isShutdown_ && taskQueue.size() == 0) threadPool[i]->stop();
});
threadPool.push_back(thread);
onBeforeStart(thread);
thread->start();
}
}
};
typedef PIThreadPoolExecutorTemplate<> PIThreadPoolExecutor;
#ifdef DOXYGEN
/**
* @brief Thread pools address two different problems: they usually provide improved performance when executing large
* numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and
* managing the resources, including threads, consumed when executing a collection of tasks.
*/
class PIThreadPoolExecutor {
public:
explicit PIThreadPoolExecutor(size_t corePoolSize);
virtual ~PIThreadPoolExecutor();
/**
* @brief Submits a Runnable task for execution and returns a Future representing that task. The Future's get method
* will return null upon successful completion.
*
* @tparam FunctionType - custom type of function with operator() and return type
* @tparam R - derived from FunctionType return type
*
* @param callable - the task to submit
* @return a future representing pending completion of the task
*/
std::future<R> submit(FunctionType&& callable);
/**
* @brief Executes the given task sometime in the future. The task execute in an existing pooled thread. If the task
* cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been
* reached.
*
* @tparam FunctionType - custom type of function with operator() and return type
*
* @param runnable not empty function for thread pool execution
*/
void execute(FunctionType&& runnable);
/**
* @brief Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be
* accepted. Invocation has no additional effect if already shut down. This method does not wait for previously
* submitted tasks to complete execution. Use awaitTermination to do that.
*/
void shutdown();
void shutdownNow();
bool isShutdown() const;
bool awaitTermination(int timeoutMs);
};
#endif //DOXYGEN
#endif //PIEXECUTOR_H

View File

@@ -25,7 +25,7 @@
#include "pitimer.h" #include "pitimer.h"
#include "pipipelinethread.h" #include "pipipelinethread.h"
#include "pigrabberbase.h" #include "pigrabberbase.h"
#include "pithreadpoolexecutor.h" #include "piexecutor.h"
#include "piconditionvar.h" #include "piconditionvar.h"
#endif // PITHREADMODULE_H #endif // PITHREADMODULE_H

View File

@@ -1,89 +0,0 @@
/*
PIP - Platform Independent Primitives
Stephan Fomenko
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 "pithreadpoolexecutor.h"
#include "pisysteminfo.h"
/*! \class PIThreadPoolExecutor
* @brief Thread pools address two different problems: they usually provide improved performance when executing large
* numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and
* managing the resources, including threads, consumed when executing a collection of tasks.
*/
PIThreadPoolExecutor::PIThreadPoolExecutor(size_t corePoolSize, PIBlockingDequeue<std::function<void()> > * taskQueue_) : isShutdown_(false) {
queue_own = false;
if (corePoolSize <= 0)
corePoolSize = PISystemInfo::instance()->processorsCount;
if (!taskQueue_) {
taskQueue = new PIBlockingDequeue<std::function<void()> >();
queue_own = true;
}
for (size_t i = 0; i < corePoolSize; ++i) {
PIThread * thread = new PIThread([&, i](){
auto runnable = taskQueue->poll(100, std::function<void()>());
if (runnable) {
runnable();
}
if (isShutdown_ && taskQueue->size() == 0) threadPool[i]->stop();
});
threadPool.push_back(thread);
thread->start();
}
}
bool PIThreadPoolExecutor::awaitTermination(int timeoutMs) {
PITimeMeasurer measurer;
for (size_t i = 0; i < threadPool.size(); ++i) {
int dif = timeoutMs - (int)measurer.elapsed_m();
if (dif < 0) return false;
if (!threadPool[i]->waitForFinish(dif)) return false;
}
return true;
}
void PIThreadPoolExecutor::shutdownNow() {
isShutdown_ = true;
for (size_t i = 0; i < threadPool.size(); ++i) threadPool[i]->stop();
}
PIThreadPoolExecutor::~PIThreadPoolExecutor() {
shutdownNow();
while (threadPool.size() > 0) delete threadPool.take_back();
if (queue_own)
delete taskQueue;
}
void PIThreadPoolExecutor::execute(const std::function<void()> & runnable) {
if (!isShutdown_) taskQueue->offer(runnable);
}
bool PIThreadPoolExecutor::isShutdown() const {
return isShutdown_;
}
void PIThreadPoolExecutor::shutdown() {
isShutdown_ = true;
}

View File

@@ -1,63 +0,0 @@
/*
PIP - Platform Independent Primitives
Stephan Fomenko
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/>.
*/
#ifndef PITHREADPOOLEXECUTOR_H
#define PITHREADPOOLEXECUTOR_H
#include "piblockingdequeue.h"
#include <atomic>
class PIP_EXPORT PIThreadPoolExecutor {
public:
explicit PIThreadPoolExecutor(size_t corePoolSize = -1, PIBlockingDequeue<std::function<void()> > * taskQueue_ = 0);
virtual ~PIThreadPoolExecutor();
/**
* @brief Executes the given task sometime in the future. The task execute in an existing pooled thread. If the task
* cannot be submitted for execution, either because this executor has been shutdown or because its capacity has been
* reached.
*
* @param runnable not empty function for thread pool execution
*/
void execute(const std::function<void()> & runnable);
void shutdownNow();
/**
* @brief Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be
* accepted. Invocation has no additional effect if already shut down. This method does not wait for previously
* submitted tasks to complete execution. Use awaitTermination to do that.
*/
void shutdown();
bool isShutdown() const;
bool awaitTermination(int timeoutMs);
private:
std::atomic_bool isShutdown_;
PIBlockingDequeue<std::function<void()> > * taskQueue;
PIVector<PIThread*> threadPool;
bool queue_own;
};
#endif // PITHREADPOOLEXECUTOR_H

View File

@@ -34,32 +34,81 @@ int main() {
class db { class db {
public: public:
db() { db() {
name = PIStringAscii("sflner;ljner.vjnrevsg;j35m4;gberjg2mnv");
for (int i=0; i<10000; ++i) for (int i=0; i<10000; ++i)
x << sin(double(i)/180.0); x << sin(double(i)/180.0);
//printf("jkfkhg\n"); //printf("jkfkhg\n");
} }
private: PIString name;
PIVector<double> x; PIVector<double> x;
}; };
inline PIByteArray & operator <<(PIByteArray & ba, const db & v) {
PIChunkStream cs;
cs.add(1, v.name).add(2, v.x);
ba << cs.data();
return ba;
}
inline PIByteArray & operator >>(PIByteArray & ba, db & v) {
PIByteArray src; ba >> src; PIChunkStream cs(src);
while (!cs.atEnd()) {
switch (cs.read()) {
case 1: cs.get(v.name); break;
case 2: cs.get(v.x); break;
}
}
return ba;
}
PIEthernet eth;
#include "picodeparser.h" #include "picodeparser.h"
int main() { int main() {
/*PIString s(" 324 654 sf 5fdwg sdfsdf sdfefg"); //PICodeParser cp;
piCout << s; //cp.parseFile("SH_plugin_base.h");
piCout << s.replaceAll(' ', '1');*/
piDebug = false; PIMap<int, PIString> m;
m[1] = "one";
m[2] = "two";
auto it = m.makeIterator();
while (it.next()) {
//it.next();
it.valueRef() << "_!";
piCout << it.key() << it.value();
}
/*eth.__meta_data
piForeachC (auto & i, cp.enums) {
i.
}*/
/*piDebug = false;
double min = -1, max = -1, mean = 0; double min = -1, max = -1, mean = 0;
for (int i = 0; i < 1; ++i) { const int iterations = 50;
PICodeParser cp; db d, d2;
for (int i = 0; i < iterations; ++i) {
//PICodeParser cp;
PITimeMeasurer tm; PITimeMeasurer tm;
cp.parseFile("SH_plugin_base.h"); for (int j = 0; j < 100; ++j) {
PIByteArray ba;
ba << d;
}
//cp.parseFile("SH_plugin_base.h");
double ms = tm.elapsed_m(); double ms = tm.elapsed_m();
if (min < 0) min = ms; if (min < 0) min = ms;
if (max < 0) max = ms; if (max < 0) max = ms;
min = piMin(min, ms); min = piMin(min, ms);
max = piMax(max, ms); max = piMax(max, ms);
mean += ms; mean += ms;
PIByteArray ba;
ba << d;
d2.name.clear();
d2.x.clear();
ba >> d2;
} }
piDebug = true; piDebug = true;
piCout << min << (mean / 50) << max; piCout << d2.name << d2.x.size_s();
piCout << min << (mean / iterations) << max;*/
} }

View File

@@ -4,10 +4,12 @@ macro(pip_test NAME LIBS)
file(GLOB _CPPS "${NAME}/*.cpp") file(GLOB _CPPS "${NAME}/*.cpp")
file(GLOB _HDRS "${NAME}/*.h") file(GLOB _HDRS "${NAME}/*.h")
set(_target pip_${NAME}_test) set(_target pip_${NAME}_test)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PIP_ROOT_BINARY_DIR}")
add_executable(${_target} ${_CPPS} ${_HDRS}) add_executable(${_target} ${_CPPS} ${_HDRS})
target_link_libraries(${_target} pip ${LIBS} gtest_main gmock_main) target_link_libraries(${_target} pip ${LIBS} gtest_main gmock_main)
add_test(NAME ${_target} COMMAND tests) add_test(NAME ${_target} COMMAND tests)
add_custom_target(${_target}_perform ALL COMMAND ${_target}) add_custom_target(${_target}_perform ALL COMMAND ${_target})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY)
list(APPEND PIP_TESTS_LIST "${NAME}") list(APPEND PIP_TESTS_LIST "${NAME}")
set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE) set(PIP_TESTS_LIST ${PIP_TESTS_LIST} PARENT_SCOPE)
endmacro() endmacro()

View File

@@ -1,110 +1,363 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "testutil.h"
#include "piblockingdequeue.h" #include "piblockingdequeue.h"
class MockConditionVar: public PIConditionVariable { using ::testing::_;
using ::testing::Return;
using ::testing::Eq;
using ::testing::Ne;
using ::testing::Matcher;
using ::testing::Expectation;
using ::testing::Sequence;
using ::testing::NiceMock;
class MockConditionVar {
public: public:
bool isWaitCalled = false; bool isWaitCalled = false;
bool isWaitForCalled = false; bool isWaitForCalled = false;
bool isTrueCondition = false; bool isTrueCondition = false;
int timeout = -1; int timeout = -1;
void wait(PIMutex& lk) override { MOCK_METHOD1(wait, void(PIMutex&));
isWaitCalled = true; MOCK_METHOD2(wait, void(PIMutex&, const std::function<bool()>&));
MOCK_METHOD2(waitFor, bool(PIMutex&, int));
MOCK_METHOD3(waitFor, bool(PIMutex&, int, const std::function<bool()>&));
MOCK_METHOD0(notifyOne, void());
};
struct QueueElement {
bool is_empty;
int value;
int copy_count;
QueueElement(): is_empty(true), value(0), copy_count(0) { }
explicit QueueElement(int value): is_empty(false), value(value), copy_count(0) { }
QueueElement(const QueueElement& other) {
this->is_empty = other.is_empty;
this->value = other.value;
this->copy_count = 0;
const_cast<int&>(other.copy_count)++;
}
QueueElement(QueueElement&& other) noexcept : QueueElement() {
std::swap(is_empty, other.is_empty);
std::swap(value, other.value);
std::swap(copy_count, other.copy_count);
} }
void wait(PIMutex& lk, const std::function<bool()>& condition) override { bool operator==(const QueueElement &rhs) const {
isWaitCalled = true; return is_empty == rhs.is_empty &&
lk.lock(); value == rhs.value;
isTrueCondition = condition();
lk.unlock();
} }
bool waitFor(PIMutex& lk, int timeoutMs) override { bool operator!=(const QueueElement &rhs) const {
isWaitForCalled = true; return !(rhs == *this);
timeout = timeoutMs;
return false;
} }
bool waitFor(PIMutex& lk, int timeoutMs, const std::function<bool()>& condition) override { friend std::ostream& operator<<(std::ostream& os, const QueueElement& el) {
isWaitForCalled = true; return os << "{ is_empty:" << el.is_empty << ", value:" << el.value << ", copy_count:" << el.copy_count << " }";
lk.lock();
isTrueCondition = condition();
timeout = timeoutMs;
lk.unlock();
return isTrueCondition;
} }
}; };
TEST(BlockingDequeueUnitTest, put_is_block_when_capacity_reach) { template<typename T>
size_t capacity = 0; class MockDequeBase {
auto conditionVarAdd = new MockConditionVar(); public:
auto conditionVarRem = new MockConditionVar(); MOCK_METHOD1_T(push_back_rval, void(T));
PIBlockingDequeue<int> dequeue(capacity, conditionVarAdd, conditionVarRem); MOCK_METHOD1_T(push_back, void(const T&));
dequeue.put(11); MOCK_METHOD0(size, size_t());
ASSERT_TRUE(conditionVarRem->isWaitCalled); MOCK_METHOD0_T(front, T());
ASSERT_FALSE(conditionVarRem->isTrueCondition); MOCK_METHOD0(pop_front, void());
void push_back(T&& t) {
push_back_rval(t);
}
};
template<typename T>
class MockDeque: public NiceMock<MockDequeBase<T>> {};
class PIBlockingDequeuePrepare: public PIBlockingDequeue<QueueElement, MockDeque, NiceMock<MockConditionVar>> {
public:
typedef PIBlockingDequeue<QueueElement, MockDeque, NiceMock<MockConditionVar>> SuperClass;
explicit PIBlockingDequeuePrepare(size_t capacity = SIZE_MAX): SuperClass(capacity) { }
template<typename Iterable,
typename std::enable_if<!std::is_arithmetic<Iterable>::value, int>::type = 0>
explicit PIBlockingDequeuePrepare(const Iterable& other): SuperClass(other) { }
MockConditionVar* getCondVarAdd() { return this->cond_var_add; }
MockConditionVar* getCondVarRem() { return this->cond_var_rem; }
MockDeque<QueueElement>& getQueue() { return this->data_queue; }
size_t getMaxSize() { return max_size; }
};
class BlockingDequeueUnitTest: public ::testing::Test {
public:
int timeout = 100;
size_t capacity;
PIBlockingDequeuePrepare dequeue;
QueueElement element;
BlockingDequeueUnitTest(): capacity(1), dequeue(capacity), element(11) {}
void offer2_is_wait_predicate(bool isCapacityReach);
void put_is_wait_predicate(bool isCapacityReach);
void take_is_wait_predicate(bool isEmpty);
};
TEST_F(BlockingDequeueUnitTest, construct_default_is_max_size_eq_size_max) {
PIBlockingDequeuePrepare dequeue;
ASSERT_EQ(dequeue.getMaxSize(), SIZE_MAX);
} }
TEST(BlockingDequeueUnitTest, offer_timedout_is_false_when_capacity_reach) { TEST_F(BlockingDequeueUnitTest, construct_from_constant_is_max_size_eq_capacity) {
size_t capacity = 0; PIBlockingDequeuePrepare dequeue(2);
int timeout = 11; ASSERT_EQ(dequeue.getMaxSize(), 2);
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingDequeue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
ASSERT_FALSE(dequeue.offer(11, timeout));
} }
TEST(BlockingDequeueUnitTest, offer_timedout_is_block_when_capacity_reach) { TEST_F(BlockingDequeueUnitTest, construct_from_capacity_is_max_size_eq_capacity) {
size_t capacity = 0; ASSERT_EQ(dequeue.getMaxSize(), capacity);
int timeout = 11;
auto conditionVarAdd = new MockConditionVar();
auto conditionVarRem = new MockConditionVar();
PIBlockingDequeue<int> dequeue(capacity, conditionVarAdd, conditionVarRem);
dequeue.offer(11, timeout);
EXPECT_TRUE(conditionVarRem->isWaitForCalled);
EXPECT_EQ(timeout, conditionVarRem->timeout);
ASSERT_FALSE(conditionVarRem->isTrueCondition);
} }
TEST(BlockingDequeueUnitTest, offer_is_true_before_capacity_reach) { TEST_F(BlockingDequeueUnitTest, construct_from_iterable) {
size_t capacity = 1; std::vector<QueueElement> iterable;
PIBlockingDequeue<int> dequeue(capacity); iterable.emplace_back(11);
ASSERT_TRUE(dequeue.offer(10)); iterable.emplace_back(22);
PIBlockingDequeuePrepare dequeue(iterable);
} }
TEST(BlockingDequeueUnitTest, offer_is_false_when_capacity_reach) { void BlockingDequeueUnitTest::put_is_wait_predicate(bool isCapacityReach) {
size_t capacity = 1; std::function<bool()> conditionVarPredicate;
PIBlockingDequeue<int> dequeue(capacity); EXPECT_CALL(*dequeue.getCondVarRem(), wait(_, _))
dequeue.offer(11); .WillOnce([&](PIMutex& m, const std::function<bool()>& predicate){ conditionVarPredicate = predicate; });
ASSERT_FALSE(dequeue.offer(10)); dequeue.put(element);
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(isCapacityReach ? capacity : capacity - 1));
ASSERT_EQ(conditionVarPredicate(), !isCapacityReach);
} }
TEST_F(BlockingDequeueUnitTest, put_is_wait_predicate_true) {
put_is_wait_predicate(false);
}
TEST_F(BlockingDequeueUnitTest, put_is_wait_predicate_false_when_capacity_reach) {
put_is_wait_predicate(true);
}
TEST_F(BlockingDequeueUnitTest, put_is_insert_by_copy) {
EXPECT_CALL(dequeue.getQueue(), push_back( Eq(element) ))
.WillOnce(Return());
dequeue.put(element);
}
TEST_F(BlockingDequeueUnitTest, put_is_insert_by_move) {
QueueElement copyElement = element;
EXPECT_CALL(dequeue.getQueue(), push_back_rval( Eq(element) ))
.WillOnce(Return());
dequeue.put(std::move(copyElement));
}
TEST_F(BlockingDequeueUnitTest, put_is_notify_about_insert) {
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
.WillOnce(Return());
dequeue.put(element);
}
TEST_F(BlockingDequeueUnitTest, offer1_is_insert_by_copy) {
EXPECT_CALL(dequeue.getQueue(), push_back( Eq(element) ))
.WillOnce(Return());
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity - 1));
dequeue.offer(element);
}
TEST_F(BlockingDequeueUnitTest, offer1_is_insert_by_move) {
QueueElement copyElement = element;
EXPECT_CALL(dequeue.getQueue(), push_back_rval( Eq(element) ))
.WillOnce(Return());
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity - 1));
dequeue.offer(std::move(copyElement));
}
TEST_F(BlockingDequeueUnitTest, offer1_is_not_insert_when_capacity_reach) {
EXPECT_CALL(dequeue.getQueue(), push_back(_))
.Times(0);
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity));
dequeue.offer(element);
}
TEST_F(BlockingDequeueUnitTest, offer1_is_true_when_insert) {
ON_CALL(dequeue.getQueue(), push_back(_))
.WillByDefault(Return());
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity - 1));
ASSERT_TRUE(dequeue.offer(element));
}
TEST_F(BlockingDequeueUnitTest, offer1_is_false_when_capacity_reach) {
ON_CALL(dequeue.getQueue(), push_back(_))
.WillByDefault(Return());
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity));
ASSERT_FALSE(dequeue.offer(element));
}
TEST_F(BlockingDequeueUnitTest, offer1_is_notify_about_insert) {
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity - 1));
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
.WillOnce(Return());
dequeue.offer(element);
}
TEST_F(BlockingDequeueUnitTest, offer1_is_not_notify_about_insert_when_capacity_reach) {
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(capacity));
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
.Times(0);
dequeue.offer(element);
}
void BlockingDequeueUnitTest::offer2_is_wait_predicate(bool isCapacityReach) {
std::function<bool()> conditionVarPredicate;
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
.WillOnce([&](PIMutex& m, int timeout, const std::function<bool()>& predicate) {
conditionVarPredicate = predicate;
return isCapacityReach;
});
dequeue.offer(element, timeout);
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(isCapacityReach ? capacity : capacity - 1));
ASSERT_EQ(conditionVarPredicate(), !isCapacityReach);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_wait_predicate_true) {
offer2_is_wait_predicate(false);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_wait_predicate_false_when_capacity_reach) {
offer2_is_wait_predicate(true);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_insert_by_copy) {
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
.WillOnce(Return(true));
EXPECT_CALL(dequeue.getQueue(), push_back( Eq(element) ))
.WillOnce(Return());
dequeue.offer(element, timeout);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_insert_by_move) {
QueueElement copyElement = element;
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
.WillOnce(Return(true));
EXPECT_CALL(dequeue.getQueue(), push_back_rval( Eq(element) ))
.WillOnce(Return());
dequeue.offer(std::move(copyElement), timeout);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_not_insert_when_timeout) {
EXPECT_CALL(*dequeue.getCondVarRem(), waitFor(_, Eq(timeout), _))
.WillOnce(Return(false));
EXPECT_CALL(dequeue.getQueue(), push_back(_))
.Times(0);
dequeue.offer(element, timeout);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_true_when_insert) {
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
.WillByDefault(Return(true));
ASSERT_TRUE(dequeue.offer(element, timeout));
}
TEST_F(BlockingDequeueUnitTest, offer2_is_false_when_timeout) {
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
.WillByDefault(Return(false));
ASSERT_FALSE(dequeue.offer(element, timeout));
}
TEST_F(BlockingDequeueUnitTest, offer2_is_notify_about_insert) {
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
.WillByDefault(Return(true));
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
.WillOnce(Return());
dequeue.offer(element, timeout);
}
TEST_F(BlockingDequeueUnitTest, offer2_is_not_notify_about_insert_when_timeout) {
ON_CALL(*dequeue.getCondVarRem(), waitFor(_, _, _))
.WillByDefault(Return(false));
EXPECT_CALL(*dequeue.getCondVarAdd(), notifyOne)
.Times(0);
dequeue.offer(element, timeout);
}
void BlockingDequeueUnitTest::take_is_wait_predicate(bool isEmpty) {
std::function<bool()> conditionVarPredicate;
EXPECT_CALL(*dequeue.getCondVarAdd(), wait(_, _))
.WillOnce([&](PIMutex& m, const std::function<bool()>& predicate) { conditionVarPredicate = predicate; });
dequeue.take();
ON_CALL(dequeue.getQueue(), size)
.WillByDefault(Return(isEmpty ? 0 : 1));
ASSERT_EQ(conditionVarPredicate(), !isEmpty);
}
TEST_F(BlockingDequeueUnitTest, take_is_wait_predicate_true) {
take_is_wait_predicate(false);
}
TEST_F(BlockingDequeueUnitTest, take_is_wait_predicate_false_when_queue_empty) {
take_is_wait_predicate(true);
}
TEST_F(BlockingDequeueUnitTest, take_is_get_and_remove) {
Expectation front = EXPECT_CALL(dequeue.getQueue(), front())
.WillOnce(Return(element));
EXPECT_CALL(dequeue.getQueue(), pop_front())
.After(front)
.WillOnce(Return());
QueueElement takenElement = dequeue.take();
ASSERT_EQ(element, takenElement);
}
TEST_F(BlockingDequeueUnitTest, take_is_notify_about_remove) {
EXPECT_CALL(*dequeue.getCondVarRem(), notifyOne)
.WillOnce(Return());
dequeue.take();
}
/*
// TODO change take_is_block_when_empty to prevent segfault // TODO change take_is_block_when_empty to prevent segfault
TEST(DISABLED_BlockingDequeueUnitTest, take_is_block_when_empty) { TEST(DISABLED_BlockingDequeueUnitTest, take_is_block_when_empty) {
size_t capacity = 1; size_t capacity = 1;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
// May cause segfault because take front of empty queue // May cause segfault because take front of empty queue
dequeue.take(); dequeue.take();
EXPECT_TRUE(conditionVar->isWaitCalled); EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitCalled);
ASSERT_FALSE(conditionVar->isTrueCondition); ASSERT_FALSE(dequeue.getCondVarAdd()->isTrueCondition);
} }
TEST(BlockingDequeueUnitTest, take_is_not_block_when_not_empty) { TEST(BlockingDequeueUnitTest, take_is_not_block_when_not_empty) {
size_t capacity = 1; size_t capacity = 1;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.offer(111); dequeue.offer(111);
dequeue.take(); dequeue.take();
EXPECT_TRUE(conditionVar->isWaitCalled); EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitCalled);
ASSERT_TRUE(conditionVar->isTrueCondition); ASSERT_TRUE(dequeue.getCondVarAdd()->isTrueCondition);
} }
TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) { TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
size_t capacity = 1; size_t capacity = 1;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.take(), 111); ASSERT_EQ(dequeue.take(), 111);
@@ -112,8 +365,7 @@ TEST(BlockingDequeueUnitTest, take_is_value_eq_to_offer_value) {
TEST(BlockingDequeueUnitTest, take_is_last) { TEST(BlockingDequeueUnitTest, take_is_last) {
size_t capacity = 10; size_t capacity = 10;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
EXPECT_TRUE(dequeue.offer(111)); EXPECT_TRUE(dequeue.offer(111));
EXPECT_TRUE(dequeue.offer(222)); EXPECT_TRUE(dequeue.offer(222));
ASSERT_EQ(dequeue.take(), 111); ASSERT_EQ(dequeue.take(), 111);
@@ -123,25 +375,22 @@ TEST(BlockingDequeueUnitTest, take_is_last) {
TEST(BlockingDequeueUnitTest, poll_is_not_block_when_empty) { TEST(BlockingDequeueUnitTest, poll_is_not_block_when_empty) {
size_t capacity = 1; size_t capacity = 1;
bool isOk; bool isOk;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.poll(111, &isOk); dequeue.poll(111, &isOk);
EXPECT_FALSE(conditionVar->isWaitForCalled); EXPECT_FALSE(dequeue.getCondVarAdd()->isWaitForCalled);
} }
TEST(BlockingDequeueUnitTest, poll_is_default_value_when_empty) { TEST(BlockingDequeueUnitTest, poll_is_default_value_when_empty) {
size_t capacity = 1; size_t capacity = 1;
bool isOk; bool isOk;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
ASSERT_EQ(dequeue.poll(111, &isOk), 111); ASSERT_EQ(dequeue.poll(111, &isOk), 111);
} }
TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) { TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
size_t capacity = 1; size_t capacity = 1;
bool isOk; bool isOk;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.poll(-1, &isOk), 111); ASSERT_EQ(dequeue.poll(-1, &isOk), 111);
} }
@@ -149,47 +398,42 @@ TEST(BlockingDequeueUnitTest, poll_is_offer_value_when_not_empty) {
TEST(BlockingDequeueUnitTest, poll_timeouted_is_block_when_empty) { TEST(BlockingDequeueUnitTest, poll_timeouted_is_block_when_empty) {
size_t capacity = 1; size_t capacity = 1;
int timeout = 11; int timeout = 11;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.poll(timeout, 111); dequeue.poll(timeout, 111);
EXPECT_TRUE(conditionVar->isWaitForCalled); EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitForCalled);
EXPECT_EQ(timeout, conditionVar->timeout); EXPECT_EQ(timeout, dequeue.getCondVarAdd()->timeout);
ASSERT_FALSE(conditionVar->isTrueCondition); ASSERT_FALSE(dequeue.getCondVarAdd()->isTrueCondition);
} }
TEST(BlockingDequeueUnitTest, poll_timeouted_is_default_value_when_empty) { TEST(BlockingDequeueUnitTest, poll_timeouted_is_default_value_when_empty) {
size_t capacity = 1; size_t capacity = 1;
int timeout = 11; int timeout = 11;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
ASSERT_EQ(dequeue.poll(timeout, 111), 111); ASSERT_EQ(dequeue.poll(timeout, 111), 111);
} }
TEST(BlockingDequeueUnitTest, poll_timeouted_is_not_block_when_not_empty) { TEST(BlockingDequeueUnitTest, poll_timeouted_is_not_block_when_not_empty) {
size_t capacity = 1; size_t capacity = 1;
int timeout = 11; int timeout = 11;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.offer(111); dequeue.offer(111);
dequeue.poll(timeout, -1); dequeue.poll(timeout, -1);
EXPECT_TRUE(conditionVar->isWaitForCalled); EXPECT_TRUE(dequeue.getCondVarAdd()->isWaitForCalled);
ASSERT_TRUE(conditionVar->isTrueCondition); ASSERT_TRUE(dequeue.getCondVarAdd()->isTrueCondition);
} }
TEST(BlockingDequeueUnitTest, poll_timeouted_is_offer_value_when_not_empty) { TEST(BlockingDequeueUnitTest, poll_timeouted_is_offer_value_when_not_empty) {
size_t capacity = 1; size_t capacity = 1;
int timeout = 11; int timeout = 11;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.poll(timeout, -1), 111); ASSERT_EQ(dequeue.poll(timeout, -1), 111);
} }
TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) { TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
size_t capacity = 10; size_t capacity = 10;
auto conditionVar = new MockConditionVar(); PIBlockingDequeuePrepare<int> dequeue(capacity);
PIBlockingDequeue<int> dequeue(capacity, conditionVar);
dequeue.offer(111); dequeue.offer(111);
dequeue.offer(222); dequeue.offer(222);
ASSERT_EQ(dequeue.poll(10, -1), 111); ASSERT_EQ(dequeue.poll(10, -1), 111);
@@ -198,13 +442,13 @@ TEST(BlockingDequeueUnitTest, poll_timeouted_is_last) {
TEST(BlockingDequeueUnitTest, capacity_is_eq_constructor_capacity) { TEST(BlockingDequeueUnitTest, capacity_is_eq_constructor_capacity) {
size_t capacity = 10; size_t capacity = 10;
PIBlockingDequeue<int> dequeue(capacity); PIBlockingDequeuePrepare<int> dequeue(capacity);
ASSERT_EQ(dequeue.capacity(), capacity); ASSERT_EQ(dequeue.capacity(), capacity);
} }
TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) { TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
size_t capacity = 2; size_t capacity = 2;
PIBlockingDequeue<int> dequeue(capacity); PIBlockingDequeuePrepare<int> dequeue(capacity);
ASSERT_EQ(dequeue.remainingCapacity(), capacity); ASSERT_EQ(dequeue.remainingCapacity(), capacity);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.remainingCapacity(), capacity - 1); ASSERT_EQ(dequeue.remainingCapacity(), capacity - 1);
@@ -212,7 +456,7 @@ TEST(BlockingDequeueUnitTest, remainingCapacity_is_dif_of_capacity_and_size) {
TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) { TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
size_t capacity = 1; size_t capacity = 1;
PIBlockingDequeue<int> dequeue(capacity); PIBlockingDequeuePrepare<int> dequeue(capacity);
dequeue.offer(111); dequeue.offer(111);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.remainingCapacity(), 0); ASSERT_EQ(dequeue.remainingCapacity(), 0);
@@ -220,7 +464,7 @@ TEST(BlockingDequeueUnitTest, remainingCapacity_is_zero_when_capacity_reach) {
TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) { TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
size_t capacity = 1; size_t capacity = 1;
PIBlockingDequeue<int> dequeue(capacity); PIBlockingDequeuePrepare<int> dequeue(capacity);
ASSERT_EQ(dequeue.size(), 0); ASSERT_EQ(dequeue.size(), 0);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.size(), 1); ASSERT_EQ(dequeue.size(), 1);
@@ -228,7 +472,7 @@ TEST(BlockingDequeueUnitTest, size_is_eq_to_num_of_elements) {
TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) { TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
size_t capacity = 1; size_t capacity = 1;
PIBlockingDequeue<int> dequeue(capacity); PIBlockingDequeuePrepare<int> dequeue(capacity);
dequeue.offer(111); dequeue.offer(111);
dequeue.offer(111); dequeue.offer(111);
ASSERT_EQ(dequeue.size(), capacity); ASSERT_EQ(dequeue.size(), capacity);
@@ -236,29 +480,31 @@ TEST(BlockingDequeueUnitTest, size_is_eq_to_capacity_when_capacity_reach) {
TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) { TEST(BlockingDequeueUnitTest, drainTo_is_elements_moved) {
size_t capacity = 10; size_t capacity = 10;
PIDeque<int> refDeque; std::deque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10); for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10);
PIBlockingDequeue<int> blockingDequeue(refDeque); PIBlockingDequeuePrepare<int> blockingDequeue(refDeque);
PIDeque<int> deque; PIBlockingDequeuePrepare<int>::QueueType deque;
blockingDequeue.drainTo(deque); blockingDequeue.drainTo(deque);
ASSERT_EQ(blockingDequeue.size(), 0); ASSERT_EQ(blockingDequeue.size(), 0);
ASSERT_TRUE(deque == refDeque); // FIXME
// ASSERT_TRUE(deque == refDeque);
} }
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_size_when_all_moved) { TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_size_when_all_moved) {
size_t capacity = 10; size_t capacity = 10;
PIDeque<int> refDeque; std::deque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10); for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10);
PIBlockingDequeue<int> blockingDequeue(refDeque); PIBlockingDequeuePrepare<int> blockingDequeue(refDeque);
PIDeque<int> deque; PIBlockingDequeuePrepare<int>::QueueType deque;
ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size()); ASSERT_EQ(blockingDequeue.drainTo(deque), refDeque.size());
} }
TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) { TEST(BlockingDequeueUnitTest, drainTo_is_ret_eq_to_maxCount) {
size_t capacity = 10; size_t capacity = 10;
PIDeque<int> refDeque; std::deque<int> refDeque;
for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10); for (size_t i = 0; i < capacity / 2; ++i) refDeque.push_back(i * 10);
PIBlockingDequeue<int> blockingDequeue(refDeque); PIBlockingDequeuePrepare<int> blockingDequeue(refDeque);
PIDeque<int> deque; PIBlockingDequeuePrepare<int>::QueueType deque;
ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1); ASSERT_EQ(blockingDequeue.drainTo(deque, refDeque.size() - 1), refDeque.size() - 1);
} }
*/

View File

@@ -3,7 +3,7 @@
#include "pithread.h" #include "pithread.h"
#include "testutil.h" #include "testutil.h"
class ConditionVariable : public ::testing::Test, public TestUtil { class ConditionVariableIntegrationTest : public ::testing::Test, public TestUtil {
public: public:
PIMutex m; PIMutex m;
PIConditionVariable* variable; PIConditionVariable* variable;
@@ -19,30 +19,30 @@ protected:
} }
}; };
TEST_F(ConditionVariable, wait_is_block) { TEST_F(ConditionVariableIntegrationTest, wait_is_block) {
createThread(); createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_block_when_notifyOne_before_wait) { TEST_F(ConditionVariableIntegrationTest, wait_is_block_when_notifyOne_before_wait) {
variable->notifyOne(); variable->notifyOne();
createThread(); createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_block_when_notifyAll_before_wait) { TEST_F(ConditionVariableIntegrationTest, wait_is_block_when_notifyAll_before_wait) {
variable->notifyAll(); variable->notifyAll();
createThread(); createThread();
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_unblock_when_notifyOne_after_wait) { TEST_F(ConditionVariableIntegrationTest, wait_is_unblock_when_notifyOne_after_wait) {
createThread(); createThread();
variable->notifyOne(); variable->notifyOne();
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) { TEST_F(ConditionVariableIntegrationTest, wait_is_unblock_when_notifyAll_after_wait) {
PIVector<PIThread*> threads; PIVector<PIThread*> threads;
for (int i = 0; i < THREAD_COUNT; ++i) { for (int i = 0; i < THREAD_COUNT; ++i) {
@@ -61,7 +61,7 @@ TEST_F(ConditionVariable, wait_is_unblock_when_notifyAll_after_wait) {
piForeach(PIThread* thread, threads) delete thread; piForeach(PIThread* thread, threads) delete thread;
} }
TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) { TEST_F(ConditionVariableIntegrationTest, wait_is_one_unblock_when_notifyOne) {
PIVector<PIThread*> threads; PIVector<PIThread*> threads;
for (int i = 0; i < THREAD_COUNT; ++i) { for (int i = 0; i < THREAD_COUNT; ++i) {
@@ -77,7 +77,7 @@ TEST_F(ConditionVariable, wait_is_one_unblock_when_notifyOne) {
ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1); ASSERT_EQ(runningThreadCount, THREAD_COUNT - 1);
} }
TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) { TEST_F(ConditionVariableIntegrationTest, wait_is_protected_unblock_when_notifyOne) {
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m); variable->wait(m);
@@ -89,7 +89,7 @@ TEST_F(ConditionVariable, wait_is_protected_unblock_when_notifyOne) {
ASSERT_FALSE(m.tryLock()); ASSERT_FALSE(m.tryLock());
} }
TEST_F(ConditionVariable, wait_condition_is_block) { TEST_F(ConditionVariableIntegrationTest, wait_condition_is_block) {
createThread([&](){ createThread([&](){
m.lock(); m.lock();
variable->wait(m, [](){ return false; }); variable->wait(m, [](){ return false; });
@@ -98,7 +98,7 @@ TEST_F(ConditionVariable, wait_condition_is_block) {
ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_FALSE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) { TEST_F(ConditionVariableIntegrationTest, wait_condition_is_check_condition_before_block) {
bool isConditionChecked = false; bool isConditionChecked = false;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
@@ -113,7 +113,7 @@ TEST_F(ConditionVariable, wait_condition_is_check_condition_before_block) {
m.unlock(); m.unlock();
} }
TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) { TEST_F(ConditionVariableIntegrationTest, wait_condition_is_check_condition_when_notifyOne) {
bool isConditionChecked; bool isConditionChecked;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
@@ -133,7 +133,7 @@ TEST_F(ConditionVariable, wait_condition_is_check_condition_when_notifyOne) {
m.unlock(); m.unlock();
} }
TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne) { TEST_F(ConditionVariableIntegrationTest, wait_condition_is_unblock_when_condition_and_notifyOne) {
bool condition = false; bool condition = false;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
@@ -147,7 +147,7 @@ TEST_F(ConditionVariable, wait_condition_is_unblock_when_condition_and_notifyOne
ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS)); ASSERT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS));
} }
TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) { TEST_F(ConditionVariableIntegrationTest, DISABLED_waitFor_is_block_before_timeout) {
createThread([&](){ createThread([&](){
PITimeMeasurer measurer; PITimeMeasurer measurer;
m.lock(); m.lock();
@@ -159,7 +159,7 @@ TEST_F(ConditionVariable, DISABLED_waitFor_is_block_before_timeout) {
EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3)); EXPECT_TRUE(thread->waitForFinish(WAIT_THREAD_TIME_MS * 3));
} }
TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) { TEST_F(ConditionVariableIntegrationTest, waitFor_is_unblock_when_timeout) {
std::atomic_bool isUnblock(false); std::atomic_bool isUnblock(false);
createThread([&](){ createThread([&](){
m.lock(); m.lock();
@@ -172,7 +172,7 @@ TEST_F(ConditionVariable, waitFor_is_unblock_when_timeout) {
ASSERT_TRUE(isUnblock); ASSERT_TRUE(isUnblock);
} }
TEST_F(ConditionVariable, waitFor_is_false_when_timeout) { TEST_F(ConditionVariableIntegrationTest, waitFor_is_false_when_timeout) {
bool waitRet = true; bool waitRet = true;
createThread([&](){ createThread([&](){
m.lock(); m.lock();
@@ -183,7 +183,7 @@ TEST_F(ConditionVariable, waitFor_is_false_when_timeout) {
ASSERT_FALSE(waitRet); ASSERT_FALSE(waitRet);
} }
TEST_F(ConditionVariable, waitFor_is_unblock_when_condition_and_notifyOne) { TEST_F(ConditionVariableIntegrationTest, waitFor_is_unblock_when_condition_and_notifyOne) {
bool condition = false; bool condition = false;
createThread([&](){ createThread([&](){
m.lock(); m.lock();

View File

@@ -1,8 +1,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "pithreadpoolexecutor.h" #include "testutil.h"
#include "pimutex.h" #include "pimutex.h"
#include "piexecutor.h"
const int WAIT_THREAD_TIME_MS = 30;
TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) { TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) {
PIMutex m; PIMutex m;
@@ -14,11 +13,13 @@ TEST(ExcutorIntegrationTest, execute_is_runnable_invoke) {
m.unlock(); m.unlock();
}); });
piMSleep(WAIT_THREAD_TIME_MS); piMSleep(WAIT_THREAD_TIME_MS);
m.lock();
ASSERT_EQ(invokedRunnables, 1); ASSERT_EQ(invokedRunnables, 1);
m.unlock();
} }
TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) { TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) {
bool isRunnableInvoke = false; volatile bool isRunnableInvoke = false;
PIThreadPoolExecutor executorService(1); PIThreadPoolExecutor executorService(1);
executorService.shutdown(); executorService.shutdown();
executorService.execute([&]() { executorService.execute([&]() {
@@ -29,7 +30,7 @@ TEST(ExcutorIntegrationTest, execute_is_not_execute_after_shutdown) {
} }
TEST(ExcutorIntegrationTest, execute_is_execute_before_shutdown) { TEST(ExcutorIntegrationTest, execute_is_execute_before_shutdown) {
bool isRunnableInvoke = false; volatile bool isRunnableInvoke = false;
PIThreadPoolExecutor executorService(1); PIThreadPoolExecutor executorService(1);
executorService.execute([&]() { executorService.execute([&]() {
piMSleep(WAIT_THREAD_TIME_MS); piMSleep(WAIT_THREAD_TIME_MS);

View File

@@ -0,0 +1,131 @@
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "testutil.h"
#include "piexecutor.h"
using ::testing::_;
using ::testing::SetArgReferee;
using ::testing::DoAll;
using ::testing::DeleteArg;
using ::testing::Return;
using ::testing::ByMove;
using ::testing::AtLeast;
using ::testing::ByRef;
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Pointee;
using ::testing::IsNull;
using ::testing::NiceMock;
typedef std::function<void()> VoidFunc;
namespace std {
inline bool operator ==(const VoidFunc& s, const VoidFunc& v) {
// TODO VoidFunc operator ==
return true;
}
}
class MockThread {
public:
VoidFunc runnnable;
MockThread(VoidFunc runnnable) : runnnable(runnnable) { }
MOCK_METHOD0(start, bool());
MOCK_METHOD0(stop, void());
MOCK_METHOD1(waitForStart, bool(int timeout_msecs));
MOCK_METHOD1(waitForFinish, bool(int timeout_msecs));
};
class MockDeque : public PIBlockingDequeue<FunctionWrapper> {
public:
MOCK_METHOD1(offer, bool(const FunctionWrapper&));
MOCK_METHOD0(take, FunctionWrapper());
MOCK_METHOD1(poll, FunctionWrapper(int));
MOCK_METHOD0(capacity, size_t());
MOCK_METHOD0(remainingCapacity, size_t());
};
typedef PIThreadPoolExecutorTemplate<NiceMock<MockThread>, MockDeque> PIThreadPoolExecutorMoc_t;
class PIThreadPoolExecutorMoc : public PIThreadPoolExecutorMoc_t {
public:
explicit PIThreadPoolExecutorMoc(size_t corePoolSize) : PIThreadPoolExecutorMoc_t(corePoolSize) { }
template<typename Function>
explicit PIThreadPoolExecutorMoc(size_t corePoolSize, Function onBeforeStart) : PIThreadPoolExecutorMoc_t(corePoolSize, onBeforeStart) { }
PIVector<testing::NiceMock<MockThread>*>* getThreadPool() { return &threadPool; }
bool isShutdown() { return isShutdown_; }
MockDeque* getTaskQueue() { return &taskQueue; }
};
TEST(ExecutorUnitTest, is_corePool_created) {
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
ASSERT_EQ(THREAD_COUNT, executor.getThreadPool()->size());
}
TEST(ExecutorUnitTest, is_corePool_started) {
PIThreadPoolExecutorMoc executor(THREAD_COUNT, [](MockThread* thread){
EXPECT_CALL(*thread, start())
.WillOnce(Return(true));
});
EXPECT_EQ(THREAD_COUNT, executor.getThreadPool()->size());
}
TEST(ExecutorUnitTest, submit_is_added_to_taskQueue) {
VoidFunc voidFunc = [](){};
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
// TODO add check of offered
EXPECT_CALL(*executor.getTaskQueue(), offer)
.WillOnce(Return(true));
executor.submit(voidFunc);
}
TEST(ExecutorUnitTest, submit_is_return_valid_future) {
VoidFunc voidFunc = [](){};
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
// TODO add check of offered
EXPECT_CALL(*executor.getTaskQueue(), offer)
.WillOnce(Return(true));
auto future = executor.submit(voidFunc);
EXPECT_TRUE(future.valid());
}
TEST(ExecutorUnitTest, execute_is_added_to_taskQueue) {
VoidFunc voidFunc = [](){};
PIThreadPoolExecutorMoc executor(THREAD_COUNT);
// TODO add check of offered
EXPECT_CALL(*executor.getTaskQueue(), offer)
.WillOnce(Return(true));
executor.execute(voidFunc);
}
TEST(ExecutorUnitTest, is_corePool_execute_queue_elements) {
bool is_executed = false;
PIThreadPoolExecutorMoc executor(1);
EXPECT_EQ(executor.getThreadPool()->size(), 1);
EXPECT_CALL(*executor.getTaskQueue(), poll(Ge(0)))
.WillOnce([&is_executed](int){
return FunctionWrapper([&is_executed](){ is_executed = true; });
});
executor.getThreadPool()->at(0)->runnnable();
ASSERT_TRUE(is_executed);
}
TEST(ExecutorUnitTest, shutdown_is_stop_threads) {
// Exclude stop calls when executor deleting
auto* executor = new PIThreadPoolExecutorMoc(THREAD_COUNT, [](MockThread* thread){
testing::Mock::AllowLeak(thread);
EXPECT_CALL(*thread, stop())
.WillOnce(Return());
});
testing::Mock::AllowLeak(executor);
testing::Mock::AllowLeak(executor->getTaskQueue());
EXPECT_CALL(*executor->getTaskQueue(), poll(Ge(0)))
.WillRepeatedly([](int){ return FunctionWrapper(); });
executor->shutdown();
executor->getThreadPool()->forEach([](MockThread* thread){ thread->runnnable(); });
}

View File

@@ -1,16 +1,14 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "piconditionvar.h"
#include "pithread.h" #include "pithread.h"
#include "testutil.h" #include "testutil.h"
class ConditionLock : public ::testing::Test, public TestUtil { class MutexIntegartionTest : public ::testing::Test, public TestUtil {
public: public:
PIMutex* m = new PIMutex(); PIMutex* m = new PIMutex();
}; };
TEST_F(ConditionLock, lock_is_protect) { TEST_F(MutexIntegartionTest, lock_is_protect) {
m->lock(); m->lock();
bool* isProtect = new bool(true); bool* isProtect = new bool(true);
@@ -22,7 +20,7 @@ TEST_F(ConditionLock, lock_is_protect) {
ASSERT_TRUE(*isProtect); ASSERT_TRUE(*isProtect);
} }
TEST_F(ConditionLock, unlock_is_release) { TEST_F(MutexIntegartionTest, unlock_is_release) {
m->lock(); m->lock();
bool* isReleased = new bool(false); bool* isReleased = new bool(false);
m->unlock(); m->unlock();
@@ -35,7 +33,7 @@ TEST_F(ConditionLock, unlock_is_release) {
ASSERT_TRUE(*isReleased); ASSERT_TRUE(*isReleased);
} }
TEST_F(ConditionLock, tryLock_is_false_when_locked) { TEST_F(MutexIntegartionTest, tryLock_is_false_when_locked) {
createThread([&](){ createThread([&](){
m->lock(); m->lock();
piMSleep(WAIT_THREAD_TIME_MS); piMSleep(WAIT_THREAD_TIME_MS);
@@ -43,11 +41,11 @@ TEST_F(ConditionLock, tryLock_is_false_when_locked) {
ASSERT_FALSE(m->tryLock()); ASSERT_FALSE(m->tryLock());
} }
TEST_F(ConditionLock, tryLock_is_true_when_unlocked) { TEST_F(MutexIntegartionTest, tryLock_is_true_when_unlocked) {
ASSERT_TRUE(m->tryLock()); ASSERT_TRUE(m->tryLock());
} }
TEST_F(ConditionLock, tryLock_is_recursive_lock_enable) { TEST_F(MutexIntegartionTest, tryLock_is_recursive_lock_enable) {
m->lock(); m->lock();
ASSERT_TRUE(m->tryLock()); ASSERT_TRUE(m->tryLock());
} }

View File

@@ -4,13 +4,21 @@
#include "pithread.h" #include "pithread.h"
#include <atomic> #include <atomic>
template<typename T>
void print_type_info() {
std::cout << typeid(T).name() << " is a "
<< (std::is_const<typename std::remove_reference<T>::type>::value ? "const " : "")
<< (std::is_lvalue_reference<T>::value ? "lvalue" : "rvalue")
<< " reference" << std::endl;
}
/** /**
* Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests * Minimum wait thread start, switch context or another interthread communication action time. Increase it if tests
* write "Start thread timeout reach!" message. You can reduce it if you want increase test performance. * write "Start thread timeout reach!" message. You can reduce it if you want increase test performance.
*/ */
const int WAIT_THREAD_TIME_MS = 40; const int WAIT_THREAD_TIME_MS = 30;
const int THREAD_COUNT = 5; const int THREAD_COUNT = 2;
class TestUtil: public PIObject { class TestUtil: public PIObject {
PIOBJECT(TestUtil) PIOBJECT(TestUtil)

View File

@@ -0,0 +1,28 @@
#include "dispatcherclient.h"
DispatcherClient::DispatcherClient(PIEthernet * eth_) {
eth = eth_;
eth->startThreadedRead();
CONNECTU(eth, threadedReadEvent, this, readed);
CONNECTU(eth, disconnected, this, disconnected);
piCoutObj << "client connected" << eth->sendAddress();
}
DispatcherClient::~DispatcherClient() {
}
void DispatcherClient::disconnected(bool withError) {
piCoutObj << "client disconnected" << eth->sendAddress();
disconnectEvent(this);
}
void DispatcherClient::readed(uchar *data, int size) {
PIByteArray ba(data, size);
piCoutObj << "readed" << ba.toHex();
eth->write(ba);
}

View File

@@ -0,0 +1,21 @@
#ifndef DISPATCHERCLIENT_H
#define DISPATCHERCLIENT_H
#include "piethernet.h"
class DispatcherClient: public PIObject {
PIOBJECT(DispatcherClient)
public:
DispatcherClient(PIEthernet * eth_);
~DispatcherClient();
EVENT_HANDLER2(void, readed, uchar * , data, int, size);
EVENT_HANDLER1(void, disconnected, bool, withError);
EVENT1(disconnectEvent, DispatcherClient *, client)
private:
PIEthernet * eth;
};
#endif // DISPATCHERCLIENT_H

View File

@@ -0,0 +1,32 @@
#include "dispatcherserver.h"
DispatcherServer::DispatcherServer(PIEthernet::Address addr) {
eth = new PIEthernet(PIEthernet::TCP_Server);
eth->setParameter(PIEthernet::ReuseAddress);
CONNECTU(eth, newConnection, this, newConnection);
eth->listen(addr, true);
piCoutObj << eth << "server started" << addr;
}
DispatcherServer::~DispatcherServer() {
eth->close();
piCoutObj << "server stoped";
delete eth;
}
void DispatcherServer::disconnectClient(DispatcherClient *client) {
piCoutObj << "remove client" << client;
clients.removeOne(client);
delete client;
}
void DispatcherServer::newConnection(PIEthernet *cl) {
DispatcherClient * client = new DispatcherClient(cl);
piCoutObj << "add client" << client;
CONNECTU(client, disconnectEvent, this, disconnectClient);
clients.push_back(client);
}

View File

@@ -0,0 +1,20 @@
#ifndef DISPATCHERSERVER_H
#define DISPATCHERSERVER_H
#include "dispatcherclient.h"
class DispatcherServer: public PIObject {
PIOBJECT(DispatcherServer)
public:
DispatcherServer(PIEthernet::Address addr);
~DispatcherServer();
EVENT_HANDLER1(void, newConnection, PIEthernet * , cl);
EVENT_HANDLER1(void, disconnectClient, DispatcherClient *, client);
private:
PIEthernet * eth;
PIVector<DispatcherClient*> clients;
};
#endif // DISPATCHERSERVER_H

View File

@@ -19,12 +19,12 @@
#include "pip.h" #include "pip.h"
#include "picrypt.h" #include "picrypt.h"
#include "dispatcherserver.h"
using namespace PICoutManipulators; using namespace PICoutManipulators;
PIString ip = "0.0.0.0"; PIEthernet::Address addr = PIEthernet::Address("0.0.0.0", 10101);
int port = 10101;
void usage() { void usage() {
piCout << Bold << "PIP Cloud Dispatcher"; piCout << Bold << "PIP Cloud Dispatcher";
@@ -49,10 +49,11 @@ int main (int argc, char * argv[]) {
return 0; return 0;
} }
if (cli.hasArgument("ip")) if (cli.hasArgument("ip"))
ip = cli.argumentValue("ip"); addr.setIP(cli.argumentValue("ip"));
if (cli.hasArgument("port")) if (cli.hasArgument("port"))
port = cli.argumentValue("port").toInt(); addr.setPort(cli.argumentValue("port").toInt());
DispatcherServer server(addr);
WAIT_FOR_EXIT
return 0; return 0;
} }

View File

@@ -87,7 +87,8 @@ void makeClassInfo(PIFile & f, const PICodeParser::Entity * e) {
f << "\tci->name = \"" << e->name << "\";\n"; f << "\tci->name = \"" << e->name << "\";\n";
f << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n"; f << "\tci->has_name = " << (e->has_name ? "true" : "false") << ";\n";
if (!e->meta.isEmpty()) { if (!e->meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = e->meta.begin(); i != e->meta.end(); ++i) auto i = e->meta.makeIterator();
while (i.next())
f << "\tci->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; f << "\tci->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
} }
f << "\t(*classesInfo)[ci->name] = ci;\n"; f << "\t(*classesInfo)[ci->name] = ci;\n";
@@ -176,14 +177,16 @@ void makeEnumInfo(PIFile & f, const PICodeParser::Enum * e) {
f << "\t(*enumsInfo)[\"" << e->name << "\"] = ei;\n"; f << "\t(*enumsInfo)[\"" << e->name << "\"] = ei;\n";
f << "\tei->name = \"" << e->name << "\";\n"; f << "\tei->name = \"" << e->name << "\";\n";
if (!e->meta.isEmpty()) { if (!e->meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = e->meta.begin(); i != e->meta.end(); ++i) auto i = e->meta.makeIterator();
while (i.next())
f << "\tei->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; f << "\tei->meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
} }
} }
piForeachC (PICodeParser::EnumeratorInfo & m, e->members) { piForeachC (PICodeParser::EnumeratorInfo & m, e->members) {
f << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n"; f << "\tei->members << PICodeInfo::EnumeratorInfo(\"" << m.name << "\", " << m.value << ");\n";
if (!m.meta.isEmpty()) { if (!m.meta.isEmpty()) {
for (PICodeParser::MetaMap::const_iterator i = m.meta.begin(); i != m.meta.end(); ++i) auto i = m.meta.makeIterator();
while (i.next())
f << "\tei->members.back().meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n"; f << "\tei->members.back().meta[\"" << i.key() << "\"] = PIString::fromUTF8(\"" << i.value() << "\");\n";
} }
} }

View File

@@ -583,8 +583,9 @@ int main(int argc, char * argv[]) {
qt_filters["platforms"] = platforms; qt_filters["platforms"] = platforms;
qt_filters["styles" ] = styles ; qt_filters["styles" ] = styles ;
for (PIMap<PIString, PIStringList>::iterator it = qt_filters.begin(); it != qt_filters.end(); ++it) auto it = qt_filters.makeIterator();
it.value().forEachInplace([](PIString i)->PIString{ while (it.next())
it.valueRef().forEachInplace([](PIString i)->PIString{
if (!i.startsWith("*")) i.prepend("*"); if (!i.startsWith("*")) i.prepend("*");
if (!i.endsWith("*")) i.append("*"); if (!i.endsWith("*")) i.append("*");
return i; return i;

View File

@@ -23,6 +23,8 @@ int main (int argc, char * argv[]) {
return 0; return 0;
} }
#else #else
# define PIP_CONSOLE_STATIC_DEFINE
# include "pip_console_export.h"
# include "piscreentypes.h" # include "piscreentypes.h"
# include "pisharedmemory.h" # include "pisharedmemory.h"
# include "pifile.h" # include "pifile.h"