소스 검색

Add conditional map-based types declaration

- Add conditional map-based types declaration to avoid double
  declaration of same map type.
- Implement tests

Fixes: #118
Alexey Edelev 4 년 전
부모
커밋
3df238de04

+ 4 - 0
src/generator/classgeneratorbase.cpp

@@ -129,6 +129,10 @@ void ClassGeneratorBase::printMetaTypeDeclaration()
 bool ClassGeneratorBase::isLocalMessageEnum(const google::protobuf::Descriptor *message,
                                             const ::google::protobuf::FieldDescriptor *field)
 {
+    if (message == nullptr) {
+        return false;
+    }
+
     assert(field->enum_type() != nullptr);
     for (int i = 0; i < message->enum_type_count(); i++) {
         const auto enumDescr = message->enum_type(i);

+ 25 - 7
src/generator/protobufclassgenerator.cpp

@@ -54,20 +54,20 @@ void ProtobufClassGenerator::printCopyFunctionality()
 {
     assert(mMessage != nullptr);
     mPrinter->Print({{"classname", mClassName}},
-                   Templates::CopyConstructorDeclarationTemplate);
+                    Templates::CopyConstructorDeclarationTemplate);
 
     mPrinter->Print({{"classname", mClassName}},
-                   Templates::AssignmentOperatorDeclarationTemplate);
+                    Templates::AssignmentOperatorDeclarationTemplate);
 }
 
 void ProtobufClassGenerator::printMoveSemantic()
 {
     assert(mMessage != nullptr);
     mPrinter->Print({{"classname", mClassName}},
-                   Templates::MoveConstructorDeclarationTemplate);
+                    Templates::MoveConstructorDeclarationTemplate);
 
     mPrinter->Print({{"classname", mClassName}},
-                   Templates::MoveAssignmentOperatorDeclarationTemplate);
+                    Templates::MoveAssignmentOperatorDeclarationTemplate);
 }
 
 void ProtobufClassGenerator::printComparisonOperators()
@@ -149,7 +149,7 @@ void ProtobufClassGenerator::printConstructor()
             parameters += parameterList[j] + ", ";
         }
         mPrinter->Print({{"classname", mClassName},
-                        {"parameter_list", parameters}}, Templates::ProtoConstructorTemplate);
+                         {"parameter_list", parameters}}, Templates::ProtoConstructorTemplate);
     }
     mPrinter->Print("\n");
 }
@@ -197,8 +197,26 @@ void ProtobufClassGenerator::printMapsMetaTypesDeclaration()
     for (int i = 0; i < mMessage->field_count(); i++) {
         const FieldDescriptor *field = mMessage->field(i);
         if (field->is_map()) {
-             mPrinter->Print({{"classname", field->message_type()->name()},
-                             {"namespaces", mNamespacesColonDelimited + "::" + mClassName}}, Templates::DeclareMetaTypeTemplate);
+            std::string keyType = getTypeName(field->message_type()->field(0), nullptr);
+            utils::replace(keyType, "::", "_");
+
+            std::string valueType = "";
+            if (field->message_type()->field(1)->type() == FieldDescriptor::TYPE_MESSAGE) {
+                valueType = field->message_type()->field(1)->message_type()->full_name();
+                utils::replace(valueType, ".", "_");
+            } else if (field->message_type()->field(1)->type() == FieldDescriptor::TYPE_ENUM){
+                valueType = field->message_type()->field(1)->enum_type()->full_name();
+                utils::replace(valueType, ".", "_");
+            } else {
+                valueType = getTypeName(field->message_type()->field(0), nullptr);
+                utils::replace(valueType, "::", "_");
+            }
+
+
+            mPrinter->Print({{"classname", field->message_type()->name()},
+                             {"key", keyType},
+                             {"value", valueType},
+                             {"namespaces", mNamespacesColonDelimited + "::" + mClassName}}, Templates::DeclareMetaTypeMapTemplate);
         }
     }
 }

+ 5 - 0
src/generator/templates.cpp

@@ -248,6 +248,11 @@ const char *Templates::DeclareMessageMetaTypeTemplate = "Q_DECLARE_METATYPE($nam
 const char *Templates::DeclareComplexListTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$Repeated)\n";
 const char *Templates::DeclareComplexQmlListTypeTemplate = "Q_DECLARE_METATYPE(QQmlListProperty<$namespaces$::$classname$>)\n";
 
