mirror of
https://github.com/romixlab/qmsgpack.git
synced 2025-08-04 20:54:27 +02:00
Qt types stream operators, documentation updated, custom types support improved, bug fixes and new tests.
This commit is contained in:
@@ -73,6 +73,22 @@ Unpacking is handled by ``MsgPack::unpack()`` function:
|
||||
If packed data contains only one msgpack type (fixstr of fixmap for example), unpack will return it as ``QVariant(QString())`` and ``QVariant(QMap())`` respectively.
|
||||
But if there are several values packed, ``QVariant(QList())`` will be returned (consider this 5 bool values packed without msgpack's list: [0xc3, 0xc3, 0xc3, 0xc3, 0xc3])
|
||||
|
||||
More types
|
||||
==========
|
||||
|
||||
There are built in packers and unpackers (basic and stream ones) for following types:
|
||||
``QPoint, QSize, QRect, QTime, QDate, QDateTime, QColor, QGeoCoordinate``.
|
||||
But since there is no such types in msgpack spec, ext type is used.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
MsgPack::registerType(QMetaType::QPoint, 37); // 37 is msgpack user type id
|
||||
QByteArray ba = MsgPack::pack(QPoint(12, 34));
|
||||
qDebug() << MsgPack::unpack(ba).toPoint();
|
||||
|
||||
Note, that QColor and QGeoCoordinate is enabled by default only in qmake project.
|
||||
|
||||
Thread safety
|
||||
=============
|
||||
|
141
doc/custom.rst
Normal file
141
doc/custom.rst
Normal file
@@ -0,0 +1,141 @@
|
||||
Custom types
|
||||
------------
|
||||
|
||||
.. contents::
|
||||
:depth: 4
|
||||
|
||||
Custom QVariant
|
||||
===============
|
||||
|
||||
You can provide two functions for packing QVariant with custom type inside to QByteArray and vise versa.
|
||||
For example here is how QColor packer and unpacker looks like:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
QByteArray pack_qcolor(const QVariant &variant)
|
||||
{
|
||||
QColor color = variant.value<QColor>();
|
||||
if (!color.isValid())
|
||||
return QByteArray(1, 0);
|
||||
QByteArray data;
|
||||
data.resize(4);
|
||||
quint8 *p = (quint8 *)data.data();
|
||||
p[0] = color.red();
|
||||
p[1] = color.green();
|
||||
p[2] = color.blue();
|
||||
p[3] = color.alpha();
|
||||
return data;
|
||||
}
|
||||
|
||||
QVariant unpack_qcolor(const QByteArray &data)
|
||||
{
|
||||
if (data.length() == 1)
|
||||
return QColor();
|
||||
quint8 *p = (quint8 *)data.data();
|
||||
return QColor(p[0], p[1], p[2], p[3]);
|
||||
}
|
||||
|
||||
And that's it!
|
||||
Now register this two functions:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
MsgPack::registerPacker(QMetaType::QColor, 3, pack_qcolor); // 3 is msgpack ext type id
|
||||
MsgPack::registerUnpacker(3, unpack_qcolor);
|
||||
|
||||
After that ``MsgPack::pack(QColor(127, 127, 127))`` will start to work!
|
||||
|
||||
Custom stream
|
||||
=============
|
||||
|
||||
You can provide stream operators for any other type you might want to work with. But there are some pitfalls to consider if you want to unpack something with other MsgPack implementations.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
class SomeType
|
||||
{
|
||||
public:
|
||||
double x() const { return m_x; }
|
||||
void setX(double x) { m_x = x;}
|
||||
|
||||
double y() const { return m_y; }
|
||||
void setY(double y) { m_y = y;}
|
||||
|
||||
double z() const { return m_z; }
|
||||
void setZ(double z) { m_z = z;}
|
||||
|
||||
private:
|
||||
double m_x, m_y, m_z;
|
||||
};
|
||||
|
||||
MsgPackStream &operator<<(MsgPackStream &s, const SomeType &t)
|
||||
{
|
||||
s.writeExtHeader(27, <msgpack user type id>); // size of packed double is 9 * 3 = 27
|
||||
s << t.x() << t.y() << t.z();
|
||||
return s;
|
||||
}
|
||||
|
||||
MsgPackStream &operator>>(MsgPackStream &s, SomeType &t)
|
||||
{
|
||||
quint32 len;
|
||||
s.readExtHeader(len);
|
||||
if (len != 27) {
|
||||
s.setStatus(MsgPackStream::ReadCorruptData);
|
||||
return s;
|
||||
}
|
||||
double x, y, z;
|
||||
s >> x >> y >> z;
|
||||
t.setX(x);
|
||||
t.setY(y);
|
||||
t.setZ(z);
|
||||
return s;
|
||||
}
|
||||
|
||||
In this case size of data is known id advance, if this is not the case, then you can use QByteArray. Here is how QPoint operators are implemented:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
MsgPackStream &operator<<(MsgPackStream &s, const QPoint &point)
|
||||
{
|
||||
// we need to know user type id, that was registered with MsgPack::registerType
|
||||
qint8 msgpackType = MsgPack::msgpackType(QMetaType::QPoint);
|
||||
if (msgpackType == -1) {
|
||||
s.setStatus(MsgPackStream::WriteFailed);
|
||||
return s;
|
||||
}
|
||||
QByteArray ba;
|
||||
MsgPackStream out(&ba, QIODevice::WriteOnly); // stream inside stream ;)
|
||||
if (point.isNull()) { // save some bytes if point is invalid
|
||||
quint8 p[1] = {0};
|
||||
out.writeBytes((const char *)p, 1);
|
||||
} else {
|
||||
out << point.x() << point.y();
|
||||
}
|
||||
s.writeExtHeader(ba.length(), msgpackType); // only now write msgpack ext field
|
||||
s.writeBytes(ba.data(), ba.length()); // and variable length data
|
||||
return s;
|
||||
}
|
||||
|
||||
MsgPackStream &operator>>(MsgPackStream &s, QPoint &point)
|
||||
{
|
||||
quint32 len;
|
||||
s.readExtHeader(len); // read msgpack ext field
|
||||
if (len == 1) { // handle invalid QPoint
|
||||
point = QPoint();
|
||||
return s;
|
||||
}
|
||||
QByteArray ba;
|
||||
ba.resize(len);
|
||||
s.readBytes(ba.data(), len); // read len bytes to byte array
|
||||
MsgPackStream in(ba);
|
||||
int x, y;
|
||||
in >> x >> y;
|
||||
point = QPoint(x, y);
|
||||
return s;
|
||||
}
|
||||
|
||||
.. tip::
|
||||
|
||||
Of course you can just stream out everything without any ext header and user type id's, like this: ``s << point.x() << point.y();`` but in that case you will not be able to unpack anything useful with MsgPack::unpack() or in other MsgPack implementations.
|
@@ -6,6 +6,8 @@ qmsgpack - MessagePack serializer implementation for Qt
|
||||
|
||||
install.rst
|
||||
basics.rst
|
||||
stream.rst
|
||||
custom.rst
|
||||
|
||||
Contents:
|
||||
|
||||
|
@@ -5,7 +5,7 @@ Installation
|
||||
:depth: 4
|
||||
|
||||
qmsgpack is a pure Qt library (Qt4 and Qt5 supported), so you can build it for almost any platform Qt supports.
|
||||
Also there are several build options:
|
||||
There are two build methods:
|
||||
|
||||
- CMake
|
||||
- qmake
|
||||
@@ -48,6 +48,14 @@ There are several useful cmake options available:
|
||||
|
||||
Change build type to debug mode (default is `Release`), could be very useful if something goes wrong
|
||||
|
||||
.. cmdoption:: -DWITH_GUI_TYPES=True
|
||||
|
||||
Build with support for QtGui types (QColor)
|
||||
|
||||
.. cmdoption:: -DWITH_LOCATION_TYPES=True
|
||||
|
||||
Build with support for QtLocation types(QGeoCoordinate). Might not work, because CMake seems to be failing to find QtLocation, in this case you can try qmake instead.
|
||||
|
||||
Add options before ``..`` as follow:
|
||||
|
||||
.. code-block:: bash
|
||||
|
68
doc/stream.rst
Normal file
68
doc/stream.rst
Normal file
@@ -0,0 +1,68 @@
|
||||
Streams
|
||||
-------
|
||||
|
||||
.. contents::
|
||||
:depth: 4
|
||||
|
||||
There are QDataStream analogue with almost identical API. Every basic type is supported as well as QList<T> and Qt types (QPoint, QSize, QRect, QTime, QDate, QDateTime, QColor, QGeoCoordinate). More types are on the way.
|
||||
|
||||
Packing
|
||||
=======
|
||||
|
||||
You can use any QIODevice derived class or QByteArray.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
QByteArray ba;
|
||||
MsgPackStream out(&ba, QIODevice::WriteOnly);
|
||||
out << 1 << true << "Hello";
|
||||
qDebug() << ba.toHex();
|
||||
|
||||
Of course you can unpack this byte array using ``MsgPack::unpack``:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
qDebug() << MsgPack::unpack(ba);
|
||||
// output:
|
||||
// QVariant(QVariantList, (QVariant(uint, 1), QVariant(bool, true), QVariant(QString, "Hello")))
|
||||
|
||||
QList of any type are also supported:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
QList<int> list;
|
||||
list << 1 << 2 << 3;
|
||||
QByteArray ba;
|
||||
MsgPackStream out(&ba, QIODevice::WriteOnly);
|
||||
out << list;
|
||||
|
||||
Unpacking
|
||||
=========
|
||||
|
||||
To unpack QByteArray just pass it by value, or use QIODevice::ReadOnly for other devices:
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
MsgPackStream in(ba);
|
||||
QList<int> list2;
|
||||
in >> list2;
|
||||
qDebug() << list2; // (1, 2, 3)
|
||||
|
||||
More types
|
||||
==========
|
||||
|
||||
Include ``<qmsgpack/stream/geometry.h`` (or location.h or time.h) for additional types.
|
||||
And do not forget to register type, so that MsgPackStream will know which user type id to use.
|
||||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <qmsgpack/stream/geometry.h>
|
||||
// ...
|
||||
|
||||
MsgPack::registerType(QMetaType::QPoint, 3);
|
||||
QByteArray ba;
|
||||
MsgPackStream out(&ba, QIODevice::WriteOnly);
|
||||
out << QPoint(1, 2) << QPoint();
|
||||
qDebug() << MsgPack::unpack(ba);
|
||||
// output:
|
||||
// QVariant(QVariantList, (QVariant(QPoint, QPoint(1,2)), QVariant(QPoint, QPoint(0,0))))
|
Reference in New Issue
Block a user