Browse Source

Complete refactoring of serializer

- Create abstract serializer interface
- Complete migration to serializer interface
TODO: Make serializer selection on GRPC level
Alexey Edelev 5 years ago
parent
commit
5d1b3cfa30

+ 5 - 4
src/generator/templates.cpp

@@ -31,6 +31,7 @@ const char *Templates::DefaultProtobufIncludesTemplate = "#include <QMetaType>\n
                                                          "#include <QList>\n"
                                                          "#include <QtQml/QQmlListProperty>\n"
                                                          "#include <qprotobufobject.h>\n"
+                                                         "#include <qqmllistpropertyconstructor.h>\n"
                                                          "#include <unordered_map>\n"
                                                          "#include <QSharedPointer>\n\n";
 
@@ -117,7 +118,7 @@ const char *Templates::GetterContainerExtraTemplate = "$type$ &$property_name$()
                                         "}\n\n";
 
 const char *Templates::QmlListGetterTemplate = "QQmlListProperty<$type_nolist$> $property_name$_l() {\n"
-                                               "    return qtprotobuf::ProtobufObjectPrivate::constructQmlListProperty<$type_nolist$>(this, &m_$property_name$);\n"
+                                               "    return qtprotobuf::constructQmlListProperty<$type_nolist$>(this, &m_$property_name$);\n"
                                                "}\n\n";
 
 const char *Templates::SetterTemplateMessageType = "void set$property_name_cap$_p($type$ *$property_name$) {\n"
@@ -149,7 +150,7 @@ const char *Templates::SetterTemplateSimpleType = "void set$property_name_cap$(c
 const char *Templates::SignalsBlockTemplate = "\nsignals:\n";
 const char *Templates::SignalTemplate = "void $property_name$Changed();\n";
 
-const char *Templates::FieldsOrderingContainerTemplate = "const std::unordered_map<int, int> $type$::propertyOrdering = {";
+const char *Templates::FieldsOrderingContainerTemplate = "const qtprotobuf::QProtobufPropertyOrdering $type$::propertyOrdering = {";
 const char *Templates::FieldOrderTemplate = "{$field_number$, $property_number$}";
 
 const char *Templates::EnumTemplate = "$type$";
@@ -176,7 +177,7 @@ const char *Templates::RegisterQmlListPropertyMetaTypeTemplate = "qRegisterMetaT
 
 const char *Templates::QEnumTemplate = "Q_ENUM($type$)\n";
 
-const char *Templates::MapSerializationRegisterTemplate = "qtprotobuf::ProtobufObjectPrivate::registerMap<$key_type$, $value_type$>();";
+const char *Templates::MapSerializationRegisterTemplate = "qtprotobuf::QProtobufSerializerRegistry::registerMap<$key_type$, $value_type$>();\n";
 
 const char *Templates::ClassDefinitionTemplate = "\nclass $classname$ : public $parent_class$\n"
                                                  "{\n";
@@ -206,7 +207,7 @@ const char *Templates::ClientMethodDefinitionAsync2Template = "\nvoid $classname
                                                               "    });\n"
                                                               "}\n";
 
-const char *Templates::RegisterSerializersTemplate = "qtprotobuf::ProtobufObjectPrivate::registerSerializers<$classname$>();\n";
+const char *Templates::RegisterSerializersTemplate = "qtprotobuf::QProtobufSerializerRegistry::registerSerializers<$classname$>();\n";
 const char *Templates::RegistratorTemplate = "static qtprotobuf::RegistrationHelper helper(registerTypes);\n";
 const char *Templates::QmlRegisterTypeTemplate = "qmlRegisterType<$namespaces$::$classname$>(\"$package$\", 1, 0, \"$classname$\");\n";
 const char *Templates::QmlRegisterTypeUncreatableTemplate = "qmlRegisterUncreatableType<$namespaces$::$classname$>(\"$package$\", 1, 0, \"$classname$\", \"$namespaces$::$classname$ Could not be created from qml context\");\n";

+ 10 - 7
src/protobuf/CMakeLists.txt

@@ -13,18 +13,21 @@ set(CMAKE_AUTORCC ON)
 find_package(Qt5 COMPONENTS Core Qml REQUIRED)
 
 file(GLOB SOURCES
-    qprotobufobject_p.cpp
     qtprotobuf.cpp
-    qtprotobuflogging.cpp)
-#    qprotobufserializer.cpp)
+    qtprotobuflogging.cpp
+    qprotobufserializerregistry.cpp
+    qabstractprotobufserializer.cpp
+    qprotobufserializer.cpp)
 
 file(GLOB HEADERS
-    qprotobufobject_p.h
-    qprotobufobject.h
+    qtprotobuf_global.h
     qtprotobuftypes.h
-    selfcheckiterator.h
     qtprotobuflogging.h
-    qtprotobuf_global.h
+    qprotobufobject.h
+    qprotobufserializerregistry.h
+    qqmllistpropertyconstructor.h
+    qabstractprotobufserializer.h
+    selfcheckiterator.h
     registrationhelper.h)
 
 add_library(${TARGET} SHARED ${SOURCES})

+ 55 - 0
src/protobuf/qabstractprotobufserializer.cpp

@@ -0,0 +1,55 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <QMetaProperty>
+#include <QVariant>
+#include <QMetaObject>
+
+#include "qabstractprotobufserializer.h"
+
+using namespace qtprotobuf;
+
+QByteArray QAbstractProtobufSerializer::serializeObjectCommon(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject)
+{
+    QByteArray result;
+    for (const auto &field : propertyOrdering) {
+        int propertyIndex = field.second;
+        int fieldIndex = field.first;
+        Q_ASSERT_X(fieldIndex < 536870912 && fieldIndex > 0, "", "fieldIndex is out of range");
+        QMetaProperty metaProperty = metaObject.property(propertyIndex);
+        const char *propertyName = metaProperty.name();
+        const QVariant &propertyValue = object->property(propertyName);
+        result.append(serializeProperty(propertyValue, fieldIndex, metaProperty.isEnumType()));
+    }
+
+    return result;
+}
+
+void QAbstractProtobufSerializer::deserializeObjectCommon(QObject *object, const QByteArray &data, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject)
+{
+    for (SelfcheckIterator it(data); it != data.end();) {
+        deserializeProperty(object, it, propertyOrdering, metaObject);
+    }
+}

+ 94 - 0
src/protobuf/qabstractprotobufserializer.h

@@ -0,0 +1,94 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QVariant>
+#include <QMetaObject>
+
+#include <unordered_map>
+#include <functional>
+
+#include "qtprotobuftypes.h"
+#include "qtprotobuflogging.h"
+#include "selfcheckiterator.h"
+
+#include "qtprotobuf_global.h"
+
+namespace qtprotobuf {
+
+class QTPROTOBUFSHARED_EXPORT QAbstractProtobufSerializer
+{
+public:
+    using Serializer = std::function<QByteArray(const QVariant &, int &)>;
+    using Deserializer = std::function<void(SelfcheckIterator &, QVariant &)>;
+    struct SerializationHandlers {
+        Serializer serializer;
+        Deserializer deserializer;
+        WireTypes type;
+    };
+    using SerializerRegistry = std::unordered_map<int/*metatypeid*/, SerializationHandlers>;
+
+    virtual ~QAbstractProtobufSerializer() = default;
+    virtual QByteArray serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum) = 0;
+    virtual void deserializeProperty(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) = 0;
+
+    virtual QByteArray serializeObject(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) = 0;
+    virtual void deserializeObject(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) = 0;
+
+    virtual QByteArray serializeListObject(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject, int fieldIndex) = 0;
+    virtual void deserializeListObject(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) = 0;
+
+    virtual QByteArray serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex) = 0;
+    virtual void deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it) = 0;
+
+    QByteArray serializeObjectCommon(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject);
+    void deserializeObjectCommon(QObject *object, const QByteArray &array, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject);
+
+    SerializerRegistry serializers;
+};
+
+
+class QTPROTOBUFSHARED_EXPORT QProtobufSerializer : public QAbstractProtobufSerializer
+{
+public:
+    QProtobufSerializer();
+    ~QProtobufSerializer() = default;
+
+    QByteArray serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum) override;
+    void deserializeProperty(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) override;
+
+    QByteArray serializeObject(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) override;
+    void deserializeObject(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) override;
+
+    QByteArray serializeListObject(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject, int fieldIndex) override;
+    void deserializeListObject(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject) override;
+
+    QByteArray serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex) override;
+    void deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it) override;
+};
+
+}

+ 4 - 3
src/protobuf/qprotobufobject.h

@@ -25,12 +25,13 @@
 
 #pragma once
 
-#include "qprotobufobject_p.h"
+#include "qprotobufserializerregistry.h"
+#include <unordered_map>
 
 #define Q_DECLARE_PROTOBUF_SERIALIZERS(T)\
     public:\
-        QByteArray serialize() const { return qtprotobuf::ProtobufObjectPrivate::serializeObject<T>(this); }\
-        void deserialize(const QByteArray &array) { qtprotobuf::ProtobufObjectPrivate::deserializeObject<T>(this, array); }\
+        QByteArray serialize() const { return qtprotobuf::QProtobufSerializerRegistry::serialize<T>(this); }\
+        void deserialize(const QByteArray &array) { qtprotobuf::QProtobufSerializerRegistry::deserialize<T>(this, array); }\
     private:
 
 #define Q_PROTOBUF_OBJECT\

+ 0 - 193
src/protobuf/qprotobufobject_p.cpp

