Add `{,Dynamic,Unbound}FieldSkipper()`.
It becomes increasingly common to do nothing in a field handler besides tracking
field presence. It is annoying to write lambdas which takes the value, context
parameters, and just returns `absl::OkStatus()`, and it feels uncertain whether
taking and ignoring the value is as efficient as it could (it almost is;
arranging for this to perform varint skipping instead of varint parsing is not
worth complicating the field handler protocol).
In contrast to `DynamicFieldCopier()`, `DynamicFieldSkipper()` field predicate
returns `bool` instead of `std::optional<int>`, because returning a destination
field number is not applicable.
In contrast to `AnyFieldCopier()`, `AnyFieldSkipper()` is not provided because
skipping remaining fields is the implicit default.
In contrast to `UnboundFieldCopier()`, `UnboundFieldSkipper()` can reuse the
implementation of `FieldSkipper()`, because it does not need a destination field
number.
Also, make public `FieldAccepted` and `FieldAcceptedIf` for dynamic field
handlers which carry no extra information about the field being accepted. The
field handler protocol requires an API which fits `std::unique_ptr` and
`std::optional` but not `bool`. These types are extracted from a private
implementation detail of `DynamicFieldHandler()`. They are used by
`DynamicFieldSkipper()` too.
Also, fix embarrassing compile errors in `FieldHandlerWrapper()` for dynamic
field handlers.
PiperOrigin-RevId: 914263217
diff --git a/riegeli/messages/BUILD b/riegeli/messages/BUILD
index 459a3ae..90338f2 100644
--- a/riegeli/messages/BUILD
+++ b/riegeli/messages/BUILD
@@ -237,6 +237,21 @@
)
cc_library(
+ name = "field_skipper",
+ hdrs = ["field_skipper.h"],
+ deps = [
+ ":message_wire_format",
+ ":serialized_message_reader",
+ "//riegeli/base:cord_iterator_span",
+ "//riegeli/bytes:limiting_reader",
+ "@com_google_absl//absl/base:core_headers",
+ "@com_google_absl//absl/base:nullability",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/strings:string_view",
+ ],
+)
+
+cc_library(
name = "field_copier",
hdrs = ["field_copier.h"],
deps = [
diff --git a/riegeli/messages/dynamic_field_handler.h b/riegeli/messages/dynamic_field_handler.h
index 7919f68..109387f 100644
--- a/riegeli/messages/dynamic_field_handler.h
+++ b/riegeli/messages/dynamic_field_handler.h
@@ -53,23 +53,6 @@
// Implementation details follow.
template <typename BaseFieldHandler>
class DynamicFieldHandlerType {
- private:
- struct Accepted {};
-
- class MaybeAccepted {
- public:
- explicit MaybeAccepted(bool accepted) : accepted_(accepted) {}
-
- MaybeAccepted(const MaybeAccepted& that) = default;
-
- explicit operator bool() const { return accepted_; }
-
- Accepted operator*() const { return Accepted(); }
-
- private:
- bool accepted_;
- };
-
public:
template <
typename BaseFieldHandlerInitializer,
@@ -87,8 +70,8 @@
static constexpr int kFieldNumber = kDynamicFieldNumber;
ABSL_ATTRIBUTE_ALWAYS_INLINE
- MaybeAccepted AcceptVarint(int field_number) const {
- return MaybeAccepted(field_number == field_number_);
+ FieldAcceptedIf AcceptVarint(int field_number) const {
+ return FieldAcceptedIf(field_number == field_number_);
}
template <
@@ -100,14 +83,14 @@
serialized_message_reader_internal::IsStaticFieldHandlerForVarint<
BaseFieldHandler, Context...>>,
int> = 0>
- absl::Status DynamicHandleVarint(Accepted, uint64_t repr,
+ absl::Status DynamicHandleVarint(FieldAccepted, uint64_t repr,
Context&... context) const {
return base_field_handler_.HandleVarint(repr, context...);
}
ABSL_ATTRIBUTE_ALWAYS_INLINE
- MaybeAccepted AcceptFixed32(int field_number) const {
- return MaybeAccepted(field_number == field_number_);
+ FieldAcceptedIf AcceptFixed32(int field_number) const {
+ return FieldAcceptedIf(field_number == field_number_);
}
template <
@@ -119,14 +102,14 @@
serialized_message_reader_internal::
IsStaticFieldHandlerForFixed32<BaseFieldHandler, Context...>>,
int> = 0>
- absl::Status DynamicHandleFixed32(Accepted, uint32_t repr,
+ absl::Status DynamicHandleFixed32(FieldAccepted, uint32_t repr,
Context&... context) const {
return base_field_handler_.HandleFixed32(repr, context...);
}
ABSL_ATTRIBUTE_ALWAYS_INLINE
- MaybeAccepted AcceptFixed64(int field_number) const {
- return MaybeAccepted(field_number == field_number_);
+ FieldAcceptedIf AcceptFixed64(int field_number) const {
+ return FieldAcceptedIf(field_number == field_number_);
}
template <
@@ -138,14 +121,14 @@
serialized_message_reader_internal::
IsStaticFieldHandlerForFixed64<BaseFieldHandler, Context...>>,
int> = 0>
- absl::Status DynamicHandleFixed64(Accepted, uint64_t repr,
+ absl::Status DynamicHandleFixed64(FieldAccepted, uint64_t repr,
Context&... context) const {
return base_field_handler_.HandleFixed64(repr, context...);
}
- ABSL_ATTRIBUTE_ALWAYS_INLINE MaybeAccepted
+ ABSL_ATTRIBUTE_ALWAYS_INLINE FieldAcceptedIf
AcceptLengthDelimited(int field_number) const {
- return MaybeAccepted(field_number == field_number_);
+ return FieldAcceptedIf(field_number == field_number_);
}
template <typename... Context,
@@ -158,7 +141,7 @@
BaseFieldHandler, Context...>>,
int> = 0>
absl::Status DynamicHandleLengthDelimitedFromReader(
- Accepted, ReaderSpan<> repr, Context&... context) const {
+ FieldAccepted, ReaderSpan<> repr, Context&... context) const {
return base_field_handler_.HandleLengthDelimitedFromReader(std::move(repr),
context...);
}
@@ -172,7 +155,7 @@
IsStaticFieldHandlerForLengthDelimitedFromCord<
BaseFieldHandler, Context...>>,
int> = 0>
- absl::Status DynamicHandleLengthDelimitedFromCord(Accepted,
+ absl::Status DynamicHandleLengthDelimitedFromCord(FieldAccepted,
CordIteratorSpan repr,
std::string& scratch,
Context&... context) const {
@@ -190,14 +173,14 @@
BaseFieldHandler, Context...>>,
int> = 0>
absl::Status DynamicHandleLengthDelimitedFromString(
- Accepted, absl::string_view repr, Context&... context) const {
+ FieldAccepted, absl::string_view repr, Context&... context) const {
return base_field_handler_.HandleLengthDelimitedFromString(repr,
context...);
}
ABSL_ATTRIBUTE_ALWAYS_INLINE
- MaybeAccepted AcceptStartGroup(int field_number) const {
- return MaybeAccepted(field_number == field_number_);
+ FieldAcceptedIf AcceptStartGroup(int field_number) const {
+ return FieldAcceptedIf(field_number == field_number_);
}
template <typename... Context,
@@ -209,13 +192,14 @@
IsStaticFieldHandlerForStartGroup<BaseFieldHandler,
Context...>>,
int> = 0>
- absl::Status DynamicHandleStartGroup(Accepted, Context&... context) const {
+ absl::Status DynamicHandleStartGroup(FieldAccepted,
+ Context&... context) const {
return base_field_handler_.HandleStartGroup(context...);
}
ABSL_ATTRIBUTE_ALWAYS_INLINE
- MaybeAccepted AcceptEndGroup(int field_number) const {
- return MaybeAccepted(field_number == field_number_);
+ FieldAcceptedIf AcceptEndGroup(int field_number) const {
+ return FieldAcceptedIf(field_number == field_number_);
}
template <typename... Context,
@@ -227,7 +211,7 @@
IsStaticFieldHandlerForEndGroup<BaseFieldHandler,
Context...>>,
int> = 0>
- absl::Status DynamicHandleEndGroup(Accepted, Context&... context) const {
+ absl::Status DynamicHandleEndGroup(FieldAccepted, Context&... context) const {
return base_field_handler_.HandleEndGroup(context...);
}
diff --git a/riegeli/messages/field_skipper.h b/riegeli/messages/field_skipper.h
new file mode 100644
index 0000000..4330706
--- /dev/null
+++ b/riegeli/messages/field_skipper.h
@@ -0,0 +1,321 @@
+// Copyright 2026 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef RIEGELI_MESSAGES_FIELD_SKIPPER_H_
+#define RIEGELI_MESSAGES_FIELD_SKIPPER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <type_traits>
+#include <utility>
+
+#include "absl/base/attributes.h"
+#include "absl/base/nullability.h"
+#include "absl/status/status.h"
+#include "absl/strings/string_view.h"
+#include "riegeli/base/cord_iterator_span.h"
+#include "riegeli/bytes/limiting_reader.h"
+#include "riegeli/messages/message_wire_format.h"
+#include "riegeli/messages/serialized_message_reader.h"
+
+ABSL_POINTERS_DEFAULT_NONNULL
+
+namespace riegeli {
+
+// The type returned by `FieldSkipper()` and `UnboundFieldSkipper()`.
+template <int field_number, WireTypeSet wire_types = AllWireTypes>
+class FieldSkipperType;
+
+// The type returned by `DynamicFieldSkipper()`.
+template <typename Accept, WireTypeSet wire_types = AllWireTypes>
+class DynamicFieldSkipperType;
+
+// A field handler for `SerializedMessageReader` which ignores the given field.
+//
+// This can be used together with `FieldTracker()` or
+// `OnlyFirstFieldThenCancel()` to detect field presence without otherwise
+// processing its value.
+//
+// As an optimization, `wire_types` constrains the set of wire types to handle.
+// This yields smaller and faster code.
+template <int field_number, WireTypeSet wire_types = AllWireTypes>
+constexpr FieldSkipperType<field_number, wire_types> FieldSkipper() {
+ return FieldSkipperType<field_number, wire_types>();
+}
+
+// A field handler for `SerializedMessageReader` which ignores the given field,
+// with the predicate over field numbers specified at runtime.
+//
+// This can be used together with `FieldTracker()` or
+// `OnlyFirstFieldThenCancel()` to detect field presence without otherwise
+// processing its value.
+//
+// `accept` is an invocable taking the field number as `int` and returning
+// `bool`. If it returns `true`, the field is ignored.
+template <WireTypeSet wire_types = AllWireTypes, typename Accept>
+constexpr DynamicFieldSkipperType<std::decay_t<Accept>, wire_types>
+DynamicFieldSkipper(Accept&& accept) {
+ return DynamicFieldSkipperType<std::decay_t<Accept>, wire_types>(
+ std::forward<Accept>(accept));
+}
+
+// An unbound field handler for a `DynamicFieldHandler` or `FieldHandlerMap`
+// which ignores the current field, with the field number specified at runtime.
+//
+// This can be used together with `FieldTracker()` or
+// `OnlyFirstFieldThenCancel()` to detect field presence without otherwise
+// processing its value.
+//
+// As an optimization, `wire_types` constrains the set of wire types to handle.
+// This yields smaller and faster code.
+template <WireTypeSet wire_types = AllWireTypes>
+constexpr FieldSkipperType<kUnboundFieldNumber, wire_types>
+UnboundFieldSkipper() {
+ return FieldSkipperType<kUnboundFieldNumber, wire_types>();
+}
+
+// Implementation details follow.
+
+template <int field_number, WireTypeSet wire_types>
+class FieldSkipperType {
+ public:
+ static constexpr int kFieldNumber = field_number;
+
+ constexpr FieldSkipperType() = default;
+
+ FieldSkipperType(const FieldSkipperType& that) = default;
+ FieldSkipperType& operator=(const FieldSkipperType& that) = default;
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kVarint) ==
+ WireTypeSet::kVarint,
+ int> = 0>
+ absl::Status HandleVarint(uint64_t /*repr*/, Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kFixed32) ==
+ WireTypeSet::kFixed32,
+ int> = 0>
+ absl::Status HandleFixed32(uint32_t /*repr*/, Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kFixed64) ==
+ WireTypeSet::kFixed64,
+ int> = 0>
+ absl::Status HandleFixed64(uint64_t /*repr*/, Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kLengthDelimited) ==
+ WireTypeSet::kLengthDelimited,
+ int> = 0>
+ absl::Status HandleLengthDelimitedFromReader(ReaderSpan<> repr,
+ Context&... /*context*/) const {
+ return riegeli::SkipLengthDelimitedFromReader(std::move(repr));
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kLengthDelimited) ==
+ WireTypeSet::kLengthDelimited,
+ int> = 0>
+ absl::Status HandleLengthDelimitedFromCord(CordIteratorSpan repr,
+ std::string& /*scratch*/,
+ Context&... /*context*/) const {
+ return riegeli::SkipLengthDelimitedFromCord(std::move(repr));
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kLengthDelimited) ==
+ WireTypeSet::kLengthDelimited,
+ int> = 0>
+ absl::Status HandleLengthDelimitedFromString(absl::string_view /*repr*/,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kStartGroup) ==
+ WireTypeSet::kStartGroup,
+ int> = 0>
+ absl::Status HandleStartGroup(Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kEndGroup) ==
+ WireTypeSet::kEndGroup,
+ int> = 0>
+ absl::Status HandleEndGroup(Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+};
+
+template <typename Accept, WireTypeSet wire_types>
+class DynamicFieldSkipperType {
+ public:
+ static constexpr int kFieldNumber = kDynamicFieldNumber;
+
+ template <typename AcceptInitializer,
+ std::enable_if_t<std::is_convertible_v<AcceptInitializer&&, Accept>,
+ int> = 0>
+ explicit constexpr DynamicFieldSkipperType(AcceptInitializer&& accept)
+ : accept_(std::forward<AcceptInitializer>(accept)) {}
+
+ DynamicFieldSkipperType() = default;
+
+ DynamicFieldSkipperType(const DynamicFieldSkipperType& that) = default;
+ DynamicFieldSkipperType& operator=(const DynamicFieldSkipperType& that) =
+ default;
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kVarint) ==
+ WireTypeSet::kVarint,
+ int> = 0>
+ FieldAcceptedIf AcceptVarint(int field_number) const {
+ return FieldAcceptedIf(accept_(field_number));
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kVarint) ==
+ WireTypeSet::kVarint,
+ int> = 0>
+ absl::Status DynamicHandleVarint(FieldAccepted, uint64_t /*repr*/,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kFixed32) ==
+ WireTypeSet::kFixed32,
+ int> = 0>
+ FieldAcceptedIf AcceptFixed32(int field_number) const {
+ return FieldAcceptedIf(accept_(field_number));
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kFixed32) ==
+ WireTypeSet::kFixed32,
+ int> = 0>
+ absl::Status DynamicHandleFixed32(FieldAccepted, uint32_t /*repr*/,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kFixed64) ==
+ WireTypeSet::kFixed64,
+ int> = 0>
+ FieldAcceptedIf AcceptFixed64(int field_number) const {
+ return FieldAcceptedIf(accept_(field_number));
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kFixed64) ==
+ WireTypeSet::kFixed64,
+ int> = 0>
+ absl::Status DynamicHandleFixed64(FieldAccepted, uint64_t /*repr*/,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context>
+ FieldAcceptedIf AcceptLengthDelimited(int field_number) const {
+ return FieldAcceptedIf(accept_(field_number));
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kLengthDelimited) ==
+ WireTypeSet::kLengthDelimited,
+ int> = 0>
+ absl::Status DynamicHandleLengthDelimitedFromReader(
+ FieldAccepted, ReaderSpan<> repr, Context&... /*context*/) const {
+ return riegeli::SkipLengthDelimitedFromReader(std::move(repr));
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kLengthDelimited) ==
+ WireTypeSet::kLengthDelimited,
+ int> = 0>
+ absl::Status DynamicHandleLengthDelimitedFromCord(
+ FieldAccepted, CordIteratorSpan repr, std::string& /*scratch*/,
+ Context&... /*context*/) const {
+ return riegeli::SkipLengthDelimitedFromCord(std::move(repr));
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kLengthDelimited) ==
+ WireTypeSet::kLengthDelimited,
+ int> = 0>
+ absl::Status DynamicHandleLengthDelimitedFromString(
+ FieldAccepted, absl::string_view /*repr*/,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kStartGroup) ==
+ WireTypeSet::kStartGroup,
+ int> = 0>
+ FieldAcceptedIf AcceptStartGroup(int field_number) const {
+ return FieldAcceptedIf(accept_(field_number));
+ }
+
+ template <
+ typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kStartGroup) ==
+ WireTypeSet::kStartGroup,
+ int> = 0>
+ absl::Status DynamicHandleStartGroup(FieldAccepted,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kEndGroup) ==
+ WireTypeSet::kEndGroup,
+ int> = 0>
+ FieldAcceptedIf AcceptEndGroup(int field_number) const {
+ return FieldAcceptedIf(accept_(field_number));
+ }
+
+ template <typename... Context, WireTypeSet dependent_wire_types = wire_types,
+ std::enable_if_t<(dependent_wire_types & WireTypeSet::kEndGroup) ==
+ WireTypeSet::kEndGroup,
+ int> = 0>
+ absl::Status DynamicHandleEndGroup(FieldAccepted,
+ Context&... /*context*/) const {
+ return absl::OkStatus();
+ }
+
+ private:
+ ABSL_ATTRIBUTE_NO_UNIQUE_ADDRESS Accept accept_;
+};
+
+} // namespace riegeli
+
+#endif // RIEGELI_MESSAGES_FIELD_SKIPPER_H_
diff --git a/riegeli/messages/fields_found.h b/riegeli/messages/fields_found.h
index b10c899..e846bfe 100644
--- a/riegeli/messages/fields_found.h
+++ b/riegeli/messages/fields_found.h
@@ -476,8 +476,8 @@
Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleVarint(std::forward<Accepted>(accepted),
- repr, context...);
+ return field_handler_.DynamicHandleVarint(
+ std::forward<Accepted>(accepted), repr, context...);
},
[] { return absl::OkStatus(); }, context...);
}
@@ -513,8 +513,8 @@
Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleFixed32(std::forward<Accepted>(accepted),
- repr, context...);
+ return field_handler_.DynamicHandleFixed32(
+ std::forward<Accepted>(accepted), repr, context...);
},
[] { return absl::OkStatus(); }, context...);
}
@@ -550,8 +550,8 @@
Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleFixed64(std::forward<Accepted>(accepted),
- repr, context...);
+ return field_handler_.DynamicHandleFixed64(
+ std::forward<Accepted>(accepted), repr, context...);
},
[] { return absl::OkStatus(); }, context...);
}
@@ -626,7 +626,7 @@
Accepted&& accepted, ReaderSpan<> repr, Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleLengthDelimitedFromReader(
+ return field_handler_.DynamicHandleLengthDelimitedFromReader(
std::forward<Accepted>(accepted), std::move(repr), context...);
},
[&] { return SkipLengthDelimitedFromReader(std::move(repr)); },
@@ -645,7 +645,7 @@
Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleLengthDelimitedFromCord(
+ return field_handler_.DynamicHandleLengthDelimitedFromCord(
std::forward<Accepted>(accepted), std::move(repr), scratch,
context...);
},
@@ -663,7 +663,7 @@
Accepted&& accepted, absl::string_view repr, Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleLengthDelimitedFromString(
+ return field_handler_.DynamicHandleLengthDelimitedFromString(
std::forward<Accepted>(accepted), repr, context...);
},
[] { return absl::OkStatus(); }, context...);
@@ -699,7 +699,7 @@
Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleStartGroup(
+ return field_handler_.DynamicHandleStartGroup(
std::forward<Accepted>(accepted), context...);
},
[] { return absl::OkStatus(); }, context...);
@@ -736,8 +736,8 @@
Context&... context) const {
return action_wrapper_(
[&] {
- return field_handler_.HandleEndGroup(std::forward<Accepted>(accepted),
- context...);
+ return field_handler_.DynamicHandleEndGroup(
+ std::forward<Accepted>(accepted), context...);
},
[] { return absl::OkStatus(); }, context...);
}
diff --git a/riegeli/messages/serialized_message_reader.h b/riegeli/messages/serialized_message_reader.h
index 510abf4..9b06801 100644
--- a/riegeli/messages/serialized_message_reader.h
+++ b/riegeli/messages/serialized_message_reader.h
@@ -228,10 +228,10 @@
//
// `MaybeAccepted` is some type explicitly convertible to `bool`, with
// `operator*` returning some `Accepted` type. `MaybeAccepted` can be e.g.
-// `std::optional<Accepted>` or `Accepted*`. If `AcceptX()` returns a value
-// explicitly convertible to `true`, then the field is accepted, and the
-// corresponding `HandleX()` function is called with the result of `operator*`
-// as the first argument.
+// `std::optional<Accepted>`, `Accepted*`, or `FieldAcceptedIf`. If `AcceptX()`
+// returns a value explicitly convertible to `true`, then the field is accepted,
+// and the corresponding `HandleX()` function is called with the result of
+// `operator*` as the first argument.
// In `FieldHandler::kFieldNumber`, marks a dynamic field handler.
inline constexpr int kDynamicFieldNumber =
@@ -572,6 +572,31 @@
absl::Status SkipLengthDelimitedFromCord(CordIteratorSpan value);
+// An `Accepted` type for a dynamic field handler which does not carry any
+// information about the field being accepted.
+//
+// This is used as the first parameter of a `DynamicHandleX()` function.
+struct FieldAccepted {};
+
+// A `MaybeAccepted` type for a dynamic field handler which does not carry any
+// information about the field being accepted.
+//
+// This is used as the result of an `AcceptX()` function.
+class FieldAcceptedIf {
+ public:
+ explicit FieldAcceptedIf(bool accepted) : accepted_(accepted) {}
+
+ FieldAcceptedIf(const FieldAcceptedIf& that) = default;
+ FieldAcceptedIf& operator=(const FieldAcceptedIf& that) = default;
+
+ explicit operator bool() const { return accepted_; }
+
+ FieldAccepted operator*() const { return FieldAccepted(); }
+
+ private:
+ bool accepted_;
+};
+
// Implementation details follow.
template <typename T, typename... Context>