From 922ed2caccc49fd59b0f843083db2c90e9f13c5e Mon Sep 17 00:00:00 2001 From: Jan Blackquill Date: Sun, 19 Sep 2021 22:40:40 -0400 Subject: [PATCH] feat, wip: support for optional --- CMakeLists.txt | 4 +-- cmake/QtProtobufGen.cmake | 3 +- cmake/QtProtobufInternalHelpers.cmake | 1 + src/generator/generatorbase.h | 2 ++ src/generator/generatorcommon.cpp | 16 +++++++++ src/generator/generatorcommon.h | 1 + src/generator/messagedeclarationprinter.cpp | 36 ++++++++++++++++++++- src/generator/messagedefinitionprinter.cpp | 21 ++++++++++++ src/generator/templates.cpp | 24 ++++++++------ src/generator/templates.h | 3 ++ tests/test_protobuf/proto/simpletest.proto | 8 +++++ 11 files changed, 105 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e69e6eb..917c6bb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,10 +49,10 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ${${QT_VERSIONED_PREFIX}_POSITION_INDEPENDEN if(UNIX) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # using Clang - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wno-pessimizing-move -Wno-mismatched-tags -Wno-unused-private-field -Wno-self-assign-overloaded") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pessimizing-move -Wno-mismatched-tags -Wno-unused-private-field -Wno-self-assign-overloaded") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # using GCC - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wno-error=deprecated-declarations") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=deprecated-declarations") endif() elseif(WIN32) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") diff --git a/cmake/QtProtobufGen.cmake b/cmake/QtProtobufGen.cmake index 39da158b..84fcf2f2 100644 --- a/cmake/QtProtobufGen.cmake +++ b/cmake/QtProtobufGen.cmake @@ -5,7 +5,7 @@ function(qtprotobuf_link_target TARGET GENERATED_TARGET) endfunction() function(qtprotobuf_generate) - set(options MULTI QML COMMENTS FOLDER FIELDENUM) + set(options MULTI QML COMMENTS FOLDER FIELDENUM ALLOW_PROTO3_OPTIONAL) set(oneValueArgs OUT_DIR TARGET GENERATED_TARGET EXTRA_NAMESPACE) set(multiValueArgs GENERATED_HEADERS EXCLUDE_HEADERS PROTO_FILES PROTO_INCLUDES) cmake_parse_arguments(qtprotobuf_generate "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) @@ -143,6 +143,7 @@ function(qtprotobuf_generate) OUTPUT ${GENERATED_SOURCES_FULL} ${GENERATED_HEADERS_FULL} COMMAND ${PROTOC_COMMAND} --plugin=protoc-gen-qtprotobufgen=${QT_PROTOBUF_EXECUTABLE} + $,--experimental_allow_proto3_optional,> --qtprotobufgen_out=${OUT_DIR} ${PROTO_INCLUDES} ${qtprotobuf_generate_PROTO_FILES} diff --git a/cmake/QtProtobufInternalHelpers.cmake b/cmake/QtProtobufInternalHelpers.cmake index 5958d92e..929a50af 100644 --- a/cmake/QtProtobufInternalHelpers.cmake +++ b/cmake/QtProtobufInternalHelpers.cmake @@ -54,6 +54,7 @@ function(qt_protobuf_internal_add_test) PROTO_FILES ${proto_files} GENERATED_HEADERS ${add_test_target_GENERATED_HEADERS} EXCLUDE_HEADERS ${add_test_target_EXCLUDE_HEADERS} + ALLOW_PROTO3_OPTIONAL ${EXTRA_OPTIONS} PROTO_INCLUDES ${add_test_target_PROTO_INCLUDES}) diff --git a/src/generator/generatorbase.h b/src/generator/generatorbase.h index 06e5aba6..7c24ece8 100644 --- a/src/generator/generatorbase.h +++ b/src/generator/generatorbase.h @@ -136,6 +136,8 @@ class GeneratorBase: public ::google::protobuf::compiler::CodeGenerator GeneratorBase(Mode mode); virtual ~GeneratorBase() = default; + ::google::protobuf::uint64 GetSupportedFeatures() const override { return FEATURE_PROTO3_OPTIONAL; } + virtual bool GenerateAll(const std::vector &files, const std::string ¶meter, ::google::protobuf::compiler::GeneratorContext *generatorContext, diff --git a/src/generator/generatorcommon.cpp b/src/generator/generatorcommon.cpp index d0f953a4..b5026818 100644 --- a/src/generator/generatorcommon.cpp +++ b/src/generator/generatorcommon.cpp @@ -347,6 +347,22 @@ PropertyMap common::producePropertyMap(const FieldDescriptor *field, const Descr return propertyMap; } +PropertyMap common::produceOptionalPropertyMap(const FieldDescriptor *field, const Descriptor *scope) +{ + PropertyMap map = producePropertyMap(field, scope); + + map["property_type"] = "bool"; + map["getter_type"] = "bool"; + map["setter_type"] = "bool"; + map["scope_type"] = "bool"; + map["initializer"] = "true"; + map["scriptable"] = "true"; + map["property_name"] = "has_" + map["property_name"]; + map["property_name_cap"] = utils::upperCaseName(map["property_name"]); + + return map; +} + std::string common::qualifiedName(const std::string &name) { std::string fieldName(name); diff --git a/src/generator/generatorcommon.h b/src/generator/generatorcommon.h index f8d14239..b92dcd3b 100644 --- a/src/generator/generatorcommon.h +++ b/src/generator/generatorcommon.h @@ -80,6 +80,7 @@ struct common { static TypeMap produceSimpleTypeMap(::google::protobuf::FieldDescriptor::Type type); static TypeMap produceTypeMap(const ::google::protobuf::FieldDescriptor *field, const ::google::protobuf::Descriptor *scope); static PropertyMap producePropertyMap(const ::google::protobuf::FieldDescriptor *field, const ::google::protobuf::Descriptor *scope); + static PropertyMap produceOptionalPropertyMap(const ::google::protobuf::FieldDescriptor *field, const ::google::protobuf::Descriptor *scope); static std::string qualifiedName(const std::string &name); 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); diff --git a/src/generator/messagedeclarationprinter.cpp b/src/generator/messagedeclarationprinter.cpp index e09dac22..ec892cae 100644 --- a/src/generator/messagedeclarationprinter.cpp +++ b/src/generator/messagedeclarationprinter.cpp @@ -248,6 +248,16 @@ void MessageDeclarationPrinter::printProperties() } } + for (int i = 0; i < mDescriptor->field_count(); i++) { + const FieldDescriptor *field = mDescriptor->field(i); + + if (!field->is_optional()) { + continue; + } + + mPrinter->Print(common::produceOptionalPropertyMap(field, mDescriptor), Templates::ReadOnlyPropertyTemplate); + } + Outdent(); } @@ -263,6 +273,12 @@ void MessageDeclarationPrinter::printGetters() mPrinter->Print(propertyMap, Templates::GetterTemplate); } + if (field->is_optional()) { + auto optionalMap = common::produceOptionalPropertyMap(field, mDescriptor); + + mPrinter->Print(optionalMap, Templates::GetterTemplate); + } + if (field->is_repeated()) { mPrinter->Print(propertyMap, Templates::GetterContainerExtraTemplate); if (field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_map() @@ -292,6 +308,11 @@ void MessageDeclarationPrinter::printSetters() break; default: mPrinter->Print(propertyMap, Templates::SetterTemplate); + if (field->is_optional()) { + mPrinter->Print(propertyMap, Templates::EndOptionalSetterTemplate); + } else { + mPrinter->Print(propertyMap, Templates::EndSetterTemplate); + } break; } }); @@ -324,7 +345,12 @@ void MessageDeclarationPrinter::printSignals() { Indent(); for (int i = 0; i < mDescriptor->field_count(); i++) { - mPrinter->Print(common::producePropertyMap(mDescriptor->field(i), mDescriptor), Templates::SignalTemplate); + const auto field = mDescriptor->field(i); + mPrinter->Print(common::producePropertyMap(field, mDescriptor), Templates::SignalTemplate); + + if (field->is_optional()) { + mPrinter->Print(common::produceOptionalPropertyMap(field, mDescriptor), Templates::SignalTemplate); + } } Outdent(); } @@ -337,6 +363,11 @@ void MessageDeclarationPrinter::printPrivateMethods() if (common::hasQmlAlias(field)) { mPrinter->Print(propertyMap, Templates::NonScriptableGetterTemplate); mPrinter->Print(propertyMap, Templates::NonScriptableSetterTemplate); + if (field->is_optional()) { + mPrinter->Print(propertyMap, Templates::EndOptionalSetterTemplate); + } else { + mPrinter->Print(propertyMap, Templates::EndSetterTemplate); + } } }); Outdent(); @@ -434,6 +465,9 @@ void MessageDeclarationPrinter::printClassMembers() } else { mPrinter->Print(propertyMap, Templates::MemberTemplate); } + if (field->is_optional()) { + mPrinter->Print(common::produceOptionalPropertyMap(field, mDescriptor), Templates::MemberTemplate); + } }); Outdent(); } diff --git a/src/generator/messagedefinitionprinter.cpp b/src/generator/messagedefinitionprinter.cpp index e6f353d2..e0c0101b 100644 --- a/src/generator/messagedefinitionprinter.cpp +++ b/src/generator/messagedefinitionprinter.cpp @@ -194,6 +194,10 @@ void MessageDefinitionPrinter::printInitializationList(int fieldCount) } } } + + if (field->is_optional()) { + mPrinter->Print(common::produceOptionalPropertyMap(field, mDescriptor), Templates::PropertyDefaultInitializerTemplate); + } } } @@ -372,16 +376,33 @@ void MessageDefinitionPrinter::printGetters() case FieldDescriptor::TYPE_MESSAGE: if (!field->is_map() && !field->is_repeated() && !common::isQtType(field)) { mPrinter->Print(propertyMap, Templates::SetterPrivateTemplateDefinitionMessageType); + if (field->is_optional()) { + mPrinter->Print(propertyMap, Templates::EndOptionalSetterTemplate); + } else { + mPrinter->Print(propertyMap, Templates::EndSetterTemplate); + } + mPrinter->Print(propertyMap, Templates::SetterTemplateDefinitionMessageType); } else { mPrinter->Print(propertyMap, Templates::SetterTemplateDefinitionComplexType); } + if (field->is_optional()) { + mPrinter->Print(propertyMap, Templates::EndOptionalSetterTemplate); + } else { + mPrinter->Print(propertyMap, Templates::EndSetterTemplate); + } break; case FieldDescriptor::FieldDescriptor::TYPE_STRING: case FieldDescriptor::FieldDescriptor::TYPE_BYTES: mPrinter->Print(propertyMap, Templates::SetterTemplateDefinitionComplexType); + if (field->is_optional()) { + mPrinter->Print(propertyMap, Templates::EndOptionalSetterTemplate); + } else { + mPrinter->Print(propertyMap, Templates::EndSetterTemplate); + } break; default: + mPrinter->Print(propertyMap, "/* default ending */"); break; } }); diff --git a/src/generator/templates.cpp b/src/generator/templates.cpp index 0e42834a..2ca93c64 100644 --- a/src/generator/templates.cpp +++ b/src/generator/templates.cpp @@ -86,6 +86,7 @@ const char *Templates::ProtoClassDeclarationBeginTemplate = "\nclass $classname$ " Q_DECLARE_PROTOBUF_SERIALIZERS($classname$)\n"; const char *Templates::PropertyTemplate = "Q_PROPERTY($property_type$ $property_name$ READ $property_name$ WRITE set$property_name_cap$ NOTIFY $property_name$Changed SCRIPTABLE $scriptable$)\n"; +const char *Templates::ReadOnlyPropertyTemplate = "Q_PROPERTY($property_type$ $property_name$ READ $property_name$ NOTIFY $property_name$Changed SCRIPTABLE $scriptable$)\n"; const char *Templates::RepeatedPropertyTemplate = "Q_PROPERTY($property_list_type$ $property_name$ READ $property_name$ WRITE set$property_name_cap$ NOTIFY $property_name$Changed SCRIPTABLE $scriptable$)\n"; const char *Templates::RepeatedMessagePropertyTemplate = "Q_PROPERTY($property_list_type$ $property_name$Data READ $property_name$ WRITE set$property_name_cap$ NOTIFY $property_name$Changed SCRIPTABLE $scriptable$)\n"; const char *Templates::NonScriptablePropertyTemplate = "Q_PROPERTY($property_type$ $property_name$_p READ $property_name$ WRITE set$property_name_cap$ NOTIFY $property_name$Changed SCRIPTABLE false)\n"; @@ -208,37 +209,40 @@ const char *Templates::SetterPrivateTemplateDefinitionMessageType = "void $class " if (m_$property_name$.get() != $property_name$) {\n" " m_$property_name$.reset($property_name$);\n" " $property_name$Changed();\n" - " }\n" - "}\n\n"; + " }\n"; const char *Templates::SetterTemplateDeclarationMessageType = "void set$property_name_cap$(const $setter_type$ &$property_name$);\n"; const char *Templates::SetterTemplateDefinitionMessageType = "void $classname$::set$property_name_cap$(const $setter_type$ &$property_name$)\n{\n" " if (*m_$property_name$ != $property_name$) {\n" " *m_$property_name$ = $property_name$;\n" " $property_name$Changed();\n" - " }\n" - "}\n\n"; + " }\n"; const char *Templates::SetterTemplateDeclarationComplexType = "void set$property_name_cap$(const $setter_type$ &$property_name$);\n"; const char *Templates::SetterTemplateDefinitionComplexType = "void $classname$::set$property_name_cap$(const $setter_type$ &$property_name$)\n{\n" " if (m_$property_name$ != $property_name$) {\n" " m_$property_name$ = $property_name$;\n" " $property_name$Changed();\n" - " }\n" - "}\n\n"; + " }\n"; const char *Templates::SetterTemplate = "void set$property_name_cap$(const $setter_type$ &$property_name$) {\n" " if (m_$property_name$ != $property_name$) {\n" " m_$property_name$ = $property_name$;\n" " $property_name$Changed();\n" - " }\n" - "}\n\n"; + " }\n"; const char *Templates::NonScriptableSetterTemplate = "void set$property_name_cap$_p(const $qml_alias_type$ &$property_name$) {\n" " if (m_$property_name$ != $property_name$) {\n" " m_$property_name$ = $property_name$;\n" " $property_name$Changed();\n" - " }\n" - "}\n\n"; + " }\n"; + +const char *Templates::EndSetterTemplate = "}\n\n"; + +const char *Templates::EndOptionalSetterTemplate = " if (!m_has_$property_name$) {\n" + " m_has_$property_name$ = true;\n" + " Q_EMIT has_$property_name$Changed();\n" + " }\n" + "}\n\n"; const char *Templates::SignalsBlockTemplate = "\nsignals:\n"; const char *Templates::SignalTemplate = "void $property_name$Changed();\n"; diff --git a/src/generator/templates.h b/src/generator/templates.h index 124812fd..02679a8e 100644 --- a/src/generator/templates.h +++ b/src/generator/templates.h @@ -69,6 +69,7 @@ class Templates { static const char *QObjectMacro; static const char *PropertyTemplate; + static const char *ReadOnlyPropertyTemplate; static const char *RepeatedPropertyTemplate; static const char *RepeatedMessagePropertyTemplate; static const char *NonScriptablePropertyTemplate; @@ -147,6 +148,8 @@ class Templates { static const char *SetterTemplateDefinitionComplexType; static const char *SetterTemplate; static const char *NonScriptableSetterTemplate; + static const char *EndSetterTemplate; + static const char *EndOptionalSetterTemplate; static const char *SignalsBlockTemplate; static const char *SignalTemplate; static const char *FieldsOrderingContainerTemplate; diff --git a/tests/test_protobuf/proto/simpletest.proto b/tests/test_protobuf/proto/simpletest.proto index 681cb8a0..b5300b72 100644 --- a/tests/test_protobuf/proto/simpletest.proto +++ b/tests/test_protobuf/proto/simpletest.proto @@ -634,3 +634,11 @@ message LowerCaseFieldMessageName { message NoPackageMessage { SimpleIntMessageExt testField = 1; } + +message OptionalScalarMessage { + optional int32 testField = 1; +} + +message OptionalNonScalarMessage { + optional EmptyMessage testField = 1; +}