@@ -1,193 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
- *
- * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this
- * software and associated documentation files (the "Software"), to deal in the Software
- * without restriction, including without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
- * to permit persons to whom the Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies
- * or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
- * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "qprotobufobject_p.h"
-
-#include "qprotobufserializer_p.h"
-
-using namespace qtprotobuf;
-
-ProtobufObjectPrivate::SerializerRegistry ProtobufObjectPrivate::serializers = {};
-
-void ProtobufObjectPrivate::registerSerializers()
-{
-    QProtobufSerializerPrivate::wrapSerializer<float>(QProtobufSerializerPrivate::serializeBasic<float>, QProtobufSerializerPrivate::deserializeBasic<float>, Fixed32);
-    QProtobufSerializerPrivate::wrapSerializer<double>(QProtobufSerializerPrivate::serializeBasic<double>, QProtobufSerializerPrivate::deserializeBasic<double>, Fixed64);
-    QProtobufSerializerPrivate::wrapSerializer<int32>(QProtobufSerializerPrivate::serializeBasic<int32>, QProtobufSerializerPrivate::deserializeBasic<int32>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<int64>(QProtobufSerializerPrivate::serializeBasic<int64>, QProtobufSerializerPrivate::deserializeBasic<int64>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<uint32>(QProtobufSerializerPrivate::serializeBasic<uint32>, QProtobufSerializerPrivate::deserializeBasic<uint32>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<uint64>(QProtobufSerializerPrivate::serializeBasic<uint64>, QProtobufSerializerPrivate::deserializeBasic<uint64>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<sint32>(QProtobufSerializerPrivate::serializeBasic<sint32>, QProtobufSerializerPrivate::deserializeBasic<sint32>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<sint64>(QProtobufSerializerPrivate::serializeBasic<sint64>, QProtobufSerializerPrivate::deserializeBasic<sint64>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<fixed32>(QProtobufSerializerPrivate::serializeBasic<fixed32>, QProtobufSerializerPrivate::deserializeBasic<fixed32>, Fixed32);
-    QProtobufSerializerPrivate::wrapSerializer<fixed64>(QProtobufSerializerPrivate::serializeBasic<fixed64>, QProtobufSerializerPrivate::deserializeBasic<fixed64>, Fixed64);
-    QProtobufSerializerPrivate::wrapSerializer<sfixed32>(QProtobufSerializerPrivate::serializeBasic<sfixed32>, QProtobufSerializerPrivate::deserializeBasic<sfixed32>, Fixed32);
-    QProtobufSerializerPrivate::wrapSerializer<sfixed64>(QProtobufSerializerPrivate::serializeBasic<sfixed64>, QProtobufSerializerPrivate::deserializeBasic<sfixed64>, Fixed64);
-    QProtobufSerializerPrivate::wrapSerializer<bool>(QProtobufSerializerPrivate::serializeBasic<uint32>, QProtobufSerializerPrivate::deserializeBasic<uint32>, Varint);
-    QProtobufSerializerPrivate::wrapSerializer<QString>(QProtobufSerializerPrivate::serializeBasic<QString>, QProtobufSerializerPrivate::deserializeBasic<QString>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<QByteArray>(QProtobufSerializerPrivate::serializeBasic<QByteArray>, QProtobufSerializerPrivate::deserializeBasic<QByteArray>, LengthDelimited);
-
-    QProtobufSerializerPrivate::wrapSerializer<FloatList>(QProtobufSerializerPrivate::serializeListType<float>, QProtobufSerializerPrivate::deserializeList<float>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<DoubleList>(QProtobufSerializerPrivate::serializeListType<double>, QProtobufSerializerPrivate::deserializeList<double>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<fixed32List>(QProtobufSerializerPrivate::serializeListType<fixed32>, QProtobufSerializerPrivate::deserializeList<fixed32>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<fixed64List>(QProtobufSerializerPrivate::serializeListType<fixed64>, QProtobufSerializerPrivate::deserializeList<fixed64>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<sfixed32List>(QProtobufSerializerPrivate::serializeListType<sfixed32>, QProtobufSerializerPrivate::deserializeList<sfixed32>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<sfixed64List>(QProtobufSerializerPrivate::serializeListType<sfixed64>, QProtobufSerializerPrivate::deserializeList<sfixed64>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<int32List>(QProtobufSerializerPrivate::serializeListType<int32>, QProtobufSerializerPrivate::deserializeList<int32>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<int64List>(QProtobufSerializerPrivate::serializeListType<int64>, QProtobufSerializerPrivate::deserializeList<int64>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<sint32List>(QProtobufSerializerPrivate::serializeListType<sint32>, QProtobufSerializerPrivate::deserializeList<sint32>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<sint64List>(QProtobufSerializerPrivate::serializeListType<sint64>, QProtobufSerializerPrivate::deserializeList<sint64>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<uint32List>(QProtobufSerializerPrivate::serializeListType<uint32>, QProtobufSerializerPrivate::deserializeList<uint32>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<uint64List>(QProtobufSerializerPrivate::serializeListType<uint64>, QProtobufSerializerPrivate::deserializeList<uint64>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<QStringList>(QProtobufSerializerPrivate::serializeListType<QString>, QProtobufSerializerPrivate::deserializeList<QString>, LengthDelimited);
-    QProtobufSerializerPrivate::wrapSerializer<QByteArrayList>(QProtobufSerializerPrivate::serializeListType<QByteArray>, QProtobufSerializerPrivate::deserializeList<QByteArray>, LengthDelimited);
-}
-
-QByteArray ProtobufObjectPrivate::serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum)
-{
-    qProtoDebug() << __func__ << "propertyValue" << propertyValue << "fieldIndex" << fieldIndex
-                  << static_cast<QMetaType::Type>(propertyValue.type());
-
-    QByteArray result;
-    WireTypes type = UnknownWireType;
-
-    int userType = propertyValue.userType();
-    if (isEnum) {
-        userType = qMetaTypeId<int64>();
-    }
-    auto serializer = serializers.at(userType);//Throws exception if not found
-    type = serializer.type;
-    result.append(serializer.serializer(propertyValue, fieldIndex));
-
-    if (fieldIndex != NotUsedFieldIndex
-            && type != UnknownWireType) {
-        result.prepend(QProtobufSerializerPrivate::encodeHeader(fieldIndex, type));
-    }
-    return result;
-}
-
-QByteArray ProtobufObjectPrivate::serializeObjectCommon(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
-{
-    QByteArray result;
-    for (const auto &field : propertyOrdering) {
-        int propertyIndex = field.second;
-        int fieldIndex = field.first;
-        Q_ASSERT_X(fieldIndex < 536870912 && fieldIndex > 0, "", "fieldIndex is out of range");
-        QMetaProperty metaProperty = metaObject.property(propertyIndex);
-        const char *propertyName = metaProperty.name();
-        const QVariant &propertyValue = object->property(propertyName);
-        result.append(ProtobufObjectPrivate::serializeProperty(propertyValue, fieldIndex, metaProperty.isEnumType()));
-    }
-
-    return result;
-}
-
-void ProtobufObjectPrivate::deserializeObjectCommon(QObject *object, const QByteArray &data, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
-{
-    for (SelfcheckIterator it(data); it != data.end();) {
-        deserializeProperty(object, it, propertyOrdering, metaObject);
-    }
-}
-
-QByteArray ProtobufObjectPrivate::serializeObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
-{
-    return QProtobufSerializerPrivate::prependLengthDelimitedSize(serializeObjectCommon(object, propertyOrdering, metaObject));
-}
-
-void ProtobufObjectPrivate::deserializeObject(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
-{
-    QByteArray array = QProtobufSerializerPrivate::deserializeLengthDelimited(it);
-    deserializeObjectCommon(object, array, propertyOrdering, metaObject);
-}
-
-QByteArray ProtobufObjectPrivate::serializeListObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject, int fieldIndex)
-{
-    QByteArray result = QProtobufSerializerPrivate::encodeHeader(fieldIndex, LengthDelimited);
-    result.append(serializeObject(object, propertyOrdering, metaObject));
-    return result;
-}
-
-QByteArray ProtobufObjectPrivate::serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex)
-{
-    QByteArray result = QProtobufSerializerPrivate::encodeHeader(fieldIndex, LengthDelimited);
-    result.append(QProtobufSerializerPrivate::prependLengthDelimitedSize(serializeProperty(key, 1, false) + serializeProperty(value, 2, false)));
-    return result;
-}
-
-void ProtobufObjectPrivate::deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it)
-{
-    int mapIndex = 0;
-    WireTypes type = WireTypes::UnknownWireType;
-    unsigned int count = QProtobufSerializerPrivate::deserializeVarintCommon<uint32>(it);
-    qProtoDebug() << __func__ << "count:" << count;
-    SelfcheckIterator last = it + count;
-    while (it != last) {
-        QProtobufSerializerPrivate::decodeHeader(it, mapIndex, type);
-        if(mapIndex == 1) {
-            deserializeMapField(key, it);
-        } else {
-            deserializeMapField(value, it);
-        }
-    }
-}
-
-
-void ProtobufObjectPrivate::deserializeProperty(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject)
-{
-    //Each iteration we expect iterator is setup to beginning of next chunk
-    int fieldNumber = NotUsedFieldIndex;
-    WireTypes wireType = UnknownWireType;
-    if (!QProtobufSerializerPrivate::decodeHeader(it, fieldNumber, wireType)) {
-        qProtoCritical() << "Message received doesn't contains valid header byte. "
-                            "Trying next, but seems stream is broken" << QString::number((*it), 16);
-        throw std::invalid_argument("Message received doesn't contains valid header byte. "
-                              "Seems stream is broken");
-    }
-
-    auto propertyNumberIt = propertyOrdering.find(fieldNumber);
-    if (propertyNumberIt == std::end(propertyOrdering)) {
-        auto bytesCount = QProtobufSerializerPrivate::skipSerializedFieldBytes(it, wireType);
-        qProtoWarning() << "Message received contains unexpected/optional field. WireType:" << wireType
-                        << ", field number: " << fieldNumber << "Skipped:" << (bytesCount + 1) << "bytes";
-        return;
-    }
-
-    int propertyIndex = propertyNumberIt->second;
-    QMetaProperty metaProperty = metaObject.property(propertyIndex);
-
-    qProtoDebug() << __func__ << " wireType: " << wireType << " metaProperty: " << metaProperty.typeName()
-                  << "currentByte:" << QString::number((*it), 16);
-
-    QVariant newPropertyValue;
-    if (metaProperty.isEnumType()) {
-        newPropertyValue = QVariant::fromValue(int32_t(QProtobufSerializerPrivate::deserializeBasic<int64>(it).value<int64>()._t));
-    } else {
-        newPropertyValue = metaProperty.read(object);
-        int userType = metaProperty.userType();
-        auto deserializer = serializers.at(userType).deserializer;//Throws exception if not found
-        deserializer(it, newPropertyValue);
-    }
-    metaProperty.write(object, newPropertyValue);
-}

+ 0 - 322
src/protobuf/qprotobufobject_p.h

@@ -1,322 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
- *
- * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this
- * software and associated documentation files (the "Software"), to deal in the Software
- * without restriction, including without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
- * to permit persons to whom the Software is furnished to do so, subject to the following
- * conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies
- * or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
- * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
- * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#pragma once
-
-#include <QObject>
-#include <QSharedPointer>
-#include <QMetaProperty>
-#include <QQmlListProperty>
-#include <QQmlEngine>
-
-#include <unordered_map>
-#include <memory>
-#include <type_traits>
-#include <functional>
-
-#include "qtprotobuftypes.h"
-#include "qtprotobuflogging.h"
-#include "selfcheckiterator.h"
-
-#include "qtprotobuf_global.h"
-
-#define ASSERT_FIELD_NUMBER(X) Q_ASSERT_X(X < 536870912 && X > 0, T::staticMetaObject.className(), "fieldIndex is out of range")
-
-namespace qtprotobuf {
-
-class QTPROTOBUFSHARED_EXPORT ProtobufObjectPrivate
-{
-    ProtobufObjectPrivate() = delete;
-    ~ProtobufObjectPrivate() = delete;
-    Q_DISABLE_COPY(ProtobufObjectPrivate)
-    ProtobufObjectPrivate(ProtobufObjectPrivate &&) = delete;
-    ProtobufObjectPrivate &operator =(ProtobufObjectPrivate &&) = delete;
-public:
-    using Serializer = std::function<QByteArray(const QVariant &, int &)>;
-    using Deserializer = std::function<void(SelfcheckIterator &, QVariant &)>;
-    struct SerializationHandlers {
-        Serializer serializer;
-        Deserializer deserializer;
-        WireTypes type;
-    };
-    using SerializerRegistry = std::unordered_map<int/*metatypeid*/, SerializationHandlers>;
-    static SerializerRegistry serializers;
-
-    static void registerSerializers();
-
-    static QByteArray serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum);
-    static void deserializeProperty(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);
-
-    static QByteArray serializeObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);
-    static void deserializeObject(QObject *object, SelfcheckIterator &it, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);
-
-    static QByteArray serializeListObject(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject, int fieldIndex);
-
-    static QByteArray serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex);
-    static void deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it);
-
-    static QByteArray serializeObjectCommon(const QObject *object, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);//TODO make protected
-    static void deserializeObjectCommon(QObject *object, const QByteArray &array, const std::unordered_map<int, int> &propertyOrdering, const QMetaObject &metaObject);//TODO make protected
-    //###########################################################################
-    //                           Serialization helpers
-    //###########################################################################
-    //------------------------Serialize lists of any type------------------------
-    template<typename V,
-             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray serializeListType(const QList<QSharedPointer<V>> &listValue, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
-
-        if (listValue.count() <= 0) {
-            outFieldIndex = NotUsedFieldIndex;
-            return QByteArray();
-        }
-
-        QByteArray serializedList;
-        for (auto &value : listValue) {
-            if (!value) {
-                qProtoWarning() << "Null pointer in list";
-                continue;
-            }
-            serializedList.append(serializeListObject(value.data(), V::propertyOrdering, V::staticMetaObject, outFieldIndex));
-        }
-
-        outFieldIndex = NotUsedFieldIndex;
-
-        return serializedList;
-    }
-
-    //-------------------------Serialize maps of any type------------------------
-    template<typename K, typename V,
-             typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray serializeMap(const QVariant &value, int &outFieldIndex) {
-        QMap<K,V> mapValue = value.value<QMap<K,V>>();
-        using ItType = typename QMap<K,V>::const_iterator;
-        QByteArray mapResult;
-
-        for ( ItType it = mapValue.constBegin(); it != mapValue.constEnd(); it++) {
-            mapResult.append(serializeMapPair(QVariant::fromValue<K>(it.key()), QVariant::fromValue<V>(it.value()), outFieldIndex));
-        }
-        outFieldIndex = NotUsedFieldIndex;
-        return mapResult;
-    }
-
-    template<typename K, typename V,
-             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static QByteArray serializeMap(const QVariant &value, int &outFieldIndex) {
-        QMap<K, QSharedPointer<V>> mapValue = value.value<QMap<K, QSharedPointer<V>>>();
-        using ItType = typename QMap<K, QSharedPointer<V>>::const_iterator;
-        QByteArray mapResult;
-
-        for ( ItType it = mapValue.constBegin(); it != mapValue.constEnd(); it++) {
-            if (it.value().isNull()) {
-                qProtoWarning() << __func__ << "Trying to serialize map value that contains nullptr";
-                continue;
-            }
-            mapResult.append(serializeMapPair(QVariant::fromValue<K>(it.key()), QVariant::fromValue<V *>(it.value().data()), outFieldIndex));
-        }
-        outFieldIndex = NotUsedFieldIndex;
-        return mapResult;
-    }
-
-    //###########################################################################
-    //                           Deserialization helpers
-    //###########################################################################
-    //-----------------------Deserialize lists of any type-----------------------
-    template <typename V,
-              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        QVariant newValue;
-        QList<QSharedPointer<V>> list = previous.value<QList<QSharedPointer<V>>>();
-        serializers.at(qMetaTypeId<V *>()).deserializer(it, newValue);//Throws exception if not found
-        list.append(QSharedPointer<V>(newValue.value<V *>()));
-        previous.setValue(list);
-    }
-
-    //-----------------------Deserialize maps of any type------------------------
-    template <typename K, typename V,
-              typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
-    static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        QMap<K, V> out = previous.value<QMap<K, V>>();
-        QVariant key = QVariant::fromValue<K>(K());
-        QVariant value = QVariant::fromValue<V>(V());
-
-        deserializeMapPair(key, value, it);
-        out[key.value<K>()] = value.value<V>();
-        previous = QVariant::fromValue<QMap<K, V>>(out);
-    }
-
-    template <typename K, typename V,
-              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        auto out = previous.value<QMap<K, QSharedPointer<V>>>();
-        QVariant key = QVariant::fromValue<K>(K());
-        QVariant value = QVariant::fromValue<V *>(nullptr);
-
-        deserializeMapPair(key, value, it);
-        out[key.value<K>()] = QSharedPointer<V>(value.value<V *>());
-        previous = QVariant::fromValue<QMap<K, QSharedPointer<V>>>(out);
-    }
-
-    template <typename T,
-              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static T *deserializeMapHelper(SelfcheckIterator &it) {
-        auto serializer = serializers.at(qMetaTypeId<T *>());//Throws exception if not found
-        QVariant value;
-        serializer.deserializer(it, value);
-        return value.value<T *>();
-    }
-
-    template <typename T,
-              typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
-    static T deserializeMapHelper(SelfcheckIterator &it) {
-        auto serializer = serializers.at(qMetaTypeId<T>());//Throws exception if not found
-        QVariant value;
-        serializer.deserializer(it, value);
-        return value.value<T>();
-    }
-
-    static void deserializeMapField(QVariant &value, SelfcheckIterator &it) {
-        auto serializer = serializers.at(value.userType());//Throws exception if not found
-        serializer.deserializer(it, value);
-    }
-
-    //-----------------------Functions to work with objects------------------------
-    template<typename T>
-    static void registerSerializers() {
-        ProtobufObjectPrivate::serializers[qMetaTypeId<T *>()] = {ProtobufObjectPrivate::Serializer(serializeComplexType<T>),
-                ProtobufObjectPrivate::Deserializer(deserializeComplexType<T>), LengthDelimited};
-        ProtobufObjectPrivate::serializers[qMetaTypeId<QList<QSharedPointer<T>>>()] = {ProtobufObjectPrivate::Serializer(serializeComplexListType<T>),
-                ProtobufObjectPrivate::Deserializer(deserializeComplexListType<T>), LengthDelimited};
-    }
-
-    template<typename K, typename V,
-             typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
-    static void registerMap() {
-        ProtobufObjectPrivate::serializers[qMetaTypeId<QMap<K, V>>()] = {qtprotobuf::ProtobufObjectPrivate::serializeMap<K, V>,
-        qtprotobuf::ProtobufObjectPrivate::deserializeMap<K, V>, LengthDelimited};
-    }
-
-    template<typename K, typename V,
-             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
-    static void registerMap() {
-        ProtobufObjectPrivate::serializers[qMetaTypeId<QMap<K, QSharedPointer<V>>>()] = {qtprotobuf::ProtobufObjectPrivate::serializeMap<K, V>,
-        qtprotobuf::ProtobufObjectPrivate::deserializeMap<K, V>, LengthDelimited};
-    }
-
-    template <typename T,
-              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static QByteArray serializeComplexType(const QVariant &value, int &/*outFieldIndex*/) {
-        return ProtobufObjectPrivate::serializeObject(value.value<T *>(), T::propertyOrdering, T::staticMetaObject);
-    }
-
-    template <typename T,
-              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static void deserializeComplexType(SelfcheckIterator &it, QVariant &to) {
-        T *value = new T;
-        deserializeObject(value, it, T::propertyOrdering, T::staticMetaObject);
-        to = QVariant::fromValue<T *>(value);
-    }
-
-    template <typename T,
-              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static QByteArray serializeComplexListType(const QVariant &listValue, int &outFieldIndex) {
-        QList<QSharedPointer<T>> list = listValue.value<QList<QSharedPointer<T>>>();
-        return ProtobufObjectPrivate::serializeListType(list, outFieldIndex);
-    }
-
-    template <typename T,
-              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
-    static void deserializeComplexListType(SelfcheckIterator &it, QVariant &previous) {
-        ProtobufObjectPrivate::deserializeList<T>(it, previous);
-    }
-
-    /**
-    * @brief Serialization of a registered qtproto message object into byte-array
-    *
-    *
-    * @param[in] object Pointer to QObject containing message to be serialized
-    * @result serialized message bytes
-    */
-    template<typename T>
-    static QByteArray serializeObject(const QObject *object) {
-        qProtoDebug() << T::staticMetaObject.className() << "serialize";
-        return serializeObjectCommon(object, T::propertyOrdering, T::staticMetaObject);
-    }
-
-    /**
-    * @brief Deserialization of a byte-array into a registered qtproto message object
-    *
-    * @details Properties in a message are identified via ProtobufObjectPrivate::decodeHeader.
-    *          Bytes corresponding to unexpected properties are skipped without any exception
-    *
-    * @param[out] object Pointer to memory where result of deserialization should be injected
-    * @param[in] array Bytes with serialized message
-    */
-    template<typename T>
-    static void deserializeObject(QObject *object, const QByteArray &array) {
-        qProtoDebug() << T::staticMetaObject.className() << "deserialize";
-        deserializeObjectCommon(object, array, T::propertyOrdering, T::staticMetaObject);
-    }
-
-    //###########################################################################
-    //                                Qml support
-    //###########################################################################
-
-    template<typename T>
-    static void qmllistpropertyAppend(QQmlListProperty<T> *p, T *v) {
-        QQmlEngine::setObjectOwnership(v, QQmlEngine::CppOwnership);
-        reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->append(QSharedPointer<T>(v));
-    }
-
-    template<typename T>
-    static int qmllistpropertyCount(QQmlListProperty<T> *p) {
-        return reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->count();
-    }
-
-    template<typename T>
-    static T * qmllistpropertyAt(QQmlListProperty<T> *p, int index) {
-        return reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->at(index).data();
-    }
-
-    template<typename T>
-    static void qmllistpropertyReset(QQmlListProperty<T> *p){
-        reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->clear();
-    }
-
-    template<typename T>
-    static QQmlListProperty<T> constructQmlListProperty(QObject *p, QList<QSharedPointer<T>> *data)
-    {
-        return QQmlListProperty<T>(p, data, qmllistpropertyAppend<T>, qmllistpropertyCount<T>,
-                                   qmllistpropertyAt<T>, qmllistpropertyReset<T>);
-    }
-};
-
-}

+ 220 - 0
src/protobuf/qprotobufserializer.cpp

@@ -0,0 +1,220 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "qabstractprotobufserializer.h"
+#include "qprotobufserializer_p.h"
+#include "qprotobufserializerregistry.h"
+
+#include <QMetaProperty>
+
+using namespace qtprotobuf;
+
+QProtobufSerializer::QProtobufSerializer()
+{
+    QProtobufSerializerPrivate::wrapSerializer<float>(this, QProtobufSerializerPrivate::serializeBasic<float>, QProtobufSerializerPrivate::deserializeBasic<float>, Fixed32);
+    QProtobufSerializerPrivate::wrapSerializer<double>(this, QProtobufSerializerPrivate::serializeBasic<double>, QProtobufSerializerPrivate::deserializeBasic<double>, Fixed64);
+    QProtobufSerializerPrivate::wrapSerializer<int32>(this, QProtobufSerializerPrivate::serializeBasic<int32>, QProtobufSerializerPrivate::deserializeBasic<int32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<int64>(this, QProtobufSerializerPrivate::serializeBasic<int64>, QProtobufSerializerPrivate::deserializeBasic<int64>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<uint32>(this, QProtobufSerializerPrivate::serializeBasic<uint32>, QProtobufSerializerPrivate::deserializeBasic<uint32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<uint64>(this, QProtobufSerializerPrivate::serializeBasic<uint64>, QProtobufSerializerPrivate::deserializeBasic<uint64>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<sint32>(this, QProtobufSerializerPrivate::serializeBasic<sint32>, QProtobufSerializerPrivate::deserializeBasic<sint32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<sint64>(this, QProtobufSerializerPrivate::serializeBasic<sint64>, QProtobufSerializerPrivate::deserializeBasic<sint64>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<fixed32>(this, QProtobufSerializerPrivate::serializeBasic<fixed32>, QProtobufSerializerPrivate::deserializeBasic<fixed32>, Fixed32);
+    QProtobufSerializerPrivate::wrapSerializer<fixed64>(this, QProtobufSerializerPrivate::serializeBasic<fixed64>, QProtobufSerializerPrivate::deserializeBasic<fixed64>, Fixed64);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed32>(this, QProtobufSerializerPrivate::serializeBasic<sfixed32>, QProtobufSerializerPrivate::deserializeBasic<sfixed32>, Fixed32);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed64>(this, QProtobufSerializerPrivate::serializeBasic<sfixed64>, QProtobufSerializerPrivate::deserializeBasic<sfixed64>, Fixed64);
+    QProtobufSerializerPrivate::wrapSerializer<bool>(this, QProtobufSerializerPrivate::serializeBasic<uint32>, QProtobufSerializerPrivate::deserializeBasic<uint32>, Varint);
+    QProtobufSerializerPrivate::wrapSerializer<QString>(this, QProtobufSerializerPrivate::serializeBasic<QString>, QProtobufSerializerPrivate::deserializeBasic<QString>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<QByteArray>(this, QProtobufSerializerPrivate::serializeBasic<QByteArray>, QProtobufSerializerPrivate::deserializeBasic<QByteArray>, LengthDelimited);
+
+    QProtobufSerializerPrivate::wrapSerializer<FloatList>(this, QProtobufSerializerPrivate::serializeListType<float>, QProtobufSerializerPrivate::deserializeList<float>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<DoubleList>(this, QProtobufSerializerPrivate::serializeListType<double>, QProtobufSerializerPrivate::deserializeList<double>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<fixed32List>(this, QProtobufSerializerPrivate::serializeListType<fixed32>, QProtobufSerializerPrivate::deserializeList<fixed32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<fixed64List>(this, QProtobufSerializerPrivate::serializeListType<fixed64>, QProtobufSerializerPrivate::deserializeList<fixed64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed32List>(this, QProtobufSerializerPrivate::serializeListType<sfixed32>, QProtobufSerializerPrivate::deserializeList<sfixed32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sfixed64List>(this, QProtobufSerializerPrivate::serializeListType<sfixed64>, QProtobufSerializerPrivate::deserializeList<sfixed64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<int32List>(this, QProtobufSerializerPrivate::serializeListType<int32>, QProtobufSerializerPrivate::deserializeList<int32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<int64List>(this, QProtobufSerializerPrivate::serializeListType<int64>, QProtobufSerializerPrivate::deserializeList<int64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sint32List>(this, QProtobufSerializerPrivate::serializeListType<sint32>, QProtobufSerializerPrivate::deserializeList<sint32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<sint64List>(this, QProtobufSerializerPrivate::serializeListType<sint64>, QProtobufSerializerPrivate::deserializeList<sint64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<uint32List>(this, QProtobufSerializerPrivate::serializeListType<uint32>, QProtobufSerializerPrivate::deserializeList<uint32>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<uint64List>(this, QProtobufSerializerPrivate::serializeListType<uint64>, QProtobufSerializerPrivate::deserializeList<uint64>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<QStringList>(this, QProtobufSerializerPrivate::serializeListType<QString>, QProtobufSerializerPrivate::deserializeList<QString>, LengthDelimited);
+    QProtobufSerializerPrivate::wrapSerializer<QByteArrayList>(this, QProtobufSerializerPrivate::serializeListType<QByteArray>, QProtobufSerializerPrivate::deserializeList<QByteArray>, LengthDelimited);
+
+}
+
+QByteArray QProtobufSerializer::serializeProperty(const QVariant &propertyValue, int fieldIndex, bool isEnum)
+{
+    qProtoDebug() << __func__ << "propertyValue" << propertyValue << "fieldIndex" << fieldIndex
+                  << static_cast<QMetaType::Type>(propertyValue.type());
+
+    QByteArray result;
+    WireTypes type = UnknownWireType;
+
+    int userType = propertyValue.userType();
+    if (isEnum) {
+        userType = qMetaTypeId<int64>();
+    }
+
+    auto handler = QProtobufSerializerRegistry::handler(userType);
+    type = handler.type;
+    result.append(handler.serializer(propertyValue, fieldIndex));
+
+    if (fieldIndex != NotUsedFieldIndex
+            && type != UnknownWireType) {
+        result.prepend(QProtobufSerializerPrivate::encodeHeader(fieldIndex, type));
+    }
+    return result;
+}
+
+void QProtobufSerializer::deserializeProperty(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject)
+{
+    //Each iteration we expect iterator is setup to beginning of next chunk
+    int fieldNumber = NotUsedFieldIndex;
+    WireTypes wireType = UnknownWireType;
+    if (!QProtobufSerializerPrivate::decodeHeader(it, fieldNumber, wireType)) {
+        qProtoCritical() << "Message received doesn't contains valid header byte. "
+                            "Trying next, but seems stream is broken" << QString::number((*it), 16);
+        throw std::invalid_argument("Message received doesn't contains valid header byte. "
+                              "Seems stream is broken");
+    }
+
+    auto propertyNumberIt = propertyOrdering.find(fieldNumber);
+    if (propertyNumberIt == std::end(propertyOrdering)) {
+        auto bytesCount = QProtobufSerializerPrivate::skipSerializedFieldBytes(it, wireType);
+        qProtoWarning() << "Message received contains unexpected/optional field. WireType:" << wireType
+                        << ", field number: " << fieldNumber << "Skipped:" << (bytesCount + 1) << "bytes";
+        return;
+    }
+
+    int propertyIndex = propertyNumberIt->second;
+    QMetaProperty metaProperty = metaObject.property(propertyIndex);
+
+    qProtoDebug() << __func__ << " wireType: " << wireType << " metaProperty: " << metaProperty.typeName()
+                  << "currentByte:" << QString::number((*it), 16);
+
+    QVariant newPropertyValue;
+    if (metaProperty.isEnumType()) {
+        newPropertyValue = QVariant::fromValue(int32_t(QProtobufSerializerPrivate::deserializeBasic<int64>(it).value<int64>()._t));
+    } else {
+        newPropertyValue = metaProperty.read(object);
+        int userType = metaProperty.userType();
+        auto deserializer = QProtobufSerializerRegistry::handler(userType).deserializer;
+        deserializer(it, newPropertyValue);
+    }
+    metaProperty.write(object, newPropertyValue);
+}
+
+QByteArray QProtobufSerializer::serializeObject(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject)
+{
+    return QProtobufSerializerPrivate::prependLengthDelimitedSize(serializeObjectCommon(object, propertyOrdering, metaObject));
+}
+
+void QProtobufSerializer::deserializeObject(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject)
+{
+    QByteArray array = QProtobufSerializerPrivate::deserializeLengthDelimited(it);
+    deserializeObjectCommon(object, array, propertyOrdering, metaObject);
+}
+
+QByteArray QProtobufSerializer::serializeListObject(const QObject *object, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject, int fieldIndex)
+{
+    QByteArray result = QProtobufSerializerPrivate::encodeHeader(fieldIndex, LengthDelimited);
+    result.append(serializeObject(object, propertyOrdering, metaObject));
+    return result;
+}
+
+void QProtobufSerializer::deserializeListObject(QObject *object, SelfcheckIterator &it, const QProtobufPropertyOrdering &propertyOrdering, const QMetaObject &metaObject)
+{
+    deserializeObject(object, it, propertyOrdering, metaObject);
+}
+
+QByteArray QProtobufSerializer::serializeMapPair(const QVariant &key, const QVariant &value, int fieldIndex)
+{
+    QByteArray result = QProtobufSerializerPrivate::encodeHeader(fieldIndex, LengthDelimited);
+    result.append(QProtobufSerializerPrivate::prependLengthDelimitedSize(serializeProperty(key, 1, false) + serializeProperty(value, 2, false)));
+    return result;
+}
+
+void QProtobufSerializer::deserializeMapPair(QVariant &key, QVariant &value, SelfcheckIterator &it)
+{
+    int mapIndex = 0;
+    WireTypes type = WireTypes::UnknownWireType;
+    unsigned int count = QProtobufSerializerPrivate::deserializeVarintCommon<uint32>(it);
+    qProtoDebug() << __func__ << "count:" << count;
+    SelfcheckIterator last = it + count;
+    while (it != last) {
+        QProtobufSerializerPrivate::decodeHeader(it, mapIndex, type);
+        if(mapIndex == 1) {
+            QProtobufSerializerPrivate::deserializeMapField(key, it);
+        } else {
+            QProtobufSerializerPrivate::deserializeMapField(value, it);
+        }
+    }
+}
+
+void QProtobufSerializerPrivate::skipVarint(SelfcheckIterator &it)
+{
+    while ((*it) & 0x80) {
+        ++it;
+    }
+    ++it;
+}
+
+void QProtobufSerializerPrivate::skipLengthDelimited(SelfcheckIterator &it)
+{
+    //Get length of lenght-delimited field
+    unsigned int length = QProtobufSerializerPrivate::deserializeBasic<uint32>(it).toUInt();
+    it += length;
+}
+
+int QProtobufSerializerPrivate::skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type)
+{
+    const auto initialIt = QByteArray::const_iterator(it);
+    switch (type) {
+    case WireTypes::Varint:
+        skipVarint(it);
+        break;
+    case WireTypes::Fixed32:
+        it += sizeof(decltype(fixed32::_t));
+        break;
+    case WireTypes::Fixed64:
+        it += sizeof(decltype(fixed64::_t));
+        break;
+    case WireTypes::LengthDelimited:
+        skipLengthDelimited(it);
+        break;
+    case WireTypes::UnknownWireType:
+    default:
+        throw std::invalid_argument("Cannot skip due to undefined length of the redundant field.");
+    }
+
+    return std::distance(initialIt, QByteArray::const_iterator(it));
+}
+
+void QProtobufSerializerPrivate::deserializeMapField(QVariant &value, SelfcheckIterator &it)
+{
+    QProtobufSerializerRegistry::handler(value.userType()).deserializer(it, value);
+}

+ 267 - 303
src/protobuf/qprotobufserializer_p.h

@@ -26,8 +26,10 @@
 #include <QString>
 #include <QByteArray>
 
+#include "selfcheckiterator.h"
 #include "qtprotobuftypes.h"
-#include "qprotobufobject_p.h"
+#include "qtprotobuflogging.h"
+#include "qabstractprotobufserializer.h"
 
 namespace qtprotobuf {
 
@@ -35,7 +37,7 @@ class QProtobufSerializerPrivate {
     QProtobufSerializerPrivate() = delete;
     ~QProtobufSerializerPrivate() = delete;
     Q_DISABLE_COPY(QProtobufSerializerPrivate)
-    QProtobufSerializerPrivate(ProtobufObjectPrivate &&) = delete;
+    QProtobufSerializerPrivate(QProtobufSerializerPrivate &&) = delete;
     QProtobufSerializerPrivate &operator =(QProtobufSerializerPrivate &&) = delete;
 public:
     template <typename V,
@@ -84,34 +86,34 @@ public:
 
     template <typename T,
               typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
-    static void wrapSerializer(const std::function<QByteArray(const T &, int &)> &s, const std::function<QVariant(SelfcheckIterator &)> &d, WireTypes type)
+    static void wrapSerializer(QProtobufSerializer *owner,const std::function<QByteArray(const T &, int &)> &s, const std::function<QVariant(SelfcheckIterator &)> &d, WireTypes type)
     {
-        ProtobufObjectPrivate::serializers[qMetaTypeId<T>()] = {
-            [s](const QVariant &value, int &fieldIndex) {
-                return s(value.value<T>(), fieldIndex);
-            },
-            [d](SelfcheckIterator &it, QVariant & value){
-                value = d(it);
-            },
-            type
-        };
-    }
+        owner->serializers[qMetaTypeId<T>()] = {
+                [s](const QVariant &value, int &fieldIndex) {
+            return s(value.value<T>(), fieldIndex);
+        },
+        [d](SelfcheckIterator &it, QVariant & value){
+            value = d(it);
+        },
+        type
+    };
+}
 
-    template <typename T,
-              typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
-    static void wrapSerializer(const std::function<QByteArray(const T &, int &)> &s, const std::function<void(SelfcheckIterator &it, QVariant & value)> &d, WireTypes type)
-    {
-        ProtobufObjectPrivate::serializers[qMetaTypeId<T>()] = {
+template <typename T,
+typename std::enable_if_t<!std::is_base_of<QObject, T>::value, int> = 0>
+static void wrapSerializer(QProtobufSerializer *owner, const std::function<QByteArray(const T &, int &)> &s, const std::function<void(SelfcheckIterator &it, QVariant & value)> &d, WireTypes type)
+{
+    owner->serializers[qMetaTypeId<T>()] = {
             [s](const QVariant &value, int &fieldIndex) {
-                return s(value.value<T>(), fieldIndex);
-            },
-            d,
-            type
-        };
-    }
+        return s(value.value<T>(), fieldIndex);
+    },
+    d,
+    type
+};
+}
 
-    //----------------Serialize basic integral and floating point----------------
-    /**
+//----------------Serialize basic integral and floating point----------------
+/**
      * @brief Serialization of fixed-length primitive types
      *
      * Natural layout of bits is used: value is encoded in a byte array same way as it is located in memory
@@ -120,18 +122,18 @@ public:
      * @param[out] outFieldIndex Index of the value in parent structure (ignored)
      * @return Byte array with value encoded
      */
-    template <typename V,
-              typename std::enable_if_t<std::is_floating_point<V>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
-        qProtoDebug() << __func__ << "value" << value;
-
-        //Reserve required number of bytes
-        QByteArray result(sizeof(V), '\0');
-        *reinterpret_cast<V *>(result.data()) = value;
-        return result;
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_floating_point<V>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
+    qProtoDebug() << __func__ << "value" << value;
+
+    //Reserve required number of bytes
+    QByteArray result(sizeof(V), '\0');
+    *reinterpret_cast<V *>(result.data()) = value;
+    return result;
+}
 
-    /**
+/**
      * @brief Serialization of fixed length integral types
      *
      * @details Natural layout of bits is employed
@@ -140,21 +142,21 @@ public:
      * @param[out] outFieldIndex Index of the value in parent structure (ignored)
      * @return Byte array with value encoded
      */
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, fixed32>::value
-                                        || std::is_same<V, fixed64>::value
-                                        || std::is_same<V, sfixed32>::value
-                                        || std::is_same<V, sfixed64>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
-        qProtoDebug() << __func__ << "value" << value;
-
-        //Reserve required number of bytes
-        QByteArray result(sizeof(V), '\0');
-        *reinterpret_cast<V *>(result.data()) = value;
-        return result;
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<V, fixed32>::value
+                                    || std::is_same<V, fixed64>::value
+                                    || std::is_same<V, sfixed32>::value
+                                    || std::is_same<V, sfixed64>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
+    qProtoDebug() << __func__ << "value" << value;
+
+    //Reserve required number of bytes
+    QByteArray result(sizeof(V), '\0');
+    *reinterpret_cast<V *>(result.data()) = value;
+    return result;
+}
 
-    /**
+/**
      *@brief Serialization of signed integral types
      *
      * Use <a href="https://developers.google.com/protocol-buffers/docs/encoding">ZigZag encoding</a> first,
@@ -165,30 +167,30 @@ public:
      * @param[out] outFieldIndex Index of the value in parent structure
      * @return Byte array with value encoded
      */
-    template <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_signed<V>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "value" << value;
-        using UV = typename std::make_unsigned<V>::type;
-        UV uValue = 0;
-
-        //Use ZigZag convertion first and apply unsigned variant next
-        V zigZagValue = (value << 1) ^ (value >> (sizeof(UV) * 8 - 1));
-        uValue = static_cast<UV>(zigZagValue);
-        return serializeBasic(uValue, outFieldIndex);
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_integral<V>::value
+                                    && std::is_signed<V>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
+    qProtoDebug() << __func__ << "value" << value;
+    using UV = typename std::make_unsigned<V>::type;
+    UV uValue = 0;
+
+    //Use ZigZag convertion first and apply unsigned variant next
+    V zigZagValue = (value << 1) ^ (value >> (sizeof(UV) * 8 - 1));
+    uValue = static_cast<UV>(zigZagValue);
+    return serializeBasic(uValue, outFieldIndex);
+}
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, int32>::value
-                                        || std::is_same<V, int64>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "value" << value;
-        using UV = typename std::make_unsigned<V>::type;
-        return serializeBasic(static_cast<UV>(value), outFieldIndex);
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<V, int32>::value
+                                    || std::is_same<V, int64>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
+    qProtoDebug() << __func__ << "value" << value;
+    using UV = typename std::make_unsigned<V>::type;
+    return serializeBasic(static_cast<UV>(value), outFieldIndex);
+}
 
-    /**
+/**
     *@brief Serialization of unsigned integral types
     *
     * Use <a href="https://developers.google.com/protocol-buffers/docs/encoding">Varint encoding</a>:
@@ -199,239 +201,240 @@ public:
     * @param[out] outFieldIndex Index of the value in parent structure
     * @return Byte array with value encoded
     */
-    template <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_unsigned<V>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "value" << value;
-        V varint = value;
-        QByteArray result;
-
-        while (varint != 0) {
-            //Put 7 bits to result buffer and mark as "not last" (0b10000000)
-            result.append((varint & 0b01111111) | 0b10000000);
-            //Divide values to chunks of 7 bits and move to next chunk
-            varint >>= 7;
-        }
-
-        // Invalidate field index in case if serialized result is empty
-        // NOTE: the field will not be sent if its index is equal to NotUsedFieldIndex
-        if (result.size() == 0) {
-            outFieldIndex = NotUsedFieldIndex;
-        } else {
-            //Mark last chunk as last by clearing last bit
-            result.data()[result.size() - 1] &= ~0b10000000;
-        }
-        return result;
-    }
-
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
-        return serializeLengthDelimited(value.toUtf8());
+template <typename V,
+          typename std::enable_if_t<std::is_integral<V>::value
+                                    && std::is_unsigned<V>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &outFieldIndex) {
+    qProtoDebug() << __func__ << "value" << value;
+    V varint = value;
+    QByteArray result;
+
+    while (varint != 0) {
+        //Put 7 bits to result buffer and mark as "not last" (0b10000000)
+        result.append((varint & 0b01111111) | 0b10000000);
+        //Divide values to chunks of 7 bits and move to next chunk
+        varint >>= 7;
     }
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
-    static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
-        return serializeLengthDelimited(value);
+    // Invalidate field index in case if serialized result is empty
+    // NOTE: the field will not be sent if its index is equal to NotUsedFieldIndex
+    if (result.size() == 0) {
+        outFieldIndex = NotUsedFieldIndex;
+    } else {
+        //Mark last chunk as last by clearing last bit
+        result.data()[result.size() - 1] &= ~0b10000000;
     }
+    return result;
+}
 
-    //---------------Deserialize basic integral and floating point---------------
-    template <typename V,
-              typename std::enable_if_t<std::is_floating_point<V>::value
-                                        || std::is_same<V, fixed32>::value
-                                        || std::is_same<V, fixed64>::value
-                                        || std::is_same<V, sfixed32>::value
-                                        || std::is_same<V, sfixed64>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+template <typename V,
+          typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
+    return serializeLengthDelimited(value.toUtf8());
+}
 
-        QVariant newPropertyValue(QVariant::fromValue(*(V *)((QByteArray::const_iterator&)it)));
-        it += sizeof(V);
-        return newPropertyValue;
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
+static QByteArray serializeBasic(const V &value, int &/*outFieldIndex*/) {
+    return serializeLengthDelimited(value);
+}
 
-    template <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_unsigned<V>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+//---------------Deserialize basic integral and floating point---------------
+template <typename V,
+          typename std::enable_if_t<std::is_floating_point<V>::value
+                                    || std::is_same<V, fixed32>::value
+                                    || std::is_same<V, fixed64>::value
+                                    || std::is_same<V, sfixed32>::value
+                                    || std::is_same<V, sfixed64>::value, int> = 0>
+static QVariant deserializeBasic(SelfcheckIterator &it) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+    QVariant newPropertyValue(QVariant::fromValue(*(V *)((QByteArray::const_iterator&)it)));
+    it += sizeof(V);
+    return newPropertyValue;
+}
 
-        return QVariant::fromValue(deserializeVarintCommon<V>(it));
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_integral<V>::value
+                                    && std::is_unsigned<V>::value, int> = 0>
+static QVariant deserializeBasic(SelfcheckIterator &it) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
-    template <typename V,
-              typename std::enable_if_t<std::is_integral<V>::value
-                                        && std::is_signed<V>::value,int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-        using  UV = typename std::make_unsigned<V>::type;
-        UV unsignedValue = deserializeVarintCommon<UV>(it);
-        V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1));
-        return QVariant::fromValue<V>(value);
-    }
+    return QVariant::fromValue(deserializeVarintCommon<V>(it));
+}
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<int32, V>::value
-                                        || std::is_same<int64, V>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-        using  UV = typename std::make_unsigned<V>::type;
-        UV unsignedValue = deserializeVarintCommon<UV>(it);
-        V value = static_cast<V>(unsignedValue);
-        return QVariant::fromValue(value);
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_integral<V>::value
+                                    && std::is_signed<V>::value,int> = 0>
+static QVariant deserializeBasic(SelfcheckIterator &it) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+    using  UV = typename std::make_unsigned<V>::type;
+    UV unsignedValue = deserializeVarintCommon<UV>(it);
+    V value = (unsignedValue >> 1) ^ (-1 * (unsignedValue & 1));
+    return QVariant::fromValue<V>(value);
+}
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<QByteArray, V>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        return QVariant::fromValue(deserializeLengthDelimited(it));
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<int32, V>::value
+                                    || std::is_same<int64, V>::value, int> = 0>
+static QVariant deserializeBasic(SelfcheckIterator &it) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+    using  UV = typename std::make_unsigned<V>::type;
+    UV unsignedValue = deserializeVarintCommon<UV>(it);
+    V value = static_cast<V>(unsignedValue);
+    return QVariant::fromValue(value);
+}
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<QString, V>::value, int> = 0>
-    static QVariant deserializeBasic(SelfcheckIterator &it) {
-        return QVariant::fromValue(QString::fromUtf8(deserializeLengthDelimited(it)));
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<QByteArray, V>::value, int> = 0>
+static QVariant deserializeBasic(SelfcheckIterator &it) {
+    return QVariant::fromValue(deserializeLengthDelimited(it));
+}
 
-    template<typename V,
-             typename std::enable_if_t<!(std::is_same<V, QString>::value
-                                       || std::is_same<V, QByteArray>::value
-                                       || std::is_base_of<QObject, V>::value), int> = 0>
-    static QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
+template <typename V,
+          typename std::enable_if_t<std::is_same<QString, V>::value, int> = 0>
+static QVariant deserializeBasic(SelfcheckIterator &it) {
+    return QVariant::fromValue(QString::fromUtf8(deserializeLengthDelimited(it)));
+}
 
-        if (listValue.count() <= 0) {
-            outFieldIndex = NotUsedFieldIndex;
-            return QByteArray();
-        }
+template<typename V,
+         typename std::enable_if_t<!(std::is_same<V, QString>::value
+                                   || std::is_same<V, QByteArray>::value
+                                   || std::is_base_of<QObject, V>::value), int> = 0>
+static QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
+    qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
 
-        int empty = NotUsedFieldIndex;
-        QByteArray serializedList;
-        for (auto &value : listValue) {
-            serializedList.append(serializeBasic<V>(value, empty));
-        }
-        //If internal field type is not LengthDelimited, exact amount of fields to be specified
-        serializedList = prependLengthDelimitedSize(serializedList);
-        return serializedList;
+    if (listValue.count() <= 0) {
+        outFieldIndex = NotUsedFieldIndex;
+        return QByteArray();
     }
 
-    template<typename V,
-             typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
-    static QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
-
-        if (listValue.count() <= 0) {
-            outFieldIndex = NotUsedFieldIndex;
-            return QByteArray();
-        }
+    int empty = NotUsedFieldIndex;
+    QByteArray serializedList;
+    for (auto &value : listValue) {
+        serializedList.append(serializeBasic<V>(value, empty));
+    }
+    //If internal field type is not LengthDelimited, exact amount of fields to be specified
+    serializedList = prependLengthDelimitedSize(serializedList);
+    return serializedList;
+}
 
-        QByteArray serializedList;
-        for (auto &value : listValue) {
-            serializedList.append(QProtobufSerializerPrivate::encodeHeader(outFieldIndex, LengthDelimited));
-            serializedList.append(serializeLengthDelimited(value.toUtf8()));
-        }
+template<typename V,
+         typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
+static QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
+    qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
 
+    if (listValue.count() <= 0) {
         outFieldIndex = NotUsedFieldIndex;
-        return serializedList;
+        return QByteArray();
     }
 
-    template<typename V,
-             typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
-    static QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
-        qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
+    QByteArray serializedList;
+    for (auto &value : listValue) {
+        serializedList.append(QProtobufSerializerPrivate::encodeHeader(outFieldIndex, LengthDelimited));
+        serializedList.append(serializeLengthDelimited(value.toUtf8()));
+    }
 
-        if (listValue.count() <= 0) {
-            outFieldIndex = NotUsedFieldIndex;
-            return QByteArray();
-        }
+    outFieldIndex = NotUsedFieldIndex;
+    return serializedList;
+}
 
-        QByteArray serializedList;
-        for (auto &value : listValue) {
-            serializedList.append(QProtobufSerializerPrivate::encodeHeader(outFieldIndex, LengthDelimited));
-            serializedList.append(serializeLengthDelimited(value));
-        }
+template<typename V,
+         typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
+static QByteArray serializeListType(const QList<V> &listValue, int &outFieldIndex) {
+    qProtoDebug() << __func__ << "listValue.count" << listValue.count() << "outFiledIndex" << outFieldIndex;
 
+    if (listValue.count() <= 0) {
         outFieldIndex = NotUsedFieldIndex;
-        return serializedList;
+        return QByteArray();
     }
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
-
-        QByteArrayList list = previous.value<QByteArrayList>();
-        list.append(deserializeLengthDelimited(it));
-        previous.setValue(list);
+    QByteArray serializedList;
+    for (auto &value : listValue) {
+        serializedList.append(QProtobufSerializerPrivate::encodeHeader(outFieldIndex, LengthDelimited));
+        serializedList.append(serializeLengthDelimited(value));
     }
 
-    template <typename V,
-              typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+    outFieldIndex = NotUsedFieldIndex;
+    return serializedList;
+}
 
-        QStringList list = previous.value<QStringList>();
-        QByteArray value = deserializeLengthDelimited(it);
-        list.append(QString::fromUtf8(value));
-        previous.setValue(list);
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<V, QByteArray>::value, int> = 0>
+static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
-    template <typename V,
-              typename std::enable_if_t<!(std::is_same<V, QString>::value
-                                        || std::is_same<V, QByteArray>::value
-                                        || std::is_base_of<QObject, V>::value), int> = 0>
-    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+    QByteArrayList list = previous.value<QByteArrayList>();
+    list.append(deserializeLengthDelimited(it));
+    previous.setValue(list);
+}
 
-        QList<V> out;
-        unsigned int count = deserializeVarintCommon<uint32>(it);
-        SelfcheckIterator lastVarint = it + count;
-        while (it != lastVarint) {
-            QVariant variant = deserializeBasic<V>(it);
-            out.append(variant.value<V>());
-        }
-        previous.setValue(out);
-    }
+template <typename V,
+          typename std::enable_if_t<std::is_same<V, QString>::value, int> = 0>
+static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
 
-    static QByteArray deserializeLengthDelimited(SelfcheckIterator &it) {
-        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+    QStringList list = previous.value<QStringList>();
+    QByteArray value = deserializeLengthDelimited(it);
+    list.append(QString::fromUtf8(value));
+    previous.setValue(list);
+}
 
-        unsigned int length = deserializeVarintCommon<uint32>(it);
-        QByteArray result((QByteArray::const_iterator&)it, length); //TODO it's possible to void buffeer copying by setupimg new "end of QByteArray";
-        it += length;
-        return result;
+template <typename V,
+          typename std::enable_if_t<!(std::is_same<V, QString>::value
+                                    || std::is_same<V, QByteArray>::value
+                                    || std::is_base_of<QObject, V>::value), int> = 0>
+static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+    QList<V> out;
+    unsigned int count = deserializeVarintCommon<uint32>(it);
+    SelfcheckIterator lastVarint = it + count;
+    while (it != lastVarint) {
+        QVariant variant = deserializeBasic<V>(it);
+        out.append(variant.value<V>());
     }
+    previous.setValue(out);
+}
 
-    static QByteArray serializeLengthDelimited(const QByteArray &data) {
-        qProtoDebug() << __func__ << "data.size" << data.size() << "data" << data.toHex();
-        QByteArray result(data);
-        //Varint serialize field size and apply result as starting point
-        result = prependLengthDelimitedSize(result);
-        return result;
-    }
+static QByteArray deserializeLengthDelimited(SelfcheckIterator &it) {
+    qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+    unsigned int length = deserializeVarintCommon<uint32>(it);
+    QByteArray result((QByteArray::const_iterator&)it, length); //TODO it's possible to void buffeer copying by setupimg new "end of QByteArray";
+    it += length;
+    return result;
+}
 
-    static bool decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType);
-    static QByteArray encodeHeader(int fieldIndex, WireTypes wireType);
+static QByteArray serializeLengthDelimited(const QByteArray &data) {
+    qProtoDebug() << __func__ << "data.size" << data.size() << "data" << data.toHex();
+    QByteArray result(data);
+    //Varint serialize field size and apply result as starting point
+    result = prependLengthDelimitedSize(result);
+    return result;
+}
+
+static bool decodeHeader(SelfcheckIterator &it, int &fieldIndex, WireTypes &wireType);
+static QByteArray encodeHeader(int fieldIndex, WireTypes wireType);
 
-    /**
+/**
      * @brief Gets length of a byte-array and prepends to it its serialized length value
      *      using the appropriate serialization algorithm
      *
      *
      * @param[in, out] serializedList Byte-array to be prepended
      */
-    static QByteArray prependLengthDelimitedSize(const QByteArray &data)
-    {
-        return serializeVarintCommon<uint32_t>(data.size()) + data;
-    }
+static QByteArray prependLengthDelimitedSize(const QByteArray &data)
+{
+    return serializeVarintCommon<uint32_t>(data.size()) + data;
+}
 
-    // this set of 3 methods is used to skip bytes corresponding to an unexpected property
-    // in a serialized message met while the message being deserialized
-    static void skipVarint(SelfcheckIterator &it);
-    static void skipLengthDelimited(SelfcheckIterator &it);
-    static int skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type);
+// this set of 3 methods is used to skip bytes corresponding to an unexpected property
+// in a serialized message met while the message being deserialized
+static void skipVarint(SelfcheckIterator &it);
+static void skipLengthDelimited(SelfcheckIterator &it);
+static int skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type);
+static void deserializeMapField(QVariant &value, SelfcheckIterator &it);
 };
 
 //###########################################################################
@@ -472,48 +475,9 @@ inline bool QProtobufSerializerPrivate::decodeHeader(SelfcheckIterator &it, int
 
     constexpr int maxFieldIndex = (1 << 29) - 1;
     return fieldIndex <= maxFieldIndex && fieldIndex > 0 && (wireType == Varint
-                                                  || wireType == Fixed64
-                                                  || wireType == Fixed32
-                                                  || wireType == LengthDelimited);
-}
-
-void QProtobufSerializerPrivate::skipVarint(SelfcheckIterator &it)
-{
-    while ((*it) & 0x80) {
-        ++it;
-    }
-    ++it;
-}
-
-void QProtobufSerializerPrivate::skipLengthDelimited(SelfcheckIterator &it)
-{
-    //Get length of lenght-delimited field
-    unsigned int length = QProtobufSerializerPrivate::deserializeBasic<uint32>(it).toUInt();
-    it += length;
-}
-
-int QProtobufSerializerPrivate::skipSerializedFieldBytes(SelfcheckIterator &it, WireTypes type)
-{
-    const auto initialIt = QByteArray::const_iterator(it);
-    switch (type) {
-    case WireTypes::Varint:
-        skipVarint(it);
-        break;
-    case WireTypes::Fixed32:
-        it += sizeof(decltype(fixed32::_t));
-        break;
-    case WireTypes::Fixed64:
-        it += sizeof(decltype(fixed64::_t));
-        break;
-    case WireTypes::LengthDelimited:
-        skipLengthDelimited(it);
-        break;
-    case WireTypes::UnknownWireType:
-    default:
-        throw std::invalid_argument("Cannot skip due to undefined length of the redundant field.");
-    }
-
-    return std::distance(initialIt, QByteArray::const_iterator(it));
+                                                             || wireType == Fixed64
+                                                             || wireType == Fixed32
+                                                             || wireType == LengthDelimited);
 }
 
 }

+ 49 - 0
src/protobuf/qprotobufserializerregistry.cpp

@@ -0,0 +1,49 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "qprotobufserializerregistry.h"
+
+using namespace qtprotobuf;
+
+QAbstractProtobufSerializer::SerializerRegistry QProtobufSerializerRegistry::serializers = {};
+std::unique_ptr<QAbstractProtobufSerializer> QProtobufSerializerRegistry::basicSerializer = std::make_unique<QProtobufSerializer>();
+QAbstractProtobufSerializer::SerializationHandlers QProtobufSerializerRegistry::empty;
+
+const QAbstractProtobufSerializer::SerializationHandlers &QProtobufSerializerRegistry::handler(int userType)
+{
+    auto it = serializers.find(userType);
+    if (it != serializers.end()) {
+        return it->second;
+    }
+
+    if (basicSerializer != nullptr) {
+        it = basicSerializer->serializers.find(userType);
+        if (it != basicSerializer->serializers.end()) {
+            return it->second;
+        }
+    }
+
+    return empty;
+}

+ 230 - 0
src/protobuf/qprotobufserializerregistry.h

@@ -0,0 +1,230 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QSharedPointer>
+
+#include <memory>
+#include <type_traits>
+
+#include "qtprotobuflogging.h"
+#include "selfcheckiterator.h"
+#include "qabstractprotobufserializer.h"
+
+#include "qtprotobuf_global.h"
+
+namespace qtprotobuf {
+
+class QTPROTOBUFSHARED_EXPORT QProtobufSerializerRegistry
+{
+    QProtobufSerializerRegistry() = delete;
+    ~QProtobufSerializerRegistry() = delete;
+    Q_DISABLE_COPY(QProtobufSerializerRegistry)
+    QProtobufSerializerRegistry(QProtobufSerializerRegistry &&) = delete;
+    QProtobufSerializerRegistry &operator =(QProtobufSerializerRegistry &&) = delete;
+    static QAbstractProtobufSerializer::SerializerRegistry serializers;
+
+    static std::unique_ptr<QAbstractProtobufSerializer> basicSerializer;
+    static QAbstractProtobufSerializer::SerializationHandlers empty;
+public:
+    static const QAbstractProtobufSerializer::SerializationHandlers &handler(int userType);
+    /**
+    * @brief Serialization of a registered qtproto message object into byte-array
+    *
+    *
+    * @param[in] object Pointer to QObject containing message to be serialized
+    * @result serialized message bytes
+    */
+    template<typename T>
+    static QByteArray serialize(const QObject *object) {
+        qProtoDebug() << T::staticMetaObject.className() << "serialize";
+        return basicSerializer->serializeObjectCommon(object, T::propertyOrdering, T::staticMetaObject);
+    }
+
+    /**
+    * @brief Deserialization of a byte-array into a registered qtproto message object
+    *
+    * @details Properties in a message are identified via ProtobufObjectPrivate::decodeHeader.
+    *          Bytes corresponding to unexpected properties are skipped without any exception
+    *
+    * @param[out] object Pointer to memory where result of deserialization should be injected
+    * @param[in] array Bytes with serialized message
+    */
+    template<typename T>
+    static void deserialize(QObject *object, const QByteArray &array) {
+        qProtoDebug() << T::staticMetaObject.className() << "deserialize";
+        basicSerializer->deserializeObjectCommon(object, array, T::propertyOrdering, T::staticMetaObject);
+    }
+
+    //----------------------Functions to work with QObjects------------------------
+    template<typename T>
+    static void registerSerializers() {
+        serializers[qMetaTypeId<T *>()] = { serializeComplexType<T>,
+                deserializeComplexType<T>, LengthDelimited };
+        serializers[qMetaTypeId<QList<QSharedPointer<T>>>()] = { serializeList<T>,
+                deserializeList<T>, LengthDelimited };
+    }
+
+    template<typename K, typename V,
+             typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
+    static void registerMap() {
+        serializers[qMetaTypeId<QMap<K, V>>()] = { serializeMap<K, V>,
+        deserializeMap<K, V>, LengthDelimited };
+    }
+
+    template<typename K, typename V,
+             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
+    static void registerMap() {
+        serializers[qMetaTypeId<QMap<K, QSharedPointer<V>>>()] = { serializeMap<K, V>,
+        deserializeMap<K, V>, LengthDelimited };
+    }
+
+private:
+    //###########################################################################
+    //                           Serialization helpers
+    //###########################################################################
+    template <typename T,
+              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
+    static QByteArray serializeComplexType(const QVariant &value, int &/*outFieldIndex*/) {
+        return basicSerializer->serializeObject(value.value<T *>(), T::propertyOrdering, T::staticMetaObject);
+    }
+
+    //------------------Serialize lists of QObject based classes-----------------
+    template<typename V,
+             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
+    static QByteArray serializeList(const QVariant &listValue, int &outFieldIndex) {
+        QList<QSharedPointer<V>> list = listValue.value<QList<QSharedPointer<V>>>();
+
+        qProtoDebug() << __func__ << "listValue.count" << list.count() << "outFiledIndex" << outFieldIndex;
+
+        if (list.count() <= 0) {
+            outFieldIndex = NotUsedFieldIndex;
+            return QByteArray();
+        }
+
+        QByteArray serializedList;
+        for (auto &value : list) {
+            if (!value) {
+                qProtoWarning() << "Null pointer in list";
+                continue;
+            }
+            serializedList.append(basicSerializer->serializeListObject(value.data(), V::propertyOrdering, V::staticMetaObject, outFieldIndex));
+        }
+
+        outFieldIndex = NotUsedFieldIndex;
+
+        return serializedList;
+    }
+
+    //-------------------------Serialize maps of any type------------------------
+    template<typename K, typename V,
+             typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
+    static QByteArray serializeMap(const QVariant &value, int &outFieldIndex) {
+        QMap<K,V> mapValue = value.value<QMap<K,V>>();
+        using ItType = typename QMap<K,V>::const_iterator;
+        QByteArray mapResult;
+
+        for ( ItType it = mapValue.constBegin(); it != mapValue.constEnd(); it++) {
+            mapResult.append(basicSerializer->serializeMapPair(QVariant::fromValue<K>(it.key()), QVariant::fromValue<V>(it.value()), outFieldIndex));
+        }
+        outFieldIndex = NotUsedFieldIndex;
+        return mapResult;
+    }
+
+    template<typename K, typename V,
+             typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
+    static QByteArray serializeMap(const QVariant &value, int &outFieldIndex) {
+        QMap<K, QSharedPointer<V>> mapValue = value.value<QMap<K, QSharedPointer<V>>>();
+        using ItType = typename QMap<K, QSharedPointer<V>>::const_iterator;
+        QByteArray mapResult;
+
+        for ( ItType it = mapValue.constBegin(); it != mapValue.constEnd(); it++) {
+            if (it.value().isNull()) {
+                qProtoWarning() << __func__ << "Trying to serialize map value that contains nullptr";
+                continue;
+            }
+            mapResult.append(basicSerializer->serializeMapPair(QVariant::fromValue<K>(it.key()), QVariant::fromValue<V *>(it.value().data()), outFieldIndex));
+        }
+        outFieldIndex = NotUsedFieldIndex;
+        return mapResult;
+    }
+
+    //###########################################################################
+    //                           Deserialization helpers
+    //###########################################################################
+    //-----------------Deserialize lists of QObject based classes----------------
+    template <typename V,
+              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
+    static void deserializeList(SelfcheckIterator &it, QVariant &previous) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        V *newValue = new V;
+        QList<QSharedPointer<V>> list = previous.value<QList<QSharedPointer<V>>>();
+        basicSerializer->deserializeListObject(newValue, it, V::propertyOrdering, V::staticMetaObject);
+        list.append(QSharedPointer<V>(newValue));
+        previous.setValue(list);
+    }
+
+    //-----------------------Deserialize maps of any type------------------------
+    template <typename K, typename V,
+              typename std::enable_if_t<!std::is_base_of<QObject, V>::value, int> = 0>
+    static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        QMap<K, V> out = previous.value<QMap<K, V>>();
+        QVariant key = QVariant::fromValue<K>(K());
+        QVariant value = QVariant::fromValue<V>(V());
+
+        basicSerializer->deserializeMapPair(key, value, it);
+        out[key.value<K>()] = value.value<V>();
+        previous = QVariant::fromValue<QMap<K, V>>(out);
+    }
+
+    template <typename K, typename V,
+              typename std::enable_if_t<std::is_base_of<QObject, V>::value, int> = 0>
+    static void deserializeMap(SelfcheckIterator &it, QVariant &previous) {
+        qProtoDebug() << __func__ << "currentByte:" << QString::number((*it), 16);
+
+        auto out = previous.value<QMap<K, QSharedPointer<V>>>();
+        QVariant key = QVariant::fromValue<K>(K());
+        QVariant value = QVariant::fromValue<V *>(nullptr);
+
+        basicSerializer->deserializeMapPair(key, value, it);
+        out[key.value<K>()] = QSharedPointer<V>(value.value<V *>());
+        previous = QVariant::fromValue<QMap<K, QSharedPointer<V>>>(out);
+    }
+
+    template <typename T,
+              typename std::enable_if_t<std::is_base_of<QObject, T>::value, int> = 0>
+    static void deserializeComplexType(SelfcheckIterator &it, QVariant &to) {
+        T *value = new T;
+        basicSerializer->deserializeObject(value, it, T::propertyOrdering, T::staticMetaObject);
+        to = QVariant::fromValue<T *>(value);
+    }
+};
+
+}

+ 61 - 0
src/protobuf/qqmllistpropertyconstructor.h

@@ -0,0 +1,61 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>, Viktor Kopp <vifactor@gmail.com>
+ *
+ * This file is part of qtprotobuf project https://git.semlanik.org/semlanik/qtprotobuf
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QQmlListProperty>
+#include <QQmlEngine>
+
+namespace qtprotobuf {
+
+template<typename T>
+static void qmllistpropertyAppend(QQmlListProperty<T> *p, T *v) {
+    QQmlEngine::setObjectOwnership(v, QQmlEngine::CppOwnership);
+    reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->append(QSharedPointer<T>(v));
+}
+
+template<typename T>
+static int qmllistpropertyCount(QQmlListProperty<T> *p) {
+    return reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->count();
+}
+
+template<typename T>
+static T * qmllistpropertyAt(QQmlListProperty<T> *p, int index) {
+    return reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->at(index).data();
+}
+
+template<typename T>
+static void qmllistpropertyReset(QQmlListProperty<T> *p){
+    reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->clear();
+}
+
+template<typename T>
+static QQmlListProperty<T> constructQmlListProperty(QObject *p, QList<QSharedPointer<T>> *data)
+{
+    return QQmlListProperty<T>(p, data, qmllistpropertyAppend<T>, qmllistpropertyCount<T>,
+                               qmllistpropertyAt<T>, qmllistpropertyReset<T>);
+}
+
+}

+ 0 - 2
src/protobuf/qtprotobuf.cpp

@@ -112,8 +112,6 @@ void registerTypes() {
     registerBasicConverters<sfixed64>();
     registerBasicConverters<fixed32>();
     registerBasicConverters<fixed64>();
-
-    ProtobufObjectPrivate::registerSerializers();
 }
 }
 

+ 6 - 1
src/protobuf/qtprotobuftypes.h

@@ -27,7 +27,8 @@
 
 #include <QList>
 #include <QMetaType>
-#include <QtQml/QJSValue>
+
+#include <unordered_map>
 
 namespace qtprotobuf {
 
@@ -144,3 +145,7 @@ template<>
 struct std::is_signed<qtprotobuf::sfixed32> : public is_signed<decltype(qtprotobuf::sfixed32::_t)> {};
 template<>
 struct std::is_signed<qtprotobuf::sfixed64> : public is_signed<decltype(qtprotobuf::sfixed64::_t)> {};
+
+namespace qtprotobuf {
+using QProtobufPropertyOrdering = std::unordered_map<int, int>;
+}