From 1b6beaa53da7143d2daeef70ff987dd5b526ae5b Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Fri, 24 Feb 2023 17:04:34 -0800 Subject: [PATCH] [Upb C++] Update generator to create oneof_case() accessor and OneOfCase enumerations. PiperOrigin-RevId: 512197371 --- protos_generator/gen_accessors.cc | 44 ++++++++++++++++++++++++ protos_generator/gen_accessors.h | 2 ++ protos_generator/gen_messages.cc | 1 + protos_generator/gen_utils.cc | 26 ++++++++++++++ protos_generator/gen_utils.h | 4 +++ protos_generator/tests/test_generated.cc | 7 +++- 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/protos_generator/gen_accessors.cc b/protos_generator/gen_accessors.cc index a6cfe5320e..9f599cc8b0 100644 --- a/protos_generator/gen_accessors.cc +++ b/protos_generator/gen_accessors.cc @@ -672,9 +672,53 @@ void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc, } } } + for (int i = 0; i < desc->real_oneof_decl_count(); ++i) { + const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i); + output("using $0Access::$1_case;\n", class_name, oneof->name()); + output("using $0Access::$1Case;\n", class_name, + ToCamelCase(oneof->name(), /*lower_first=*/false)); + for (int j = 0; j < oneof->field_count(); ++j) { + const protobuf::FieldDescriptor* field = oneof->field(j); + output("using $0Access::k$1;\n", class_name, + ToCamelCase(field->name(), /*lower_first=*/false), + field->number()); + } + output("using $0Access::$1_NOT_SET;\n", class_name, + absl::AsciiStrToUpper(oneof->name())); + } output("using $0Access::msg;\n", class_name); } +void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output) { + // Generate const methods. + OutputIndenter i(output); + std::string class_name = ClassName(desc); + auto field_names = CreateFieldNameMap(desc); + for (int i = 0; i < desc->real_oneof_decl_count(); ++i) { + const protobuf::OneofDescriptor* oneof = desc->oneof_decl(i); + output("enum $0Case {\n", + ToCamelCase(oneof->name(), /*lower_first=*/false)); + for (int j = 0; j < oneof->field_count(); ++j) { + const protobuf::FieldDescriptor* field = oneof->field(j); + output(" k$0 = $1,\n", ToCamelCase(field->name(), /*lower_first=*/false), + field->number()); + } + output(" $0_NOT_SET = 0,\n", absl::AsciiStrToUpper(oneof->name())); + output("};\n\n"); + output("$0Case $1_case() const {\n", + ToCamelCase(oneof->name(), /*lower_first=*/false), oneof->name()); + for (int j = 0; j < oneof->field_count(); ++j) { + const protobuf::FieldDescriptor* field = oneof->field(j); + std::string resolved_field_name = ResolveFieldName(field, field_names); + output(" if (has_$0()) { return k$1; }\n", resolved_field_name, + ToCamelCase(field->name(), /*lower_first=*/false)); + } + output(" return $0_NOT_SET;\n", absl::AsciiStrToUpper(oneof->name())); + output("}\n;"); + } +} + std::string ResolveFieldName(const protobuf::FieldDescriptor* field, const NameToFieldDescriptorMap& field_names) { // C++ implementation specific reserved names. diff --git a/protos_generator/gen_accessors.h b/protos_generator/gen_accessors.h index 070cb886d8..64df52352a 100644 --- a/protos_generator/gen_accessors.h +++ b/protos_generator/gen_accessors.h @@ -39,6 +39,8 @@ void WriteFieldAccessorsInHeader(const protobuf::Descriptor* desc, void WriteAccessorsInSource(const protobuf::Descriptor* desc, Output& output); void WriteUsingAccessorsInHeader(const protobuf::Descriptor* desc, MessageClassType handle_type, Output& output); +void WriteOneofAccessorsInHeader(const protobuf::Descriptor* desc, + Output& output); } // namespace protos_generator #endif // UPB_PROTOS_GENERATOR_ACCESSORS_H_ diff --git a/protos_generator/gen_messages.cc b/protos_generator/gen_messages.cc index acdbad085b..2af76ff4ff 100644 --- a/protos_generator/gen_messages.cc +++ b/protos_generator/gen_messages.cc @@ -117,6 +117,7 @@ void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor, )cc", ClassName(descriptor), MessageName(descriptor)); WriteFieldAccessorsInHeader(descriptor, output); + WriteOneofAccessorsInHeader(descriptor, output); output.Indent(); output( R"cc( diff --git a/protos_generator/gen_utils.cc b/protos_generator/gen_utils.cc index 4ab3572b2e..53630fdf26 100644 --- a/protos_generator/gen_utils.cc +++ b/protos_generator/gen_utils.cc @@ -27,6 +27,7 @@ #include "protos_generator/gen_utils.h" +#include #include #include "absl/log/absl_check.h" @@ -264,6 +265,7 @@ std::vector SortedExtensions( std::vector FieldNumberOrder( const protobuf::Descriptor* message) { std::vector fields; + fields.reserve(message->field_count()); for (int i = 0; i < message->field_count(); i++) { fields.push_back(message->field(i)); } @@ -275,4 +277,28 @@ std::vector FieldNumberOrder( return fields; } +std::string ToCamelCase(const std::string& input, bool lower_first) { + bool capitalize_next = !lower_first; + std::string result; + result.reserve(input.size()); + + for (char character : input) { + if (character == '_') { + capitalize_next = true; + } else if (capitalize_next) { + result.push_back(absl::ascii_toupper(character)); + capitalize_next = false; + } else { + result.push_back(character); + } + } + + // Lower-case the first letter. + if (lower_first && !result.empty()) { + result[0] = absl::ascii_tolower(result[0]); + } + + return result; +} + } // namespace protos_generator diff --git a/protos_generator/gen_utils.h b/protos_generator/gen_utils.h index 5563f9f26a..394c244737 100644 --- a/protos_generator/gen_utils.h +++ b/protos_generator/gen_utils.h @@ -28,6 +28,8 @@ #ifndef UPB_PROTOS_GENERATOR_GEN_UTILS_H_ #define UPB_PROTOS_GENERATOR_GEN_UTILS_H_ +#include + #include "google/protobuf/descriptor.pb.h" #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" @@ -88,6 +90,8 @@ std::vector FieldNumberOrder( inline constexpr absl::string_view kNoPackageNamePrefix = "protos_"; +std::string ToCamelCase(const std::string& input, bool lower_first); + } // namespace protos_generator #endif // UPB_PROTOS_GENERATOR_GEN_UTILS_H_ diff --git a/protos_generator/tests/test_generated.cc b/protos_generator/tests/test_generated.cc index a085de00ab..c7a4258673 100644 --- a/protos_generator/tests/test_generated.cc +++ b/protos_generator/tests/test_generated.cc @@ -24,6 +24,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include "gtest/gtest.h" #include "protos/protos.h" @@ -271,22 +272,26 @@ TEST(CppGeneratedCode, OneOfFields) { EXPECT_FALSE(test_model.has_oneof_member1()); EXPECT_FALSE(test_model.has_oneof_member2()); + EXPECT_EQ(TestModel::CHILD_ONEOF1_NOT_SET, test_model.child_oneof1_case()); test_model.set_oneof_member1("one of string"); EXPECT_TRUE(test_model.has_oneof_member1()); EXPECT_FALSE(test_model.has_oneof_member2()); EXPECT_EQ(test_model.oneof_member1(), "one of string"); + EXPECT_EQ(TestModel::kOneofMember1, test_model.child_oneof1_case()); test_model.set_oneof_member2(true); EXPECT_FALSE(test_model.has_oneof_member1()); EXPECT_TRUE(test_model.has_oneof_member2()); EXPECT_EQ(test_model.oneof_member2(), true); + EXPECT_EQ(TestModel::kOneofMember2, test_model.child_oneof1_case()); test_model.clear_oneof_member2(); EXPECT_FALSE(test_model.has_oneof_member1()); EXPECT_FALSE(test_model.has_oneof_member2()); EXPECT_EQ(test_model.oneof_member1(), ""); EXPECT_EQ(test_model.oneof_member2(), false); + EXPECT_EQ(TestModel::CHILD_ONEOF1_NOT_SET, test_model.child_oneof1_case()); } TEST(CppGeneratedCode, Messages) { @@ -619,7 +624,7 @@ TEST(CppGeneratedCode, SharedPointer) { } TEST(CppGeneratedCode, UniquePointer) { - std::unique_ptr model = std::make_unique(); + auto model = std::make_unique(); ::upb::Arena arena; auto bytes = protos::Serialize(model, arena); EXPECT_TRUE(protos::Parse(model, bytes.value()));