Quellcode durchsuchen

Basic implementation of QtTypes

- Add QtCore proto module
- Implement correct generation of Qt-based properties
- Add QtProtobufQtTypes library
- Implement serializers for QtCore types
- Implement tests

TODO: QtGui proto module is in progress
Fixes: #78
Alexey Edelev vor 4 Jahren
Ursprung
Commit
187cd114e0

+ 9 - 6
CMakeLists.txt

@@ -80,12 +80,7 @@ endif()
 set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
-add_subdirectory("src/protobuf")
-add_subdirectory("src/grpc")
-add_subdirectory("src/generator")
-if(NOT WIN32)#TODO: There are linking issues with windows build of well-known types...
-    add_subdirectory("src/wellknowntypes")
-endif()
+include(CMakePackageConfigHelpers)
 configure_package_config_file(
     "${CMAKE_CURRENT_SOURCE_DIR}/ProjectConfig.cmake.in" "${QT_PROTOBUF_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
     INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake")
@@ -94,6 +89,14 @@ install(FILES "${QT_PROTOBUF_BINARY_DIR}/${PROJECT_NAME}Config.cmake" DESTINATIO
 
 export(PACKAGE ${PROJECT_NAME})
 
+add_subdirectory("src/protobuf")
+add_subdirectory("src/grpc")
+add_subdirectory("src/generator")
+if(NOT WIN32)#TODO: There are linking issues with windows build of well-known types...
+    add_subdirectory("src/wellknowntypes")
+    add_subdirectory("src/qttypes")
+endif()
+
 # add a target to generate API documentation with Doxygen
 find_package(Doxygen)
 if(NOT DOXYGEN_FOUND)

+ 7 - 1
cmake/QtProtobufGen.cmake

@@ -66,7 +66,7 @@ function(qtprotobuf_generate)
 
         foreach(PROTO_FILE IN LISTS qtprotobuf_generate_PROTO_FILES)
             get_filename_component(BASE_DIR ${PROTO_FILE} DIRECTORY)
-            set(PROTO_INCLUDES -I"${BASE_DIR}" ${PROTO_INCLUDES})
+            set(PROTO_INCLUDES "-I\"${BASE_DIR}\"" ${PROTO_INCLUDES})
             execute_process(COMMAND ${GO_EXECUTABLE} run ${PROTO_PARSER} ${PROTO_FILE} ${GENERATION_TYPE} ${FOLDER_ENABLED} OUTPUT_VARIABLE GENERATED_HEADERS_PART ERROR_VARIABLE PARSER_ERROR)
             set(GENERATED_HEADERS ${GENERATED_HEADERS} ${GENERATED_HEADERS_PART})
         endforeach()
@@ -127,6 +127,7 @@ function(qtprotobuf_generate)
             WORKING_DIRECTORY ${OUT_DIR}
             DEPENDS ${qtprotobuf_generate_PROTO_FILES} ${QT_PROTOBUF_EXECUTABLE}
             COMMENT "Generating QtProtobuf ${GENERATED_TARGET_NAME} sources..."
+            COMMAND_EXPAND_LISTS
     )
 
     add_custom_target(${GEN_TARGET} DEPENDS ${GENERATED_SOURCES_FULL} ${GENERATED_HEADERS_FULL} ${qtprotobuf_generate_PROTO_FILES})
@@ -149,6 +150,11 @@ function(qtprotobuf_generate)
             target_include_directories(${GENERATED_TARGET_NAME} PRIVATE
                 $<TARGET_PROPERTY:${QT_PROTOBUF_PROJECT}::QtProtobufWellKnownTypes,INTERFACE_INCLUDE_DIRECTORIES>)
         endif()
+
+        if(TARGET ${QT_PROTOBUF_PROJECT}::QtProtobufQtTypes)
+            target_include_directories(${GENERATED_TARGET_NAME} PRIVATE
+                $<TARGET_PROPERTY:${QT_PROTOBUF_PROJECT}::QtProtobufQtTypes,INTERFACE_INCLUDE_DIRECTORIES>)
+        endif()
     endif()
 
     #Automatically link whole static library to specified in parameters target

+ 3 - 0
src/generator/generatorbase.cpp

@@ -91,6 +91,9 @@ void GeneratorBase::printInclude(const std::shared_ptr<::google::protobuf::io::P
             printInclude(printer, message, field->message_type()->field(0), existingIncludes);
             printInclude(printer, message, field->message_type()->field(1), existingIncludes);
             includeTemplate = Templates::ExternalIncludeTemplate;
+        } else if (common::isQtType(field)) {
+            newInclude = field->message_type()->name();
+            includeTemplate = Templates::ExternalIncludeTemplate;
         } else {
             if (!common::isNested(field->message_type())) {
                 std::string outFileBasename = "";

+ 51 - 2
src/generator/generatorcommon.cpp

@@ -61,6 +61,38 @@ std::string common::getScopeNamespacesString(std::string original, const std::st
     return original;
 }
 
+TypeMap common::produceQtTypeMap(const ::Descriptor *type, const Descriptor *scope)
+{
+    std::vector<std::string> namespaceList = getNamespaces(type);
+    std::string namespaces = getNamespacesString(namespaceList, "::");
+    std::string scopeNamespaces = getScopeNamespacesString(namespaces, getNamespacesString(getNamespaces(scope), "::"));
+    std::string qmlPackage = getNamespacesString(namespaceList, ".");
+
+    std::string name = type->name();
+    std::string fullName = name;
+    std::string scopeName = name;
+
+    std::string listName = std::string("QList<") + Templates::ListSuffix + ">";
+    std::string fullListName = listName;
+    std::string scopeListName = listName;
+
+    return {
+        {"type", name},
+        {"full_type", fullName},
+        {"scope_type", scopeName},
+        {"list_type", listName},
+        {"full_list_type", fullListName},
+        {"scope_list_type", scopeListName},
+        {"namespaces", namespaces},
+        {"scope_namespaces", scopeNamespaces},
+        {"qml_package", qmlPackage},
+        {"property_type", fullName},
+        {"property_list_type", fullListName},
+        {"getter_type", scopeName},
+        {"setter_type", scopeName}
+    };
+}
+
 TypeMap common::produceMessageTypeMap(const ::Descriptor *type, const Descriptor *scope)
 {
     std::vector<std::string> namespaceList = getNamespaces(type);
@@ -222,6 +254,18 @@ TypeMap common::produceSimpleTypeMap(FieldDescriptor::Type type)
     };
 }
 
+bool common::isQtType(const FieldDescriptor *field)
+{
+    auto namespaces = getNamespaces(field->message_type());
+    return namespaces.size() == 1 && namespaces[0] == "QtProtobuf"
+            && field->file()->package() != "QtProtobuf"; //Used for qttypes library to avoid types conversion inside library
+}
+
+bool common::isPureMessage(const ::google::protobuf::FieldDescriptor *field)
+{
+    return field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated() && !common::isQtType(field);
+}
+
 TypeMap common::produceTypeMap(const FieldDescriptor *field, const Descriptor *scope)
 {
     TypeMap typeMap;
@@ -231,8 +275,13 @@ TypeMap common::produceTypeMap(const FieldDescriptor *field, const Descriptor *s
     std::vector<std::string> typeNamespace;
 
     switch (field->type()) {
-    case FieldDescriptor::TYPE_MESSAGE:
-        typeMap = produceMessageTypeMap(field->message_type(), scope);
+    case FieldDescriptor::TYPE_MESSAGE: {
+        if (isQtType(field)) {
+            typeMap = produceQtTypeMap(field->message_type(), nullptr);
+        } else {
+            typeMap = produceMessageTypeMap(field->message_type(), scope);
+        }
+    }
         break;
     case FieldDescriptor::TYPE_ENUM:
         typeMap = produceEnumTypeMap(field->enum_type(), scope);

+ 3 - 0
src/generator/generatorcommon.h

@@ -63,6 +63,7 @@ struct common {
 
     static std::string getNamespacesString(const std::vector<std::string> &namespacesList, const std::string &separator);
     static std::string getScopeNamespacesString(std::string original, const std::string &scope);
+    static TypeMap produceQtTypeMap(const ::google::protobuf::Descriptor *type, const ::google::protobuf::Descriptor *scope);
     static TypeMap produceMessageTypeMap(const ::google::protobuf::Descriptor *type, const ::google::protobuf::Descriptor *scope);
     static TypeMap produceEnumTypeMap(const ::google::protobuf::EnumDescriptor *type, const ::google::protobuf::Descriptor *scope);
     static TypeMap produceSimpleTypeMap(::google::protobuf::FieldDescriptor::Type type);
@@ -72,6 +73,8 @@ struct common {
     static bool isLocalEnum(const ::google::protobuf::EnumDescriptor *type, const google::protobuf::Descriptor *scope);
     static EnumVisibility enumVisibility(const ::google::protobuf::EnumDescriptor *type, const ::google::protobuf::Descriptor *scope);
     static bool hasQmlAlias(const ::google::protobuf::FieldDescriptor *field);
+    static bool isQtType(const ::google::protobuf::FieldDescriptor *field);
+    static bool isPureMessage(const ::google::protobuf::FieldDescriptor *field);
 
     using InterateMessageLogic = std::function<void(const ::google::protobuf::FieldDescriptor *, PropertyMap &)>;
     static void iterateMessageFields(const ::google::protobuf::Descriptor *message, InterateMessageLogic callback) {

+ 4 - 4
src/generator/messagedeclarationprinter.cpp

@@ -216,7 +216,7 @@ void MessageDeclarationPrinter::printProperties()
     for (int i = 0; i < mDescriptor->field_count(); i++) {
         const FieldDescriptor *field = mDescriptor->field(i);
         const char *propertyTemplate = Templates::PropertyTemplate;
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             propertyTemplate = Templates::MessagePropertyTemplate;
         } else if (common::hasQmlAlias(field)) {
             propertyTemplate = Templates::NonScriptablePropertyTemplate;
@@ -247,7 +247,7 @@ void MessageDeclarationPrinter::printGetters()
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
         printComments(field);
         mPrinter->Print("\n");
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::GetterPrivateMessageDeclarationTemplate);
             mPrinter->Print(propertyMap, Templates::GetterMessageDeclarationTemplate);
         } else {
@@ -271,7 +271,7 @@ void MessageDeclarationPrinter::printSetters()
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
         switch (field->type()) {
         case FieldDescriptor::TYPE_MESSAGE:
-            if (!field->is_map() && !field->is_repeated()) {
+            if (!field->is_map() && !field->is_repeated() && !common::isQtType(field)) {
                 mPrinter->Print(propertyMap, Templates::SetterPrivateTemplateDeclarationMessageType);
                 mPrinter->Print(propertyMap, Templates::SetterTemplateDeclarationMessageType);
             } else {
@@ -389,7 +389,7 @@ void MessageDeclarationPrinter::printClassMembers()
 {
     Indent();
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::ComplexMemberTemplate);
         } else if (field->is_repeated() && !field->is_map()) {
              mPrinter->Print(propertyMap, Templates::ListMemberTemplate);

+ 11 - 15
src/generator/messagedefinitionprinter.cpp

@@ -167,8 +167,7 @@ void MessageDefinitionPrinter::printInitializationList(int fieldCount)
             }
         }
 
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE
-                && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             if (i < fieldCount) {
                 mPrinter->Print(propertyMap, Templates::MessagePropertyInitializerTemplate);
             } else {
@@ -200,8 +199,7 @@ void MessageDefinitionPrinter::printCopyFunctionality()
     mPrinter->Print({{"classname", mName}},
                     constructorTemplate);
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE
-                && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::MessagePropertyDefaultInitializerTemplate);
         }
     });
@@ -209,7 +207,7 @@ void MessageDefinitionPrinter::printCopyFunctionality()
 
     Indent();
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::CopyComplexFieldTemplate);
         } else {
             mPrinter->Print(propertyMap, Templates::CopyFieldTemplate);
@@ -221,7 +219,7 @@ void MessageDefinitionPrinter::printCopyFunctionality()
     mPrinter->Print({{"classname", mName}}, assignmentOperatorTemplate);
     Indent();
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::CopyComplexFieldTemplate);
         } else {
             mPrinter->Print(propertyMap, Templates::CopyFieldTemplate);
@@ -246,8 +244,7 @@ void MessageDefinitionPrinter::printMoveSemantic()
     mPrinter->Print({{"classname", mName}},
                     constructorTemplate);
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, const PropertyMap &propertyMap) {
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE
-                && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::MessagePropertyDefaultInitializerTemplate);
         }
     });
