/*! \file piparsehelper.h
* \ingroup IO
* \~\brief
* \~english Helper class to automate structs receive
* \~russian Класс для автоматизации приема структур
*/
/*
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 "piobject.h"
#include "pip_io_utils_export.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