Skip to content

Commit

Permalink
feat: add merge_mode for field merging
Browse files Browse the repository at this point in the history
  • Loading branch information
PragmaTwice committed Dec 3, 2024
1 parent 6f17bf4 commit 5e68fa4
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 7 deletions.
26 changes: 23 additions & 3 deletions include/protopuf/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <field_c D, typename S> requires field_c<std::remove_cvref_t<S>>
template <merge_mode mode = merge_mode{}, field_c D, typename S> requires field_c<std::remove_cvref_t<S>>
constexpr void merge_field(D& f, S&& v) {
if constexpr (D::attr == singular) {
if (!empty_field(v)) {
f = std::forward<S>(v);
if constexpr (mode.singular_mode == merge_mode::singular::override) {
if (!empty_field(v)) {
f = std::forward<S>(v);
}
} else {
if (!empty_field(v) && empty_field(f)) {
f = std::forward<S>(v);
}
}
} else {
auto inserter = std::inserter(f, f.end());
Expand Down
8 changes: 4 additions & 4 deletions include/protopuf/message.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename M> requires std::same_as<std::remove_cvref_t<M>, message>
template <merge_mode mode = merge_mode{}, typename M> requires std::same_as<std::remove_cvref_t<M>, message>
constexpr void merge(M&& other) {
(merge_field(static_cast<T&>(*this), static_cast<type_forward<T, M&&>>(other)), ...);
(merge_field<mode>(static_cast<T&>(*this), static_cast<type_forward<T, M&&>>(other)), ...);
}
};

Expand All @@ -244,13 +244,13 @@ namespace pp {
concept message_c = is_message<T>;

/// Merge multiple messages into one message, and return the merged message
template <typename M1, typename... MN>
template <merge_mode mode = merge_mode{}, typename M1, typename... MN>
requires message_c<std::remove_cvref_t<M1>> &&
are_same<std::remove_cvref_t<M1>, std::remove_cvref_t<MN>...>
constexpr auto merge(M1&& msg1, MN&& ... msgN) {
std::remove_cvref_t<M1> res = std::forward<M1>(msg1);

(res.merge(std::forward<MN>(msgN)), ...);
(res.template merge<mode>(std::forward<MN>(msgN)), ...);

return res;
}
Expand Down
7 changes: 7 additions & 0 deletions test/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,13 @@ GTEST_TEST(message, merge) {
EXPECT_EQ(merged["name"_f], "e");
EXPECT_EQ(merged["titles"_f], (vector<string>{"b", "c", "d", "f"}));
EXPECT_EQ(merged["age"_f], 124);

constexpr merge_mode mode{merge_mode::singular::assign_if_empty};
merged = merge<mode>(merged, Person{"a", {}, {}});
EXPECT_EQ(merged["name"_f], "e");

merged = merge<mode>(Person{{}, {}, {}}, Person{"a", {}, {}});
EXPECT_EQ(merged["name"_f], "a");
}

GTEST_TEST(message_coder, encode_with_insufficient_buffer_size) {
Expand Down

0 comments on commit 5e68fa4

Please sign in to comment.