+const char *Templates::DeclareMetaTypeMapTemplate = "#ifndef Q_PROTOBUF_MAP_$key$_$value$\n"
+                                                    "#define Q_PROTOBUF_MAP_$key$_$value$\n"
+                                                    "Q_DECLARE_METATYPE($namespaces$::$classname$)\n"
+                                                    "#endif\n";
+
 const char *Templates::RegisterMetaTypeDefaultTemplate = "qRegisterMetaType<$namespaces$::$type$>();\n";
 const char *Templates::RegisterMetaTypeTemplateNoNamespace = "qRegisterMetaType<$namespaces$::$type$>(\"$type$\");\n";
 const char *Templates::RegisterMetaTypeTemplate = "qRegisterMetaType<$namespaces$::$type$>(\"$namespaces$::$type$\");\n";

+ 1 - 0
src/generator/templates.h

@@ -148,6 +148,7 @@ public:
     static const char *DeclareMessageMetaTypeTemplate;
     static const char *DeclareComplexListTypeTemplate;
     static const char *DeclareComplexQmlListTypeTemplate;
+    static const char *DeclareMetaTypeMapTemplate;
     static const char *RegisterMetaTypeDefaultTemplate;
     static const char *RegisterMetaTypeTemplate;
     static const char *RegisterMetaTypeTemplateNoNamespace;

+ 2 - 1
tests/test_protobuf/CMakeLists.txt

@@ -9,7 +9,8 @@ file(GLOB SOURCES
     serializationcomplexmessagemap.cpp
     converterstest.cpp
     jsonserializationtest.cpp
-    jsondeserializationtest.cpp)
+    jsondeserializationtest.cpp
+    duplicatedmetatypestest.cpp)
 
 add_test_target(TARGET ${TARGET}
     SOURCES ${SOURCES}

+ 66 - 0
tests/test_protobuf/duplicatedmetatypestest.cpp

@@ -0,0 +1,66 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <qprotobufserializer.h>
+
+#include "duplicated_metatypes.qpb.h"
+#include "../testscommon.h"
+
+namespace QtProtobuf {
+namespace tests {
+
+class DuplicatedMetatypesTest : public ::testing::Test
+{
+public:
+    DuplicatedMetatypesTest() = default;
+    void SetUp() override;
+    static void SetUpTestCase();
+};
+
+void DuplicatedMetatypesTest::SetUpTestCase()
+{
+    //Register all types
+    QtProtobuf::qRegisterProtobufTypes();
+}
+
+void DuplicatedMetatypesTest::SetUp()
+{
+}
+
+
+TEST_F(DuplicatedMetatypesTest, SimpleTest) {
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message1, qtprotobufnamespace::duplicated_metatypes::Message1::OptsEntry>(1, "Message1::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message2, qtprotobufnamespace::duplicated_metatypes::Message2::OptsEntry>(1, "Message2::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message3, qtprotobufnamespace::duplicated_metatypes::Message3::OptsEntry>(1, "Message3::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message4, qtprotobufnamespace::duplicated_metatypes::Message4::OptsEntry>(1, "Message4::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message5, qtprotobufnamespace::duplicated_metatypes::Message5::OptsEntry>(1, "Message5::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message6, qtprotobufnamespace::duplicated_metatypes::Message6::OptsEntry>(1, "Message6::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message7, qtprotobufnamespace::duplicated_metatypes::Message7::OptsEntry>(1, "Message7::OptsEntry", "opts");
+    assertMessagePropertyRegistered<qtprotobufnamespace::duplicated_metatypes::Message8, qtprotobufnamespace::duplicated_metatypes::Message8::OptsEntry>(1, "Message8::OptsEntry", "opts");
+}
+
+}
+}

+ 46 - 0
tests/test_protobuf/proto/duplicated_metatypes.proto

@@ -0,0 +1,46 @@
+syntax = "proto3";
+
+package qtprotobufnamespace.duplicated_metatypes;
+
+import "duplicated_metatypes_external.proto";
+
+enum TestEnum {
+    TEST_ENUM_VALUE0 = 0;
+    TEST_ENUM_VALUE1 = 1;
+}
+
+message Message0 {
+    string opts = 1;
+}
+
+message Message1 {
+    map<string, string> opts = 1;
+}
+
+message Message2 {
+    map<string, string> opts = 1;
+}
+
+message Message3 {
+    map<string, qtprotobufnamespace.duplicated_metatypes_external.Message1> opts = 1;
+}
+
+message Message4 {
+    map<string, Message0> opts = 1;
+}
+
+message Message5 {
+    map<string, Message0> opts = 1;
+}
+
+message Message6 {
+    map<string, TestEnum> opts = 1;
+}
+
+message Message7 {
+    map<string, TestEnum> opts = 1;
+}
+
+message Message8 {
+    map<string, qtprotobufnamespace.duplicated_metatypes_external.TestEnum> opts = 1;
+}

+ 20 - 0
tests/test_protobuf/proto/duplicated_metatypes_external.proto

@@ -0,0 +1,20 @@
+syntax = "proto3";
+
+package qtprotobufnamespace.duplicated_metatypes_external;
+
+enum TestEnum {
+    TEST_ENUM_VALUE0 = 0;
+    TEST_ENUM_VALUE1 = 1;
+}
+
+message Message1 {
+    string opts = 1;
+}
+
+message Message2 {
+    map<string, Message1> opts = 1;
+}
+
+message Message3 {
+    map<string, TestEnum> opts = 1;
+}

+ 1 - 11
tests/test_protobuf/simpletest.cpp.inc

@@ -28,6 +28,7 @@
 #include <QSignalSpy>
 
 #include <gtest/gtest.h>
+#include "../testscommon.h"
 
 using namespace qtprotobufnamespace::tests;
 
@@ -42,17 +43,6 @@ public:
     {
     }
 
-    template<typename MessageType, typename PropertyType>
-    static void assertMessagePropertyRegistered(int fieldIndex, const char *propertyTypeName, const char *propertyName)
-    {
-        // TODO: there should be(?) a mapping avaialble: PropertyType -> propertyTypeName
-
-        const int propertyNumber = MessageType::propertyOrdering.at(fieldIndex);
-        ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).typeName(), propertyTypeName);
-        ASSERT_EQ(MessageType::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<PropertyType>());
-        ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).name(), propertyName);
-    }
-
     static void SetUpTestCase();
 };
 