@@ -259,7 +256,7 @@ void MessageDefinitionPrinter::printMoveSemantic()
                 || field->type() == FieldDescriptor::TYPE_STRING
                 || field->type() == FieldDescriptor::TYPE_BYTES
                 || field->is_repeated()) {
-            if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+            if (common::isPureMessage(field)) {
                 mPrinter->Print(propertyMap, Templates::MoveMessageFieldTemplate);
             } else {
                 mPrinter->Print(propertyMap, Templates::MoveComplexFieldConstructorTemplate);
@@ -283,7 +280,7 @@ void MessageDefinitionPrinter::printMoveSemantic()
                 || field->type() == FieldDescriptor::TYPE_STRING
                 || field->type() == FieldDescriptor::TYPE_BYTES
                 || field->is_repeated()) {
-            if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+            if (common::isPureMessage(field)) {
                 mPrinter->Print(propertyMap, Templates::MoveMessageFieldTemplate);
             } else {
                 mPrinter->Print(propertyMap, Templates::MoveComplexFieldTemplate);
@@ -322,8 +319,7 @@ void MessageDefinitionPrinter::printComparisonOperators()
             Indent();
             isFirst = false;
         }
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE
-                && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::EqualOperatorMessagePropertyTemplate);
         } else {
             mPrinter->Print(propertyMap, Templates::EqualOperatorPropertyTemplate);
@@ -345,12 +341,12 @@ void MessageDefinitionPrinter::printComparisonOperators()
 void MessageDefinitionPrinter::printGetters()
 {
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, PropertyMap &propertyMap) {
-        if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !field->is_repeated()) {
+        if (common::isPureMessage(field)) {
             mPrinter->Print(propertyMap, Templates::GetterPrivateMessageDefinitionTemplate);
             mPrinter->Print(propertyMap, Templates::GetterMessageDefinitionTemplate);
         }
         if (field->is_repeated()) {
-            if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map()
+            if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() && !common::isQtType(field)
                     && GeneratorOptions::instance().hasQml()) {
                 mPrinter->Print(propertyMap, Templates::GetterQmlListDefinitionTemplate);
             }
@@ -360,7 +356,7 @@ void MessageDefinitionPrinter::printGetters()
     common::iterateMessageFields(mDescriptor, [&](const FieldDescriptor *field, PropertyMap &propertyMap) {
         switch (field->type()) {
         case FieldDescriptor::TYPE_MESSAGE:
-            if (!field->is_map() && !field->is_repeated()) {
+            if (!field->is_map() && !field->is_repeated() && !common::isQtType(field)) {
                 mPrinter->Print(propertyMap, Templates::SetterPrivateTemplateDefinitionMessageType);
                 mPrinter->Print(propertyMap, Templates::SetterTemplateDefinitionMessageType);
             } else {

+ 1 - 2
src/generator/multifilegenerator.cpp

@@ -91,8 +91,7 @@ bool MultiFileGenerator::Generate(const FileDescriptor *file,
         //Print dependency classes forward declaration
         for (int i = 0; i < message->field_count(); i++) {
             auto field = message->field(i);
-            if (field->type() == FieldDescriptor::TYPE_MESSAGE
-                    && !field->is_map() && !field->is_repeated()) {
+            if (common::isPureMessage(field)) {
                 auto dependency = field->message_type();
                 if (!common::isNested(dependency)) {//TODO: need to check class relations and apply namespaces accordingly.
                     auto namespaces = common::getNamespaces(dependency);

+ 21 - 0
src/generator/singlefilegenerator.cpp

@@ -97,7 +97,28 @@ bool SingleFileGenerator::GenerateMessages(const ::google::protobuf::FileDescrip
     externalIncludes.insert("QByteArray");
     externalIncludes.insert("QString");
 
+    bool hasQtTypes = false;
+    common::iterateMessages(file, [&externalIncludes, &hasQtTypes](const ::google::protobuf::Descriptor *message){
+        for (int i = 0; i < message->field_count(); i++) {
+            auto field = message->field(i);
+            if (field->type() == ::google::protobuf::FieldDescriptor::TYPE_MESSAGE
+                    && !field->is_map() && !field->is_repeated()
+                    && common::isQtType(field)) {                
+                externalIncludes.insert(field->message_type()->name());
+                hasQtTypes = true;
+            }
+        }
+    });
+
+    if (hasQtTypes) {
+        externalIncludes.insert("QtProtobufQtTypes");
+    }
+
     for (int i = 0; i < file->dependency_count(); i++) {
+        if (file->dependency(i)->name() == "QtProtobuf/QtCore.proto"
+                || file->dependency(i)->name() == "QtProtobuf/QtGui.proto") {
+            continue;
+        }
         internalIncludes.insert(utils::removeFileSuffix(file->dependency(i)->name()) + Templates::ProtoFileSuffix);
     }
 

+ 1 - 1
src/protobuf/qprotobufserializer.cpp

@@ -87,7 +87,7 @@ QByteArray QProtobufSerializer::serializeMessage(const QObject *object, const QP
         Q_ASSERT_X(fieldIndex < 536870912 && fieldIndex > 0, "", "fieldIndex is out of range");
         QMetaProperty metaProperty = metaObject.staticMetaObject.property(propertyIndex);
         const char *propertyName = metaProperty.name();
-        const QVariant &propertyValue = object->property(propertyName);
+        QVariant propertyValue = object->property(propertyName);
         result.append(dPtr->serializeProperty(propertyValue, QProtobufMetaProperty(metaProperty, fieldIndex)));
     }
 

+ 1 - 0
src/protobuf/qtprotobuftypes.h

@@ -253,6 +253,7 @@ struct ProtoTypeRegistrar {
         registerFunctions().push_back(initializer);
     }
 };
+
 }
 
 Q_DECLARE_METATYPE(QtProtobuf::int32)

+ 85 - 0
src/qttypes/CMakeLists.txt

@@ -0,0 +1,85 @@
+set(TARGET QtProtobufQtTypes)
+set(TARGET_EXPORT ${TARGET}Targets)
+set(TARGET_CONFIG ${TARGET}Config)
+
+set(TARGET_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/QtProtobuf)
+set(TARGET_LIB_DIR ${CMAKE_INSTALL_LIBDIR})
+set(TARGET_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+set(TARGET_BINDIR ${CMAKE_INSTALL_BINDIR})
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+find_package(Qt5 COMPONENTS Core Gui Qml REQUIRED)
+find_package(Protobuf QUIET)
+
+set(PROTO_PARSER ${QT_PROTOBUF_BINARY_DIR}/parsemessages.go)
+
+include(${QT_PROTOBUF_CMAKE_DIR}/Coverage.cmake)
+include(${QT_PROTOBUF_CMAKE_DIR}/GenerateQtHeaders.cmake)
+include(${QT_PROTOBUF_CMAKE_DIR}/QtProtobufGen.cmake)
+
+file(GLOB SOURCES
+    qtprotobufqttypes.cpp)
+
+file(GLOB HEADERS
+    qtprotobufqttypes.h
+    qtprotobufqttypesglobal.h)
+
+
+protobuf_generate_qt_headers(PUBLIC_HEADER ${HEADERS} COMPONENT QtProtobuf)
+
+add_library(${TARGET} ${SOURCES})
+
+file(GLOB PROTO_FILE ${CMAKE_CURRENT_SOURCE_DIR}/QtProtobuf/Qt*.proto)
+qtprotobuf_generate(TARGET ${TARGET}
+    OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated
+    PROTO_FILES ${PROTO_FILE}
+    QML FOLDER)
+
+target_compile_definitions(${TARGET} PRIVATE QT_BUILD_PROTOBUF_QT_TYPES_LIB PUBLIC QT_PROTOBUF_VERSION_MAJOR=${PROJECT_VERSION_MAJOR}
+    QT_PROTOBUF_VERSION_MINOR=${PROJECT_VERSION_MINOR})
+
+set_target_properties(${TARGET} PROPERTIES VERSION ${PROJECT_VERSION} OUTPUT_NAME ${TARGET}
+    PROTO_INCLUDES "-I\"${CMAKE_CURRENT_SOURCE_DIR}\"" PUBLIC_HEADER "${HEADERS};${GENERATED_PUBLIC_HEADER}")
+
+target_compile_features(${TARGET} PUBLIC cxx_std_14
+    cxx_auto_type
+    cxx_decltype
+    cxx_final
+    cxx_override
+    cxx_nullptr
+    cxx_lambdas
+    cxx_func_identifier)
+
+target_include_directories(${TARGET} PUBLIC
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/generated>
+    $<BUILD_INTERFACE:${QT_PROTOBUF_BINARY_DIR}/include/QtProtobuf>
+    $<INSTALL_INTERFACE:${TARGET_INCLUDE_DIR}>
+    )
+
+add_library(${QT_PROTOBUF_PROJECT}::${TARGET} ALIAS ${TARGET})
+
+target_link_libraries(${TARGET} PUBLIC Qt5::Core Qt5::Gui Qt5::Qml ${QT_PROTOBUF_PROJECT}::QtProtobuf)
+
+install(TARGETS ${TARGET}
+    EXPORT ${TARGET_EXPORT} COMPONENT dev
+    ARCHIVE DESTINATION ${TARGET_LIB_DIR} COMPONENT lib
+    PUBLIC_HEADER DESTINATION ${TARGET_INCLUDE_DIR} COMPONENT dev
+    LIBRARY DESTINATION ${TARGET_LIB_DIR} COMPONENT lib
+    RUNTIME DESTINATION ${TARGET_BINDIR} COMPONENT lib)
+
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/QtProtobuf/QtCore.proto ${CMAKE_CURRENT_SOURCE_DIR}/QtProtobuf/QtGui.proto DESTINATION "${TARGET_INCLUDE_DIR}" COMPONENT dev)
+install(EXPORT ${TARGET_EXPORT} NAMESPACE ${QT_PROTOBUF_PROJECT}:: FILE ${TARGET_EXPORT}.cmake DESTINATION ${TARGET_CMAKE_DIR} COMPONENT dev)
+
+include(CMakePackageConfigHelpers)
+configure_package_config_file(
+    "${TARGET_CONFIG}.cmake.in" "${QT_PROTOBUF_BINARY_DIR}/${TARGET_CONFIG}.cmake"
+    INSTALL_DESTINATION "${TARGET_CMAKE_DIR}")
+
+install(FILES "${QT_PROTOBUF_BINARY_DIR}/${TARGET_CONFIG}.cmake" DESTINATION "${TARGET_CMAKE_DIR}" COMPONENT dev)
+
+export(TARGETS ${TARGET} NAMESPACE ${QT_PROTOBUF_PROJECT}:: FILE ${TARGET_EXPORT}.cmake)
+
+add_coverage_target(TARGET ${TARGET})

+ 72 - 0
src/qttypes/QtProtobuf/QtCore.proto

@@ -0,0 +1,72 @@
+syntax = "proto3";
+
+package QtProtobuf;
+
+message QUrl {
+    string url = 1;
+} 
+
+message QChar {
+    bytes character = 1;
+}
+
+message QUuid {
+    string uuid = 1;
+}
+
+message QTime {
+    sint32 hour = 1;
+    sint32 minute = 2;
+    sint32 second = 3;
+    sint32 msec = 4;
+}
+
+message QDate {
+    sint32 year = 1;
+    sint32 month = 2;
+    sint32 day = 3;
+}
+
+message QDateTime {
+    QDate date = 1;
+    QTime time = 2;
+    //TODO: add timezone support
+}
+
+message QSize {
+    sint32 width = 1;
+    sint32 height = 2;
+}
+
+message QSizeF {
+    double width = 1;
+    double height = 2;
+}
+
+message QPoint {
+    sint32 x = 1;
+    sint32 y = 2;
+}
+
+message QPointF {
+    double x = 1;
+    double y = 2;
+}
+
+message QRect {
+    QPoint topLeft = 1;
+    QPoint bottomRight = 2;
+}
+
+message QRectF {
+    QPointF topLeft = 1;
+    QPointF bottomRight = 2;
+}
+
+message QPolygon {
+    repeated QPoint points = 1;
+}
+
+message QPolygonF {
+    repeated QPointF points = 1;
+}

+ 46 - 0
src/qttypes/QtProtobuf/QtGui.proto

@@ -0,0 +1,46 @@
+syntax = "proto3";
+
+package QtProtobuf;
+
+message QColor {
+    sint32 red = 1;
+    sint32 green = 2;
+    sint32 blue = 3;
+    sint32 alpha = 4;
+}
+ 
+// message QImage {
+// }
+
+// message QPixmap {
+// }
+
+// message QIcon {
+// }
+
+// message QBitmap {
+// }
+
+// message QQuaternion {
+// }
+
+// message QTransform {
+// }
+
+// message QMatrix {
+// }
+
+// message QMatrix4x4 {
+// }
+
+// message QVector2D {
+// }
+
+// message QVector3D {
+// }
+
+// message QVector4D {
+// }
+
+// message QEasingCurve {
+// }

+ 9 - 0
src/qttypes/QtProtobufQtTypesConfig.cmake.in

@@ -0,0 +1,9 @@
+include(CMakeFindDependencyMacro)
+
+find_dependency(QtProtobufProject COMPONENTS QtProtobuf REQUIRED CONFIG)
+
+if(NOT TARGET @TARGET@ AND NOT @TARGET@_BINARY_DIR)
+    include("${CMAKE_CURRENT_LIST_DIR}/@TARGET_EXPORT@.cmake")
+endif()
+
+@PACKAGE_INIT@

+ 231 - 0
src/qttypes/qtprotobufqttypes.cpp

@@ -0,0 +1,231 @@
+ /*
+ * MIT License
+ *
+ * Copyright (c) 2020 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 <QUrl>
+#include <QChar>
+#include <QUuid>
+#include <QColor>
+#include <QTime>
+#include <QDate>
+#include <QTimeZone>
+#include <QDateTime>
+#include <QDataStream>
+#include <QSize>
+#include <QSizeF>
+#include <QPoint>
+#include <QPointF>
+#include <QRect>
+#include <QRectF>
+#include <QPolygon>
+#include <QPolygonF>
+
+#include <qtprotobuftypes.h>
+#include <qtprotobufqttypes.h>
+
+#include "qabstractprotobufserializer.h"
+#include "qabstractprotobufserializer_p.h"
+
+#include "QtProtobuf/QtCore.qpb.h"
+#include "QtProtobuf/QtGui.qpb.h"
+
+namespace QtProtobuf {
+
+::QUrl convert(const ::QtProtobuf::QUrl &from) {
+    return ::QUrl(from.url());
+}
+
+::QtProtobuf::QUrl convert(const ::QUrl &from) {
+    return ::QtProtobuf::QUrl(from.url());
+}
+
+::QChar convert(const ::QtProtobuf::QChar &from) {
+    QDataStream stream(from.character());
+    ::QChar ret;
+    stream >> ret;
+    return ret;
+}
+
+::QtProtobuf::QChar convert(const ::QChar &from) {
+    QByteArray out;
+    QDataStream stream(&out, QIODevice::WriteOnly);
+    stream << from;
+    return ::QtProtobuf::QChar(out);
+}
+
+::QUuid convert(const ::QtProtobuf::QUuid &from) {
+    return ::QUuid(from.uuid());
+}
+
+::QtProtobuf::QUuid convert(const ::QUuid &from) {
+    return ::QtProtobuf::QUuid(from.toString());
+}
+
+::QColor convert(const ::QtProtobuf::QColor &from) {
+    return ::QColor(from.red(), from.green(), from.blue(), from.alpha());
+}
+
+::QtProtobuf::QColor convert(const ::QColor &from) {
+    return ::QtProtobuf::QColor(from.red(), from.green(), from.blue(), from.alpha());
+}
+
+::QTime convert(const ::QtProtobuf::QTime &from) {
+    return ::QTime(from.hour(), from.minute(), from.second(), from.msec());
+}
+
+::QtProtobuf::QTime convert(const ::QTime &from) {
+    return ::QtProtobuf::QTime(from.hour(), from.minute(), from.second(), from.msec());
+}
+
+::QDate convert(const ::QtProtobuf::QDate &from) {
+    return ::QDate(from.year(), from.month(), from.day());
+}
+
+::QtProtobuf::QDate convert(const ::QDate &from) {
+    return ::QtProtobuf::QDate(from.year(), from.month(), from.day());
+}
+
+::QDateTime convert(const ::QtProtobuf::QDateTime &from) {
+    return ::QDateTime(convert(from.date()), convert(from.time()));
+}
+
+::QtProtobuf::QDateTime convert(const ::QDateTime &from) {
+    return ::QtProtobuf::QDateTime(convert(from.date()), convert(from.time()));
+}
+
+::QSize convert(const ::QtProtobuf::QSize &from) {
+    return ::QSize(from.width(), from.height());
+}
+
+::QtProtobuf::QSize convert(const ::QSize &from) {
+    return ::QtProtobuf::QSize(from.width(), from.height());
+}
+
+::QSizeF convert(const ::QtProtobuf::QSizeF &from) {
+    return ::QSizeF(from.width(), from.height());
+}
+
+::QtProtobuf::QSizeF convert(const ::QSizeF &from) {
+    return ::QtProtobuf::QSizeF(from.width(), from.height());
+}
+
+::QPoint convert(const ::QtProtobuf::QPoint &from) {
+    return ::QPoint(from.x(), from.y());
+}
+
+::QtProtobuf::QPoint convert(const ::QPoint &from) {
+    return ::QtProtobuf::QPoint(from.x(), from.y());
+}
+
+::QPointF convert(const ::QtProtobuf::QPointF &from) {
+    return ::QPointF(from.x(), from.y());
+}
+
+::QtProtobuf::QPointF convert(const ::QPointF &from) {
+    return ::QtProtobuf::QPointF(from.x(), from.y());
+}
+
+::QRect convert(const ::QtProtobuf::QRect &from) {
+    return ::QRect(convert(from.topLeft()), convert(from.bottomRight()));
+}
+
+::QtProtobuf::QRect convert(const ::QRect &from) {
+    return ::QtProtobuf::QRect(convert(from.topLeft()), convert(from.bottomRight()));
+}
+
+::QRectF convert(const ::QtProtobuf::QRectF &from) {
+    return ::QRectF(convert(from.topLeft()), convert(from.bottomRight()));
+}
+
+::QtProtobuf::QRectF convert(const ::QRectF &from) {
+    return ::QtProtobuf::QRectF(convert(from.topLeft()), convert(from.bottomRight()));
+}
+
+::QPolygon convert(const ::QtProtobuf::QPolygon &from) {
+    ::QPolygon polygon;
+    for (auto point : from.points()) {
+        polygon.append(convert(*point));
+    }
+    return polygon;
+}
+
+::QtProtobuf::QPolygon convert(const ::QPolygon &from) {
+    ::QtProtobuf::QPolygon polygon;
+    for (auto point : from) {
+        polygon.points().append(QSharedPointer<::QtProtobuf::QPoint>(new ::QtProtobuf::QPoint(convert(point))));
+    }
+    return polygon;
+}
+
+::QPolygonF convert(const ::QtProtobuf::QPolygonF &from) {
+    ::QPolygonF polygon;
+    for (auto point : from.points()) {
+        polygon.append(convert(*point));
+    }
+    return polygon;
+}
+
+::QtProtobuf::QPolygonF convert(const ::QPolygonF &from) {
+    ::QtProtobuf::QPolygonF polygon;
+    for (auto point : from) {
+        polygon.points().append(QSharedPointer<::QtProtobuf::QPointF>(new ::QtProtobuf::QPointF(convert(point))));
+    }
+    return polygon;
+}
+
+template <typename QType, typename PType>
+void registerQtTypeHandler() {
+    QtProtobufPrivate::registerHandler(qMetaTypeId<QType>(), {
+                                           [](const QtProtobuf::QAbstractProtobufSerializer *serializer, const QVariant &value, const QtProtobuf::QProtobufMetaProperty &property, QByteArray &buffer) {
+                                               PType object(convert(value.value<QType>()));
+                                               buffer.append(serializer->serializeObject(&object, PType::protobufMetaObject, property));
+                                           },
+                                           [](const QtProtobuf::QAbstractProtobufSerializer *serializer, QtProtobuf::QProtobufSelfcheckIterator &it, QVariant &value) {
+                                               PType object;
+                                               serializer->deserializeObject(&object, PType::protobufMetaObject, it);
+                                               value = QVariant::fromValue<QType>(convert(object));
+                                           }, QtProtobufPrivate::ObjectHandler });
+}
+
+void qRegisterProtobufQtTypes() {
+    registerQtTypeHandler<::QUrl, ::QtProtobuf::QUrl>();
+    registerQtTypeHandler<::QChar, ::QtProtobuf::QChar>();
+    registerQtTypeHandler<::QUuid, ::QtProtobuf::QUuid>();
+    registerQtTypeHandler<::QTime, ::QtProtobuf::QTime>();
+    registerQtTypeHandler<::QDate, ::QtProtobuf::QDate>();
+    registerQtTypeHandler<::QDateTime, ::QtProtobuf::QDateTime>();
+    registerQtTypeHandler<::QDateTime, ::QtProtobuf::QDateTime>();
+    registerQtTypeHandler<::QSize, ::QtProtobuf::QSize>();
+    registerQtTypeHandler<::QSizeF, ::QtProtobuf::QSizeF>();
+    registerQtTypeHandler<::QPoint, ::QtProtobuf::QPoint>();
+    registerQtTypeHandler<::QPointF, ::QtProtobuf::QPointF>();
+    registerQtTypeHandler<::QRect, ::QtProtobuf::QRect>();
+    registerQtTypeHandler<::QRectF, ::QtProtobuf::QRectF>();
+    registerQtTypeHandler<::QPolygon, ::QtProtobuf::QPolygon>();
+    registerQtTypeHandler<::QPolygonF, ::QtProtobuf::QPolygonF>();
+
+    registerQtTypeHandler<::QColor, ::QtProtobuf::QColor>();
+}
+
+}

+ 32 - 0
src/qttypes/qtprotobufqttypes.h

@@ -0,0 +1,32 @@
+/*
+* MIT License
+*
+* Copyright (c) 2020 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 //QtProtobufQtTypes
+
+#include <qtprotobufqttypesglobal.h>
+
+namespace QtProtobuf {
+extern Q_PROTOBUF_QT_TYPES_EXPORT void qRegisterProtobufQtTypes();
+}

+ 39 - 0
src/qttypes/qtprotobufqttypesglobal.h

@@ -0,0 +1,39 @@
+/*
+* MIT License
+*
+* Copyright (c) 2020 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 <QtCore/QtGlobal>
+
+#ifndef QT_PROTOBUF_STATIC
+    #if defined(QT_BUILD_PROTOBUF_QT_TYPES_LIB)
+        #define Q_PROTOBUF_QT_TYPES_EXPORT Q_DECL_EXPORT
+    #else
+        #define Q_PROTOBUF_QT_TYPES_EXPORT Q_DECL_IMPORT
+    #endif
+#else
+    #define Q_PROTOBUF_QT_TYPES_EXPORT
+#endif
+

+ 2 - 2
src/wellknowntypes/CMakeLists.txt

@@ -32,7 +32,7 @@ function(add_wellknowntype TYPENAME)
             qtprotobuf_generate(TARGET ${TARGET} GENERATED_TARGET ${TYPENAME}
                 OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated
                 PROTO_FILES ${PROTO_FILE}
-                PROTO_INCLUDES -I${INCLUDE_DIR}
+                PROTO_INCLUDES "-I\"${INCLUDE_DIR}\""
                 QML FOLDER)
             target_include_directories(${TYPENAME} PRIVATE
                 $<TARGET_PROPERTY:${QT_PROTOBUF_PROJECT}::QtProtobufWellKnownTypes,INTERFACE_INCLUDE_DIRECTORIES>)
@@ -64,7 +64,7 @@ target_compile_definitions(${TARGET} PRIVATE QT_BUILD_PROTOBUF_WELLKNOWNTYPES_LI
     QT_PROTOBUF_VERSION_MINOR=${PROJECT_VERSION_MINOR})
 
 set_target_properties(${TARGET} PROPERTIES VERSION ${PROJECT_VERSION} PUBLIC_HEADER "${GENERATED_PUBLIC_HEADER}" OUTPUT_NAME ${TARGET}
-    PROTO_INCLUDES -I${QT_PROTOBUF_SOURCE_DIR}/3rdparty/grpc/third_party/protobuf/src)
+    PROTO_INCLUDES "-I\"${QT_PROTOBUF_SOURCE_DIR}/3rdparty/grpc/third_party/protobuf/src\"")
 
 target_compile_features(${TARGET} PUBLIC cxx_std_14
     cxx_auto_type

+ 2 - 1
tests/CMakeLists.txt

@@ -5,5 +5,6 @@ add_subdirectory("test_qml")
 add_subdirectory("test_protobuf_multifile")
 add_subdirectory("test_qprotobuf_serializer_plugin")
 if(NOT WIN32)#TODO: There are linking issues with windows build of well-known types...
-	add_subdirectory("test_wellknowntypes")
+    add_subdirectory("test_wellknowntypes")
+    add_subdirectory("test_qttypes")
 endif()

+ 17 - 0
tests/test_qttypes/CMakeLists.txt

@@ -0,0 +1,17 @@
+set(TARGET qttypes_test)
+
+include(${QT_PROTOBUF_CMAKE_DIR}/QtProtobufTest.cmake)
+find_package(QtProtobufProject CONFIG COMPONENTS QtProtobuf QtGrpc QtProtobufQtTypes REQUIRED)
+
+file(GLOB SOURCES
+    simpletest.cpp)
+
+add_test_target(TARGET ${TARGET}
+    SOURCES ${SOURCES}
+    PROTO_INCLUDES $<TARGET_PROPERTY:${QT_PROTOBUF_PROJECT}::QtProtobufQtTypes,PROTO_INCLUDES>
+    QML)
+add_target_windeployqt(TARGET ${TARGET}
+    QML_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+
+target_link_libraries(${TARGET} PRIVATE ${QT_PROTOBUF_PROJECT}::QtProtobufQtTypes)
+add_test(NAME ${TARGET} COMMAND ${TARGET})

+ 60 - 0
tests/test_qttypes/proto/qttypes.proto

@@ -0,0 +1,60 @@
+syntax = "proto3";
+
+package qtprotobufnamespace.qttypes.tests;
+import "QtProtobuf/QtCore.proto";
+
+message QUrlMessage {
+    QtProtobuf.QUrl testField = 1;
+}
+
+message QUuidMessage {
+    QtProtobuf.QUuid testField = 1;
+}
+
+message QCharMessage {
+    QtProtobuf.QChar testField = 1;
+}
+
+message QTimeMessage {
+    QtProtobuf.QTime testField = 1;
+}
+
+message QDateMessage {
+    QtProtobuf.QDate testField = 1;
+}
+
+message QDateTimeMessage {
+    QtProtobuf.QDateTime testField = 1;
+}
+
+message QSizeMessage {
+    QtProtobuf.QSize testField = 1;
+}
+
+message QSizeFMessage {
+    QtProtobuf.QSizeF testField = 1;
+}
+
+message QPointMessage {
+    QtProtobuf.QPoint testField = 1;
+}
+
+message QPointFMessage {
+    QtProtobuf.QPointF testField = 1;
+}
+
+message QRectMessage {
+    QtProtobuf.QRect testField = 1;
+}
+
+message QRectFMessage {
+    QtProtobuf.QRectF testField = 1;
+}
+
+message QPolygonMessage {
+    QtProtobuf.QPolygon testField = 1;
+}
+
+message QPolygonFMessage {
+    QtProtobuf.QPolygonF testField = 1;
+}

+ 334 - 0
tests/test_qttypes/simpletest.cpp

@@ -0,0 +1,334 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 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 <QVariantList>
+#include <QMetaProperty>
+#include <QSignalSpy>
+#include <QProtobufSerializer>
+#include <QProtobufJsonSerializer>
+#include <QDateTime>
+
+#include <stdio.h>
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "../testscommon.h"
+
+#include "qttypes.qpb.h"
+
+namespace QtProtobuf {
+namespace tests {
+
+class QtTypesTest : public ::testing::Test
+{
+public:
+    // see simpletest.proto for property names and their field indices
+    QtTypesTest() {
+    }
+
+    static void SetUpTestCase() {
+        QtProtobuf::qRegisterProtobufTypes();
+        QtProtobuf::qRegisterProtobufQtTypes();
+        serializer.reset(new QProtobufSerializer);
+    }
+
+    static std::unique_ptr<QProtobufSerializer> serializer;
+};
+
+std::unique_ptr<QProtobufSerializer> QtTypesTest::serializer;
+
+TEST_F(QtTypesTest, QUrlTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QUrlMessage, QUrl>(1, "QUrl", "testField");
+
+    qtprotobufnamespace::qttypes::tests::QUrlMessage msg;
+    msg.setTestField(QUrl("https://github.com/semlanik/qtprotobuf"));
+    EXPECT_TRUE(QByteArray::fromHex("0a280a2668747470733a2f2f6769746875622e636f6d2f73656d6c616e696b2f717470726f746f627566") ==
+                msg.serialize(serializer.get()));
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a1d0a1b68747470733a2f2f6769746875622e636f6d2f73656d6c616e696b"));
+
+    EXPECT_STREQ("https://github.com/semlanik", msg.testField().url().toStdString().c_str());
+}
+
+TEST_F(QtTypesTest, QCharTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QCharMessage, QChar>(1, "QChar", "testField");
+
+    qtprotobufnamespace::qttypes::tests::QCharMessage msg;
+    msg.setTestField(QChar('q'));
+    EXPECT_TRUE(QByteArray::fromHex("0a040a020071") ==
+                msg.serialize(serializer.get()));
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a040a0220ac"));
+
+    EXPECT_TRUE(QChar(8364) == msg.testField());
+}
+
+
+TEST_F(QtTypesTest, QUuidTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QUuidMessage, QUuid>(1, "QUuid", "testField");
+
+    qtprotobufnamespace::qttypes::tests::QUuidMessage msg;
+    msg.setTestField(QUuid("{4bcbcdc3-c5b3-4d34-97fe-af78c825cc7d}"));
+
+    EXPECT_TRUE(QByteArray::fromHex("0a280a267b34626362636463332d633562332d346433342d393766652d6166373863383235636337647d") ==
+                msg.serialize(serializer.get()));
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a280a267b34626362636463332d633562332d346433342d393766652d6166373863383235636337647d"));
+
+    EXPECT_TRUE(QUuid("{4bcbcdc3-c5b3-4d34-97fe-af78c825cc7d}") == msg.testField());
+}
+
+TEST_F(QtTypesTest, QTimeTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QTimeMessage, QTime>(1, "QTime", "testField");
+
+    qtprotobufnamespace::qttypes::tests::QTimeMessage msg;
+    msg.setTestField(QTime(5, 30, 48, 123));
+    QByteArray result = msg.serialize(serializer.get());
+    EXPECT_TRUE(QByteArray::fromHex("0a09080a103c186020f601") ==
+                result || QByteArray::fromHex("0a0920f6011860103c080a") == result);
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a09080e103c1824208205"));
+    EXPECT_EQ(msg.testField().hour(), 7);
+    EXPECT_EQ(msg.testField().minute(), 30);
+    EXPECT_EQ(msg.testField().second(), 18);
+    EXPECT_EQ(msg.testField().msec(), 321);
+}
+
+TEST_F(QtTypesTest, QDateTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QDateMessage, QDate>(1, "QDate", "testField");
+    qtprotobufnamespace::qttypes::tests::QDateMessage msg;
+    msg.setTestField(QDate(1856, 6, 10));
+    QByteArray result = msg.serialize(serializer.get());
+    EXPECT_TRUE(QByteArray::fromHex("0a0708801d100c1814") ==
+                result || QByteArray::fromHex("0a071814100c08801d") == result);
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0708d21d1006181c"));
+    EXPECT_EQ(msg.testField().year(), 1897);
+    EXPECT_EQ(msg.testField().month(), 3);
+    EXPECT_EQ(msg.testField().day(), 14);
+}
+
+TEST_F(QtTypesTest, QDateTimeTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QDateTimeMessage, QDateTime>(1, "QDateTime", "testField");
+    qtprotobufnamespace::qttypes::tests::QDateTimeMessage msg;
+    msg.setTestField({QDate(1897, 3, 14), QTime(7, 30, 12, 321)});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a1412092082051818103c080e0a07181c100608d21d") ==
+                result || QByteArray::fromHex("0a140a0708d21d1006181c1209080e103c1818208205") == result);
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a140a0708d21d1006181c1209080e103c1818208205"));
+    EXPECT_TRUE(msg.testField() == QDateTime(QDate(1897, 3, 14), QTime(7, 30, 12, 321)));
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a140a0708801d100c18141209080a103c186020f601"));
+    EXPECT_TRUE(msg.testField() == QDateTime(QDate(1856, 6, 10), QTime(5, 30, 48, 123)));
+}
+
+TEST_F(QtTypesTest, QSizeTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QSizeMessage, QSize>(1, "QSize", "testField");
+    qtprotobufnamespace::qttypes::tests::QSizeMessage msg;
+    msg.setTestField({1024, 768});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a0610800c088010") ==
+                result || QByteArray::fromHex("0a0608801010800c") == result);
+
+    msg.setTestField({});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0608801010800c"));
+    EXPECT_EQ(msg.testField().width(), 1024);
+    EXPECT_EQ(msg.testField().height(), 768);
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0608800c108010"));
+    EXPECT_EQ(msg.testField().width(), 768);
+    EXPECT_EQ(msg.testField().height(), 1024);
+}
+
+TEST_F(QtTypesTest, QSizeFTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QSizeFMessage, QSizeF>(1, "QSizeF", "testField");
+    qtprotobufnamespace::qttypes::tests::QSizeFMessage msg;
+    msg.setTestField({1024.0, 768.0});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a12110000000000008840090000000000009040") ==
+                result || QByteArray::fromHex("0a12090000000000009040110000000000008840") == result);
+
+    msg.setTestField({});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a12090000000000009040110000000000008840"));
+    EXPECT_EQ(msg.testField().width(), 1024.0);
+    EXPECT_EQ(msg.testField().height(), 768.0);
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a12090000000000008840110000000000009040"));
+    EXPECT_EQ(msg.testField().width(), 768.0);
+    EXPECT_EQ(msg.testField().height(), 1024.0);
+}
+
+TEST_F(QtTypesTest, QPointTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QPointMessage, QPoint>(1, "QPoint", "testField");
+    qtprotobufnamespace::qttypes::tests::QPointMessage msg;
+    msg.setTestField({1024, 768});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a0610800c088010") ==
+                result || QByteArray::fromHex("0a0608801010800c") == result);
+
+    msg.setTestField({});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0608801010800c"));
+    EXPECT_EQ(msg.testField().x(), 1024);
+    EXPECT_EQ(msg.testField().y(), 768);
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0608800c108010"));
+    EXPECT_EQ(msg.testField().x(), 768);
+    EXPECT_EQ(msg.testField().y(), 1024);
+}
+
+TEST_F(QtTypesTest, QPointFTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QPointFMessage, QPointF>(1, "QPointF", "testField");
+    qtprotobufnamespace::qttypes::tests::QPointFMessage msg;
+    msg.setTestField({1024.0, 768.0});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a12110000000000008840090000000000009040") ==
+                result || QByteArray::fromHex("0a12090000000000009040110000000000008840") == result);
+
+    msg.setTestField({});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a12090000000000009040110000000000008840"));
+    EXPECT_EQ(msg.testField().x(), 1024.0);
+    EXPECT_EQ(msg.testField().y(), 768.0);
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a12090000000000008840110000000000009040"));
+    EXPECT_EQ(msg.testField().x(), 768.0);
+    EXPECT_EQ(msg.testField().y(), 1024.0);
+}
+
+TEST_F(QtTypesTest, QRectTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QRectMessage, QRect>(1, "QRect", "testField");
+    qtprotobufnamespace::qttypes::tests::QRectMessage msg;
+    msg.setTestField({QPoint(768, 768), QPoint(1024, 1024)});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a1012061080100880100a0610800c08800c") ==
+                result || QByteArray::fromHex("0a100a0608800c10800c1206088010108010") == result);
+    msg.setTestField({});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a100a0608800c10800c1206088010108010"));
+    EXPECT_EQ(msg.testField().x(), 768);
+    EXPECT_EQ(msg.testField().y(), 768);
+    EXPECT_EQ(msg.testField().width(), 257); //WTF Qt, siriously why?
+    EXPECT_EQ(msg.testField().height(), 257); //WTF Qt, siriously why?
+
+    msg.setTestField({QPoint(0, 0), QPoint(1024, 768)});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0a0a00120608801010800c"));
+    EXPECT_EQ(msg.testField().x(), 0);
+    EXPECT_EQ(msg.testField().y(), 0);
+    EXPECT_EQ(msg.testField().width(), 1025); //WTF Qt, siriously why?
+    EXPECT_EQ(msg.testField().height(), 769); //WTF Qt, siriously why?
+}
+
+TEST_F(QtTypesTest, QRectFTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QRectFMessage, QRectF>(1, "QRectF", "testField");
+    qtprotobufnamespace::qttypes::tests::QRectFMessage msg;
+    msg.setTestField({QPointF(768.0, 768.0), QPointF(1024.0, 1024.0)});
+    QByteArray result = msg.serialize(serializer.get());
+
+    EXPECT_TRUE(QByteArray::fromHex("0a2812121100000000000090400900000000000090400a12110000000000008840090000000000008840") ==
+                result || QByteArray::fromHex("0a280a120900000000000088401100000000000088401212090000000000009040110000000000009040") == result);
+    msg.setTestField({});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a280a120900000000000088401100000000000088401212090000000000009040110000000000009040"));
+    EXPECT_EQ(msg.testField().x(), 768.0);
+    EXPECT_EQ(msg.testField().y(), 768.0);
+    EXPECT_EQ(msg.testField().width(), 256.0);
+    EXPECT_EQ(msg.testField().height(), 256.0);
+
+    msg.setTestField({QPointF(0, 0), QPointF(1024.0, 768.0)});
+
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a160a001212090000000000009040110000000000008840"));
+    EXPECT_EQ(msg.testField().x(), 0.0);
+    EXPECT_EQ(msg.testField().y(), 0.0);
+    EXPECT_EQ(msg.testField().width(), 1024.0);
+    EXPECT_EQ(msg.testField().height(), 768.0);
+}
+
+TEST_F(QtTypesTest, QPolygonTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QPolygonMessage, QPolygon>(1, "QPolygon", "testField");
+    qtprotobufnamespace::qttypes::tests::QPolygonMessage msg;
+    msg.setTestField(QPolygon({QPoint(10, 0), QPoint(20, 20), QPoint(0, 20)}));
+    QByteArray result = msg.serialize(serializer.get());
+    EXPECT_TRUE(QByteArray::fromHex("0a0e0a0208140a04102808280a021028") ==
+                result || QByteArray::fromHex("0a0e0a0208140a04082810280a021028") == result);
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a0e0a0208140a04082810280a021028"));
+
+    EXPECT_EQ(msg.testField().size(), 3);
+    EXPECT_EQ(msg.testField()[0], QPoint(10, 0));
+    EXPECT_EQ(msg.testField()[1], QPoint(20, 20));
+    EXPECT_EQ(msg.testField()[2], QPoint(0, 20));
+}
+
+TEST_F(QtTypesTest, QPolygonFTest)
+{
+    assertMessagePropertyRegistered<qtprotobufnamespace::qttypes::tests::QPolygonFMessage, QPolygonF>(1, "QPolygonF", "testField");
+    qtprotobufnamespace::qttypes::tests::QPolygonFMessage msg;
+    msg.setTestField(QPolygonF({QPointF(10, 0), QPointF(20, 20), QPointF(0, 20)}));
+    QByteArray result = msg.serialize(serializer.get());
+    EXPECT_TRUE(QByteArray::fromHex("0a3c0a121100000000000000000900000000000024400a121100000000000034400900000000000034400a12110000000000003440090000000000000000") ==
+                result || QByteArray::fromHex("0a3c0a090900000000000024400a120900000000000034401100000000000034400a09110000000000003440") == result);
+
+    msg.setTestField({});
+    msg.deserialize(serializer.get(), QByteArray::fromHex("0a2a0a090900000000000024400a120900000000000034401100000000000034400a09110000000000003440"));
+
+    EXPECT_EQ(msg.testField().size(), 3);
+    EXPECT_EQ(msg.testField()[0], QPointF(10, 0));
+    EXPECT_EQ(msg.testField()[1], QPointF(20, 20));
+    EXPECT_EQ(msg.testField()[2], QPointF(0, 20));
+}
+
+}
+}