version 4.2.0
move toStdFunction() to pibase.h refactor PIParseHelper, now it much more abstract and useful fix PIIODevice::createFromFullPath() when whitespaces at start or end are presence PIStreamPacker add events for start and end packet receive PIClientServer::ClientBase add virtual methods for start and end packet receive. also one can enable diagnostics with enableDiagnostics() method PICout now call flush() on each end of output add PIString::entries(const PIString & str)
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
* 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.
|
||||
* Every key value mapped to object member function, lambda-function or functor.
|
||||
*
|
||||
* 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.
|
||||
@@ -48,30 +48,15 @@
|
||||
*
|
||||
*
|
||||
* \section PIParseHelper_usage Usage
|
||||
* There are two variants: subclassing and aggregating.
|
||||
*
|
||||
* ### Subclass
|
||||
* Create instance of %PIParseHelper, or 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)\").
|
||||
* In \a assign() methods you can use object member function, lambda-function
|
||||
* or functor with 0 or 1 arguments,
|
||||
*
|
||||
*
|
||||
* \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<void(SomeStruct)>([this](SomeStruct s){})) \endcode
|
||||
* \code assign<SomeStruct>(1, [this](SomeStruct s){}) \endcode
|
||||
* \code assign(1, [this](const SomeStruct & s){}) \endcode
|
||||
*
|
||||
*
|
||||
* \section PIParseHelper_examples Examples
|
||||
@@ -87,56 +72,21 @@
|
||||
template<typename Key>
|
||||
class PIParseHelper {
|
||||
public:
|
||||
//! \brief Construct %PIParseHelper with target object \"p\"
|
||||
PIParseHelper(PIObject * p): parent(p) {}
|
||||
//! \brief Construct %PIParseHelper
|
||||
PIParseHelper() {}
|
||||
|
||||
|
||||
//! \brief Assign key \"key\" to event handler \"handler\" with 1 argument
|
||||
template<typename T, typename Ret>
|
||||
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<typename Ret>
|
||||
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\" without arguments
|
||||
void assign(Key key, std::function<void()> func) {
|
||||
auto lf = [func](PIByteArray) { func(); };
|
||||
functions[key] << lf;
|
||||
}
|
||||
|
||||
|
||||
//! \brief Assign key \"key\" to lambda-function \"func\" with 1 argument
|
||||
//! \note Important! Direct lambda functions are not allowed, see \ref PIParseHelper_lambda
|
||||
template<typename T>
|
||||
void assign(Key key, std::function<void(T)> func) {
|
||||
if (!parent) return;
|
||||
auto lf = [func](PIObject *, PIByteArray data) {
|
||||
void assign(Key key, std::function<void(const T &)> func) {
|
||||
auto lf = [func](PIByteArray data) {
|
||||
if (!data.isEmpty()) {
|
||||
T v;
|
||||
data >> v;
|
||||
@@ -147,26 +97,44 @@ public:
|
||||
}
|
||||
|
||||
|
||||
//! \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<void()> func) {
|
||||
if (!parent) return;
|
||||
auto lf = [func](PIObject *, PIByteArray) { func(); };
|
||||
//! \brief Assign key \"key\" to member function of object \"obj\" without arguments
|
||||
template<typename O>
|
||||
void assign(Key key, O * obj, void (O::*member_func)()) {
|
||||
auto lf = [member_func, obj](PIByteArray) { (obj->*member_func)(); };
|
||||
functions[key] << lf;
|
||||
}
|
||||
|
||||
|
||||
//! \brief Assign key \"key\" to member function of object \"obj\" with 1 argument
|
||||
template<typename T, typename O>
|
||||
void assign(Key key, O * obj, void (O::*member_func)(const T &)) {
|
||||
auto lf = [member_func, obj](PIByteArray data) {
|
||||
if (!data.isEmpty()) {
|
||||
T v;
|
||||
data >> v;
|
||||
(obj->*member_func)(v);
|
||||
}
|
||||
};
|
||||
functions[key] << lf;
|
||||
}
|
||||
|
||||
|
||||
//! \brief Assign key \"key\" to functor \"func\" with 0 or 1 argument
|
||||
template<typename L>
|
||||
void assign(Key key, L func) {
|
||||
return assign(key, toStdFunction(func));
|
||||
}
|
||||
|
||||
|
||||
//! \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);
|
||||
f(ba);
|
||||
}
|
||||
|
||||
private:
|
||||
PIMap<Key, PIVector<std::function<void(PIObject *, PIByteArray)>>> functions;
|
||||
PIObject * parent;
|
||||
PIMap<Key, PIVector<std::function<void(PIByteArray)>>> functions;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ public:
|
||||
//! Returns packet sinature, default 0xAFBE
|
||||
ushort packetSign() const { return packet_sign; }
|
||||
|
||||
//! Returns progress of current packet receive in bytes
|
||||
int receivePacketProgress() const { return packet.size_s(); }
|
||||
|
||||
|
||||
//! Set receive aggressive optimization. If yes then %PIStreamPacker doesn`t
|
||||
//! check every byte in incoming stream but check only begin of each read()
|
||||
@@ -90,6 +93,8 @@ public:
|
||||
void assignDevice(PIIODevice * dev);
|
||||
|
||||
EVENT1(packetReceiveEvent, PIByteArray &, data);
|
||||
EVENT1(startPacketReceive, int, size);
|
||||
EVENT0(endPacketReceive);
|
||||
EVENT1(sendRequest, PIByteArray, data);
|
||||
|
||||
//! \handlers
|
||||
@@ -107,6 +112,12 @@ public:
|
||||
//! \fn void packetReceiveEvent(PIByteArray data)
|
||||
//! \brief Raise on packet successfully received
|
||||
|
||||
//! \fn void startPacketReceive(int size)
|
||||
//! \brief Raise on start receive packet with overall size \"size\"
|
||||
|
||||
//! \fn void endPacketReceive()
|
||||
//! \brief Raise on finish receive packet
|
||||
|
||||
//! \fn void sendRequest(PIByteArray data)
|
||||
//! \brief Raise from \a send() function. This data should
|
||||
//! be directly sended to your device. You can
|
||||
|
||||
Reference in New Issue
Block a user