From 5e68fa489fc1df2d3207ce77b07add0a8bf0dbe3 Mon Sep 17 00:00:00 2001 From: PragmaTwice Date: Tue, 3 Dec 2024 16:44:00 +0800 Subject: [PATCH] feat: add merge_mode for field merging --- include/protopuf/field.h | 26 +++++++++++++++++++++++--- include/protopuf/message.h | 8 ++++---- test/message.cpp | 7 +++++++ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/include/protopuf/field.h b/include/protopuf/field.h index 26f309f..94249e6 100644 --- a/include/protopuf/field.h +++ b/include/protopuf/field.h @@ -290,12 +290,32 @@ namespace pp { } } + struct merge_mode { + enum class singular { + override, + assign_if_empty, + } singular_mode; + + enum class repeated { + push_back, + } repeated_mode; + + constexpr merge_mode(singular s = singular::override, repeated r = repeated::push_back) + : singular_mode(s), repeated_mode(r) {} + }; + /// Merge a field into another field: overwrite if it is singular and non-empty, merge to end otherwise - template requires field_c> + template requires field_c> constexpr void merge_field(D& f, S&& v) { if constexpr (D::attr == singular) { - if (!empty_field(v)) { - f = std::forward(v); + if constexpr (mode.singular_mode == merge_mode::singular::override) { + if (!empty_field(v)) { + f = std::forward(v); + } + } else { + if (!empty_field(v) && empty_field(f)) { + f = std::forward(v); + } } } else { auto inserter = std::inserter(f, f.end()); diff --git a/include/protopuf/message.h b/include/protopuf/message.h index 1e7e542..6906cca 100644 --- a/include/protopuf/message.h +++ b/include/protopuf/message.h @@ -226,9 +226,9 @@ namespace pp { /// @brief Merge another message into this message, for all fields: overwrite if it is singular and non-empty, merge to end otherwise /// /// same as `merge_field(field1, other.field1), ..., merge_field(fieldN, other.fieldN)`, ref to @ref merge_field - template requires std::same_as, message> + template requires std::same_as, message> constexpr void merge(M&& other) { - (merge_field(static_cast(*this), static_cast>(other)), ...); + (merge_field(static_cast(*this), static_cast>(other)), ...); } }; @@ -244,13 +244,13 @@ namespace pp { concept message_c = is_message; /// Merge multiple messages into one message, and return the merged message - template + template requires message_c> && are_same, std::remove_cvref_t...> constexpr auto merge(M1&& msg1, MN&& ... msgN) { std::remove_cvref_t res = std::forward(msg1); - (res.merge(std::forward(msgN)), ...); + (res.template merge(std::forward(msgN)), ...); return res; } diff --git a/test/message.cpp b/test/message.cpp index 68a2406..31ad2fb 100644 --- a/test/message.cpp +++ b/test/message.cpp @@ -474,6 +474,13 @@ GTEST_TEST(message, merge) { EXPECT_EQ(merged["name"_f], "e"); EXPECT_EQ(merged["titles"_f], (vector{"b", "c", "d", "f"})); EXPECT_EQ(merged["age"_f], 124); + + constexpr merge_mode mode{merge_mode::singular::assign_if_empty}; + merged = merge(merged, Person{"a", {}, {}}); + EXPECT_EQ(merged["name"_f], "e"); + + merged = merge(Person{{}, {}, {}}, Person{"a", {}, {}}); + EXPECT_EQ(merged["name"_f], "a"); } GTEST_TEST(message_coder, encode_with_insufficient_buffer_size) {