+ 1 - 13
tests/test_wellknowntypes/simpletest.cpp

@@ -42,6 +42,7 @@
 #include <google/protobuf/field_mask.qpb.h>
 
 #include "wellknowntypes.qpb.h"
+#include "../testscommon.h"
 
 using namespace google::protobuf;
 
@@ -55,19 +56,6 @@ public:
     WellknowntypesTest() {
     }
 
-    template<typename MessageType, typename PropertyType>
-    static void assertMessagePropertyRegistered(int fieldIndex, const char *propertyTypeName, const char *propertyName, bool skipMetatypeCheck = false)
-    {
-        // TODO: there should be(?) a mapping avaialble: PropertyType -> propertyTypeName
-
-        const int propertyNumber = MessageType::propertyOrdering.at(fieldIndex);
-        ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).typeName(), propertyTypeName);
-        if(!skipMetatypeCheck) {
-            ASSERT_EQ(MessageType::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<PropertyType>());
-        }
-        ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).name(), propertyName);
-    }
-
     static void SetUpTestCase() {
         QtProtobuf::qRegisterProtobufTypes();
         serializer.reset(new QProtobufSerializer);

+ 50 - 0
tests/testscommon.h

@@ -0,0 +1,50 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2020 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 <gtest/gtest.h>
+#include <QMetaType>
+
+namespace QtProtobuf {
+
+namespace tests {
+
+template<typename MessageType, typename PropertyType>
+static void assertMessagePropertyRegistered(int fieldIndex, const char *propertyTypeName, const char *propertyName, bool skipMetatypeCheck = false)
+{
+    // TODO: there should be(?) a mapping avaialble: PropertyType -> propertyTypeName
+
+    const int propertyNumber = MessageType::propertyOrdering.at(fieldIndex);
+    ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).typeName(), propertyTypeName);
+    if(!skipMetatypeCheck) {
+        ASSERT_EQ(MessageType::staticMetaObject.property(propertyNumber).userType(), qMetaTypeId<PropertyType>());
+    }
+    ASSERT_STREQ(MessageType::staticMetaObject.property(propertyNumber).name(), propertyName);
+}
+
+}
+
+}