diff --git a/CMakeLists.txt b/CMakeLists.txt index 95993975..0a66c9f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0017 NEW) # need include() with .cmake project(pip) set(pip_MAJOR 2) -set(pip_MINOR 23) -set(pip_REVISION 2) +set(pip_MINOR 24) +set(pip_REVISION 0) set(pip_SUFFIX ) set(pip_COMPANY SHS) set(pip_DOMAIN org.SHS) diff --git a/doc/examples/piparsehelper.cpp b/doc/examples/piparsehelper.cpp new file mode 100644 index 00000000..f8f5be8a --- /dev/null +++ b/doc/examples/piparsehelper.cpp @@ -0,0 +1,91 @@ +#include "pip.h" + +//! [0] +enum Header { + hInt = 1, + hString, + hVoid +}; + +class MyObj: public PIObject, public PIParseHelper { + PIOBJECT(MyObj); +public: + MyObj(): PIParseHelper(this) { + // Keys with 1 argument + assign(hInt, std::function([this](int i){piCout << "lambda type Int" << i;})); + assign(hInt, HANDLER(methodI)); + assign(hString, HANDLER(methodS)); + + // hVoid key, without arguments + assign(hVoid, [](){piCout << "type void";}); + assign(hVoid, HANDLER(method)); + } + EVENT_HANDLER1(void, methodI, int, i) {piCout << "methodI" << i;} + EVENT_HANDLER1(void, methodS, PIString, s) {piCout << "methodS" << s;} + EVENT_HANDLER0(void, method) {piCout << "method";} +}; + +int main() { + MyObj o; + + PIByteArray data; + + data.clear(); + data << 11; + o.parse(hInt, data); + // lambda type Int 11 + // methodI 11 + + data.clear(); + data << PIString("text"); + o.parse(hString, data); + // methodS text + + data.clear(); + o.parse(hVoid, data); + // type void + // method +} +//! [0] + + +//! [1] +enum Header { + hInt = 1, + hString, + hVoid +}; + +class MyObj: public PIObject { + PIOBJECT(MyObj); +public: + EVENT_HANDLER1(void, methodI, int, i) {piCout << "methodI" << i;} + EVENT_HANDLER1(void, methodS, PIString, s) {piCout << "methodS" << s;} + EVENT_HANDLER0(void, method) {piCout << "method";} +}; + +int main() { + MyObj obj; + PIParseHelper parser(&obj); + + parser.assign(hInt, obj.HANDLER(methodI)); + parser.assign(hString, obj.HANDLER(methodS)); + parser.assign(hVoid, obj.HANDLER(method)); + + PIByteArray data; + + data.clear(); + data << 11; + parser.parse(hInt, data); + // methodI 11 + + data.clear(); + data << PIString("text"); + parser.parse(hString, data); + // methodS text + + data.clear(); + parser.parse(hVoid, data); + // method +} +//! [1] diff --git a/libs/main/io_utils/piioutilsmodule.h b/libs/main/io_utils/piioutilsmodule.h index a012138e..957d7df4 100644 --- a/libs/main/io_utils/piioutilsmodule.h +++ b/libs/main/io_utils/piioutilsmodule.h @@ -26,5 +26,6 @@ #include "pidiagnostics.h" #include "pifiletransfer.h" #include "pipacketextractor.h" +#include "piparsehelper.h" #endif // PIIOUTILSMODULE_H diff --git a/libs/main/io_utils/piparsehelper.h b/libs/main/io_utils/piparsehelper.h new file mode 100644 index 00000000..a44eec1a --- /dev/null +++ b/libs/main/io_utils/piparsehelper.h @@ -0,0 +1,174 @@ +/*! \file piparsehelper.h + * \brief Helper class to automate structs receive +*/ +/* + PIP - Platform Independent Primitives + Helper class to automate structs receive + Ivan Pelipenko peri4ko@yandex.ru + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . +*/ + +#ifndef PIPARSEHELPER_H +#define PIPARSEHELPER_H + +#include "pip_io_utils_export.h" +#include "piobject.h" + + +/** \class PIParseHelper + * \brief Helper class to automate structs receive + * + * + * \section PIParseHelper_synopsis Synopsis + * This class helps to deserialize and invoke neccessarily methods. + * + * Data packets with header and various data types can be automated by this class. + * Every key value mapped to event handler or lambda-function. + * + * This class can remove \b switch-case with deserialization code and + * replace it with several \a assign() calls, binded to ready-to-use event handlers. + * Moreover data type automatic takes from event handler or lambda argument. One should + * only make \"PIByteArray & operator <<()\" with used types, deserialization will be + * performed by %PIParseHelper. + * + * + * \section PIParseHelper_usage Usage + * There are two variants: subclassing and aggregating. + * + * ### Subclass + * + * Inherit your class from %PIParseHelper and construct it with \b this. + * + * In \a assign() methods you can use event handlers with 0 or 1 arguments, + * using HANDLER() macro. + * + * ### Aggregate + * + * Create instance of %PIParseHelper and construct it with target object. + * + * In \a assign() methods you can use event handlers with 0 or 1 arguments, + * using HANDLER() macro in target object namespace (e.g. \"o.HANDLER(slot)\"). + * + * + * \section PIParseHelper_lambda Lambda-functions + * Assign methods that receive lambda-functions can`t accept direct lambda (\"[](){}\") + * with template type deducation because of C++ specific. E.g. + * \code assign(1, [this](SomeStruct s){}) \endcode + * doesn`t compile, but these variants allowed: + * \code assign(1, std::function([this](SomeStruct s){})) \endcode + * \code assign(1, [this](SomeStruct s){}) \endcode + * + * + * \section PIParseHelper_examples Examples + * First example describes subclass variant. As one can see, it`s a single place to change + * type of received data - event handler argument. + * \snippet piparsehelper.cpp 0 + * + * Second example show separate variant: + * \snippet piparsehelper.cpp 1 + * + **/ + +template +class PIParseHelper { +public: + + //! \brief Construct %PIParseHelper with target object \"p\" + PIParseHelper(PIObject * p): parent(p) {} + + + //! \brief Assign key \"key\" to event handler \"handler\" with 1 argument + template + void assign(Key key, Ret(*handler)(void*,T)) { + if (!parent) return; + auto mf = PIObject::__meta_data().value(parent->classNameID()); + auto it = mf.eh_func.makeIterator(); + while (it.hasNext()) { + it.next(); + if (it.value().addr == handler) { + void * addr = it.value().addr; + auto func = [addr](PIObject * o, PIByteArray data){ + T v; data >> v; + ((void(*)(void*, T))addr)(o, v); + }; + functions[key] << func; + break; + } + } + } + + + //! \brief Assign key \"key\" to event handler \"handler\" without arguments + template + void assign(Key key, Ret(*handler)(void*)) { + if (!parent) return; + auto mf = PIObject::__meta_data().value(parent->classNameID()); + auto it = mf.eh_func.makeIterator(); + while (it.hasNext()) { + it.next(); + if (it.value().addr == handler) { + void * addr = it.value().addr; + auto func = [addr](PIObject * o, PIByteArray ){ + ((void(*)(void*))addr)(o); + }; + functions[key] << func; + break; + } + } + } + + + //! \brief Assign key \"key\" to lambda-function \"func\" with 1 argument + //! \note Important! Direct lambda functions are not allowed, see \ref PIParseHelper_lambda + template + void assign(Key key, std::function func) { + if (!parent) return; + auto lf = [func](PIObject * , PIByteArray data){ + if (!data.isEmpty()) { + T v; data >> v; + func(v); + } + }; + functions[key] << lf; + } + + + //! \brief Assign key \"key\" to lambda-function \"func\" without arguments + //! \note Important! Direct lambda functions are not allowed, see \ref PIParseHelper_lambda + void assign(Key key, std::function func) { + if (!parent) return; + auto lf = [func](PIObject * , PIByteArray ){ + func(); + }; + functions[key] << lf; + } + + + //! \brief Deserialize data and invoke assigned to \"key\" methods + void parse(Key key, PIByteArray ba) { + if (!parent) return; + auto fl = functions.value(key); + for (auto f: fl) + f(parent, ba); + } + +private: + PIMap>> functions; + PIObject * parent; + +}; + + +#endif // PIPARSEHELPER_H diff --git a/main.cpp b/main.cpp index 7114f936..82236406 100644 --- a/main.cpp +++ b/main.cpp @@ -1,27 +1,39 @@ #include "pip.h" -extern "C" { -# include "lua.h" -# include "lauxlib.h" -# include "lualib.h" -} -#include -#include "pistring_std.h" +enum Header { + hInt = 1, + hString, + hVoid +}; -static const char * script -= "-- script.lua \n" - "testString = \"LuaBridge works!\" \n" - "number = 42 \n"; +class MyObj: public PIObject { + PIOBJECT(MyObj); +public: + EVENT_HANDLER1(void, methodI, int, i) {piCout << "methodI" << i;} + EVENT_HANDLER1(void, methodS, PIString, s) {piCout << "methodS" << s;} + EVENT_HANDLER0(void, method) {piCout << "method";} +}; -using namespace luabridge; int main() { - lua_State* L = luaL_newstate(); - luaL_dostring(L, script); - luaL_openlibs(L); - lua_pcall(L, 0, 0, 0); - LuaRef s = getGlobal(L, "testString"); - LuaRef n = getGlobal(L, "number"); - std::string luaString = s.cast(); - int answer = n.cast(); - piCout << StdString2PIString(luaString); - piCout << "And here's our number:" << answer; + MyObj o; + PIParseHelper parser(&o); + + parser.assign(hInt, o.HANDLER(methodI)); + parser.assign(hString, o.HANDLER(methodS)); + parser.assign(hVoid, o.HANDLER(method)); + + PIByteArray data; + + data.clear(); + data << 11; + parser.parse(hInt, data); + // methodI 11 + + data.clear(); + data << PIString("text"); + parser.parse(hString, data); + // methodS text + + data.clear(); + parser.parse(hVoid, data); + // method }