blob: 5764e562ac75c5a1c6ae6b27183ca2aff6f8ba0e [file]
#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_PROXY_H__
#define GOOGLE_PROTOBUF_REPEATED_FIELD_PROXY_H__
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/log/absl_check.h"
#include "absl/strings/cord.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/raw_ptr.h"
#include "google/protobuf/repeated_field.h"
#include "google/protobuf/repeated_field_proxy_iterator.h"
#include "google/protobuf/repeated_field_proxy_traits.h"
#include "google/protobuf/repeated_ptr_field.h"
// Must be included last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
template <typename ElementType>
class RepeatedFieldProxy;
namespace internal {
template <typename ElementType>
class RepeatedFieldOrProxy;
template <typename ElementType, bool kOrProxy>
class MutableRepeatedFieldProxyImpl;
template <typename ElementType, bool kOrProxy>
class ConstRepeatedFieldProxyImpl;
template <typename ElementType, bool kOrProxy>
class RepeatedFieldProxyInternalPrivateAccessHelper;
namespace string_util {
template <typename StringType, typename T>
inline void CopyToString(StringType& element, T&& value) {
// We want to explicitly enumerate all types we accept for constructing
// strings, otherwise this would be a subtle place we'd be leaking the
// `std::string` backing of `string_view` repeated fields. I.e. whatever new
// backing we use for `string_view` fields would have to support all
// `operator=` overloads that `std::string` has.
//
// With an explicit list, we no longer have this dependency.
if constexpr (std::is_convertible_v<T, absl::string_view>) {
element = absl::implicit_cast<absl::string_view>(value);
} else if constexpr (std::is_convertible_v<T, const std::string&>) {
element = absl::implicit_cast<const std::string&>(value);
} else if constexpr (std::is_convertible_v<T, const char*>) {
element = absl::implicit_cast<const char*>(value);
} else {
element = {value.data(), value.size()};
}
}
template <typename T>
inline void SetElement(std::string& element, T&& value) {
if constexpr (std::is_same_v<T&&, std::string&&>) {
element = std::forward<T>(value);
} else if constexpr (std::is_convertible_v<T, const absl::Cord&>) {
const absl::Cord& cord = std::forward<T>(value);
absl::CopyCordToString(cord, &element);
} else {
CopyToString(element, std::forward<T>(value));
}
}
template <typename T>
inline void SetElement(absl::Cord& element, T&& value) {
if constexpr (std::is_same_v<T&&, absl::Cord&&>) {
element = std::forward<T>(value);
} else if constexpr (std::is_convertible_v<T, const absl::Cord&>) {
element = absl::implicit_cast<const absl::Cord&>(std::forward<T>(value));
} else {
CopyToString(element, std::forward<T>(value));
}
}
} // namespace string_util
// The base class for both mutable and const repeated field proxies. Implements
// all of the common methods and dependent types for both classes.
//
// Class structure:
//
// internal {- - - - - - - - - - - - - - - - - - - - - - -
// RepeatedFieldProxyBase
// | |
// MutableRepeatedFieldProxyImpl |
// | |
// | ConstRepeatedFieldProxyImpl
// } - - - - - -|- - - - - - - - - - - - | - - - - - - - -
// RepeatedFieldProxy<E>, |
// RepeatedFieldOrProxy<E> |
// RepeatedFieldProxy<const E>,
// RepeatedFieldOrProxy<const E>
//
// RepeatedFieldProxyBase has the common implementation and interface between
// both mutable and const proxies, and `Mutable*Impl` and `Const*Impl` have the
// const/mutable-specific implementations + interfaces.
//
// Both `RepeatedFieldProxy` and `RepeatedFieldOrProxy` comprise the public
// API and are thin wrappers around `Mutable*Impl` and `Const*Impl`,
// implementing only the constructors and APIs particular to them.
template <typename ElementType, bool kOrProxy>
class RepeatedFieldProxyBase {
protected:
// If true, this is a view into a repeated field, meaning neither the elements
// nor the container can be modified. If false, both the elements and the
// container can be modified.
static constexpr bool kIsConst = std::is_const_v<ElementType>;
using Traits = RepeatedFieldTraits<std::remove_const_t<ElementType>>;
using RepeatedFieldType = typename Traits::type;
using ConstQualifiedRepeatedFieldType =
std::conditional_t<kIsConst, const RepeatedFieldType, RepeatedFieldType>;
public:
using value_type = std::remove_const_t<ElementType>;
using size_type = size_t;
using difference_type = ptrdiff_t;
using const_reference = typename Traits::const_reference;
using const_iterator =
internal::RepeatedFieldProxyIteratorImpl<const ElementType,
/*kReverse=*/false, kOrProxy>;
using iterator =
internal::RepeatedFieldProxyIteratorImpl<ElementType,
/*kReverse=*/false, kOrProxy>;
using const_reverse_iterator =
internal::RepeatedFieldProxyIteratorImpl<const ElementType,
/*kReverse=*/true, kOrProxy>;
using reverse_iterator =
internal::RepeatedFieldProxyIteratorImpl<ElementType,
/*kReverse=*/true, kOrProxy>;
// Allow explicit conversion to the backing repeated field type. This will
// perform a deep copy of the repeated field backed by this proxy.
//
// Note that this exposes `RepeatedFieldType`, but will be kept around for
// backwards compatibility with code that uses `RepeatedField`s or
// `RepeatedPtrField`s directly. We may freely change `RepeatedFieldType` so
// long as we maintain this explicit conversion to the legacy container types.
explicit operator RepeatedFieldType() const {
return RepeatedFieldType(field());
}
RepeatedFieldProxyBase(const RepeatedFieldProxyBase&) = default;
// Assignment of repeated field proxies simply rebinds the proxy to a
// different repeated field. It does not modify the underlying field.
//
// Copying should be done through `assign()`.
RepeatedFieldProxyBase& operator=(const RepeatedFieldProxyBase&) = default;
~RepeatedFieldProxyBase() = default;
// Returns true if the repeated field has no elements (size == 0).
[[nodiscard]] bool empty() const { return field().empty(); }
// Returns the number of elements in the repeated field.
[[nodiscard]] size_type size() const {
return static_cast<size_type>(field().size());
}
// Returns a const reference or view into the element at the given index,
// performing bounds checking in accordance with `bounds_check_mode_*`.
[[nodiscard]] const_reference get(size_type index) const {
return field()[index];
}
[[nodiscard]] const_iterator cbegin() const { return begin(); }
[[nodiscard]] const_iterator cend() const { return end(); }
[[nodiscard]] iterator begin() const { return iterator(field().begin()); }
[[nodiscard]] iterator end() const { return iterator(field().end()); }
[[nodiscard]] reverse_iterator rbegin() const {
return reverse_iterator(end());
}
[[nodiscard]] reverse_iterator rend() const {
return reverse_iterator(begin());
}
protected:
explicit RepeatedFieldProxyBase(ConstQualifiedRepeatedFieldType& field)
: field_(&field) {}
ConstQualifiedRepeatedFieldType& field() const { return *field_; }
private:
ConstQualifiedRepeatedFieldType* PROTOBUF_NONNULL field_;
};
// The following classes are used to specialize methods of `RepeatedFieldProxy`
// based on the element type. Most methods do not need specialization, since
// they look similar for all element types, maybe only differing in whether
// `const_reference` resolves to a `const T&` or some value type like
// `absl::string_view`.
//
// For methods that do have a different signature based on the element type, we
// make a `*With<MethodName>` class that defines only that method, specialized
// on the element type using whatever conditions make sense for the method. We
// then inherit from this type in `RepeatedFieldProxy`.
// Defines `set()` for primitive element types, which only take by value.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithSet {
public:
// Sets the element at the given index to the given value.
//
// Performs bounds checking in accordance with `bounds_check_mode_*`.
void set(size_t index, ElementType value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
field[index] = value;
}
};
// Defines `set()` for message element types, which take by const reference or
// rvalue.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithSet<
ElementType, kOrProxy,
std::enable_if_t<RepeatedElementTypeIsMessage<ElementType>>> {
public:
// Sets the element at the given index to the given value by move-assignment.
//
// Performs bounds checking in accordance with `bounds_check_mode_*`.
void set(size_t index, ElementType&& value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
field[index] = std::move(value);
}
// Sets the element at the given index to the given value by copy-assignment.
//
// Performs bounds checking in accordance with `bounds_check_mode_*`.
void set(size_t index, const ElementType& value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
field[index] = value;
}
};
// Defines `set()` for string element types, which dispatch to
// `string_util::SetElement` and accept many string-like types.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithSet<
ElementType, kOrProxy,
std::enable_if_t<RepeatedElementTypeIsString<ElementType>>> {
public:
// Sets the element at the given index to the given value.
//
// Performs bounds checking in accordance with `bounds_check_mode_*`.
template <typename T>
void set(size_t index, T&& value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
string_util::SetElement(field[index], std::forward<T>(value));
}
};
// Defines `push_back()` for primitive element types, which only take by value.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithPushBack {
public:
// Appends the given value to the end of the repeated field.
void push_back(ElementType value) const {
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>::Add(
this, value);
}
};
// Defines `push_back()` for message element types, which take by const
// reference or rvalue.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithPushBack<
ElementType, kOrProxy,
std::enable_if_t<RepeatedElementTypeIsMessage<ElementType>>> {
public:
// Appends the given value to the end of the repeated field by move
// construction/assignment.
void push_back(ElementType&& value) const {
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>::Add(
this, std::move(value));
}
// Appends the given value to the end of the repeated field by copy
// construction/assignment.
void push_back(const ElementType& value) const {
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>::Add(
this, value);
}
};
// Defines `push_back()` for string element types, which dispatch to
// `string_util::SetElement` and accept many string-like types.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithPushBack<
ElementType, kOrProxy,
std::enable_if_t<RepeatedElementTypeIsString<ElementType>>> {
public:
// Appends the given value to the end of the repeated field.
template <typename T>
void push_back(T&& value) const {
string_util::SetElement(
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::Add(this),
std::forward<T>(value));
}
};
// Defines `emplace_back()` for all types except `absl::string_view`. Simply
// takes any arguments that can be passed to the constructor of `ElementType`
// and in-place constructs the element at the end of the repeated field.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithEmplaceBack {
public:
// In-place constructs an element at the end of the repeated field, returning
// a reference to the newly constructed element.
template <typename... Args>
auto& emplace_back(Args&&... args) const {
return RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, kOrProxy>::Emplace(this, std::forward<Args>(args)...);
}
};
// Defines `emplace_back()` for `absl::string_view` element types. We explicitly
// list all constructors we want to support for repeated `string_views` to not
// leak the `std::string` backing of repeated `string_views`.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithEmplaceBack<
ElementType, kOrProxy,
std::enable_if_t<std::is_same_v<ElementType, absl::string_view>>> {
public:
// In-place constructs an element at the end of the repeated field, returning
// a string_view of the newly constructed element.
absl::string_view emplace_back() const {
return RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, kOrProxy>::Emplace(this);
}
// In-place constructs an element at the end of the repeated field, returning
// a string_view of the newly constructed element.
absl::string_view emplace_back(absl::string_view value) const {
return RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, kOrProxy>::Emplace(this, value);
}
// In-place constructs an element at the end of the repeated field, returning
// a string_view of the newly constructed element.
absl::string_view emplace_back(std::string&& value) const {
return RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, kOrProxy>::Emplace(this, std::move(value));
}
// In-place constructs an element at the end of the repeated field, returning
// a string_view of the newly constructed element.
absl::string_view emplace_back(const std::string& value) const {
return RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, kOrProxy>::Emplace(this, value);
}
// In-place constructs an element at the end of the repeated field, returning
// a string_view of the newly constructed element.
absl::string_view emplace_back(const char* PROTOBUF_NONNULL value) const {
return RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, kOrProxy>::Emplace(this, value);
}
};
// Defines `resize(new_size, value)` for all non-string repeated fields.
template <typename ElementType, bool kOrProxy, typename Enable = void>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithResize {
public:
// Resizes the repeated field to `new_size` elements. If `new_size` is smaller
// than the current size, the field is truncated. Otherwise, the field is
// extended with copies of `value`.
void resize(size_t new_size, const ElementType& value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
field.resize(new_size, value);
}
};
// Defines `resize(new_size, value)` for non-Cord string repeated fields.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithResize<
ElementType, kOrProxy,
std::enable_if_t<RepeatedElementTypeIsString<ElementType> &&
!std::is_same_v<ElementType, absl::Cord>>> {
public:
// Resizes the repeated field to `new_size` elements. If `new_size` is smaller
// than the current size, the field is truncated. Otherwise, the field is
// extended with copies of `value`.
void resize(size_t new_size, absl::string_view value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
field.resize(new_size, value);
}
};
// Defines `resize(new_size, value)` for repeated Cords.
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxyWithResize<
ElementType, kOrProxy,
std::enable_if_t<std::is_same_v<ElementType, absl::Cord>>> {
public:
// Resizes the repeated field to `new_size` elements. If `new_size` is smaller
// than the current size, the field is truncated. Otherwise, the field is
// extended with copies of `value`.
void resize(size_t new_size, const absl::Cord& value) const {
auto& field =
RepeatedFieldProxyInternalPrivateAccessHelper<ElementType,
kOrProxy>::field(this);
field.resize(new_size, value);
}
};
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES MutableRepeatedFieldProxyImpl
: public internal::RepeatedFieldProxyBase<ElementType, kOrProxy>,
public internal::RepeatedFieldProxyWithSet<ElementType, kOrProxy>,
public internal::RepeatedFieldProxyWithPushBack<ElementType, kOrProxy>,
public internal::RepeatedFieldProxyWithEmplaceBack<ElementType, kOrProxy>,
public internal::RepeatedFieldProxyWithResize<ElementType, kOrProxy> {
static_assert(!std::is_const_v<ElementType>);
protected:
using Base = internal::RepeatedFieldProxyBase<ElementType, kOrProxy>;
using typename Base::RepeatedFieldType;
using Base::field;
public:
using typename Base::const_iterator;
using typename Base::iterator;
using typename Base::size_type;
using reference =
typename internal::RepeatedFieldTraits<ElementType>::reference;
MutableRepeatedFieldProxyImpl(const MutableRepeatedFieldProxyImpl& other) =
default;
// Mutable proxies are not assignable. This is intentional to avoid confusion
// with the `assign` method, which reassigns the underlying repeated field.
MutableRepeatedFieldProxyImpl& operator=(
const MutableRepeatedFieldProxyImpl&) = delete;
// Returns a type which references the element at the given index. Performs
// bounds checking in accordance with `bounds_check_mode_*`.
[[nodiscard]] reference operator[](size_type index) const {
return field()[index];
}
// Removes the last element from the repeated field.
void pop_back() const { field().RemoveLast(); }
// Removes all elements from the repeated field. The field will be empty after
// this call.
void clear() const { field().Clear(); }
// Removes the element at `position` from the repeated field. Returns an
// iterator to the element immediately following the removed element.
iterator erase(const_iterator position) const {
// The internal iterator type may not match the proxy iterator type (for
// example for `absl::string_view` proxies which are backed by
// `std::string`). To avoid special casing, we will always cast to the
// internal iterator type before passing down to erase, then cast back to
// the proxy iterator type upon return. This conversion is redundant for
// types which have matching exposed and internal element types.
using const_internal_iterator = typename RepeatedFieldType::const_iterator;
return iterator(field().erase(const_internal_iterator(position)));
}
// Removes the elements in the range `[first, last)` from the repeated field.
// Returns an iterator to the element immediately following the last removed
// element.
iterator erase(const_iterator first, const_iterator last) const {
using const_internal_iterator = typename RepeatedFieldType::const_iterator;
return iterator(field().erase(const_internal_iterator(first),
const_internal_iterator(last)));
}
// Copy-assigns the elements in the range `[begin, end)` to the repeated
// field.
//
// If `begin` or `end` is an iterator into this repeated field, the behavior
// is undefined.
template <
typename Iter,
// A seemingly redundant verification that `Iter` is an iterator type.
// Even though we use `std::iterator_traits` below, we duplicate the
// condition here in case the implementation changes.
typename = std::void_t<typename std::iterator_traits<Iter>::value_type>>
auto assign(Iter begin, Iter end) const
// Verify that the iterator value type is assignable to `ElementType`.
// Pass through `push_back`, which is a catch-all for allowed conversions
// to the element type.
-> std::void_t<decltype(this->push_back(*begin))> {
field().Clear();
// Forward iterators in C++ are required to model `std::incrementable`,
// which means they are suitable for multi-pass algorithms, and therefore
// support `std::distance`.
if constexpr (std::is_base_of_v<
std::forward_iterator_tag,
typename std::iterator_traits<Iter>::iterator_category>) {
int distance = static_cast<int>(std::distance(begin, end));
field().ReserveWithArena(arena(), distance);
}
for (; begin != end; ++begin) {
this->push_back(*begin);
}
}
// A hint to the container to expect to grow/shrink to `new_size` elements.
// This may allow the container to make optimizations to avoid reallocations,
// but may also be ignored.
void reserve(size_type new_size) const {
field().ReserveWithArena(arena(), new_size);
}
// Resizes the repeated field to `new_size` elements. If `new_size` is smaller
// than the current size, the field is truncated. Otherwise, the field is
// extended with default-valued elements.
void resize(size_t new_size) const { field().resize(new_size); }
// Because we have an overload of `resize` in this class, we need to
// explicitly inherit the overload from the base class to avoid hiding it.
using internal::RepeatedFieldProxyWithResize<ElementType, kOrProxy>::resize;
protected:
MutableRepeatedFieldProxyImpl(RepeatedFieldType& field,
Arena* PROTOBUF_NULLABLE arena)
: Base(field), arena_(arena) {
ABSL_DCHECK_EQ(arena, field.GetArena());
}
Arena* PROTOBUF_NULLABLE arena() const { return arena_; }
// The following methods all forward to the backing repeated fields. This is
// done here for access to private members of the legacy containers, which
// only need to friend `MutableRepeatedFieldProxyImpl`.
auto& Add() const { return *field().AddWithArena(arena()); }
auto& Add(ElementType&& value) const {
return *field().AddWithArena(arena(), std::move(value));
}
auto& Add(const ElementType& value) const {
return *field().AddWithArena(arena(), value);
}
template <typename... Args>
auto& Emplace(Args&&... args) const {
return *field().EmplaceWithArena(arena(), std::forward<Args>(args)...);
}
private:
friend RepeatedFieldProxyInternalPrivateAccessHelper<ElementType, kOrProxy>;
Arena* PROTOBUF_NULLABLE const arena_;
};
template <typename ElementType, bool kOrProxy>
class PROTOBUF_DECLSPEC_EMPTY_BASES ConstRepeatedFieldProxyImpl
: public internal::RepeatedFieldProxyBase<const ElementType, kOrProxy> {
// A specialization of RepeatedFieldProxy for const proxies. This is needed
// for mutating methods to not be exposed on const proxies.
protected:
using Base = internal::RepeatedFieldProxyBase<const ElementType, kOrProxy>;
// Inherit constructors, but don't publicly expose them.
//
// Repeated field proxies have no public constructors aside from a copy
// constructor. This is intentional, as layout of data that is proxied is an
// implementation detail. By not exposing a way to construct a proxy, we can
// freely change the layout of the underlying repeated field.
using Base::Base;
using Base::field;
public:
ConstRepeatedFieldProxyImpl()
: Base(*internal::RawPtr<const typename Base::RepeatedFieldType>()) {}
using typename Base::const_reference;
using typename Base::size_type;
ConstRepeatedFieldProxyImpl(const ConstRepeatedFieldProxyImpl& other) =
default;
ConstRepeatedFieldProxyImpl& operator=(const ConstRepeatedFieldProxyImpl&) =
default;
// Returns a type which references the element at the given index. Performs
// bounds checking in accordance with `bounds_check_mode_*`.
[[nodiscard]] const_reference operator[](size_type index) const {
return field()[index];
}
private:
// Note that we don't need an arena pointer here, since we don't mutate the
// underlying repeated field.
};
} // namespace internal
// A proxy for a repeated field of type `ElementType` in a Protobuf message.
// Proxies alias the repeated field and provide an interface to read or modify
// it, following STL naming conventions.
//
// Proxies themselves are value types, meaning they should be passed around by
// value similar to `absl::string_view` or `absl::Span`.
//
// Proxies cannot be constructed directly. They are returned from a message's
// repeated field accessors which have the `features.(pb.cpp).repeated_type =
// PROXY` annotation. This annotation is currently only available in edition
// `UNSTABLE`, but will eventually be available in an upcoming edition.
template <typename ElementType>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxy final
: public internal::MutableRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/false> {
static_assert(!std::is_const_v<ElementType>);
private:
using Base = internal::MutableRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/false>;
using Base::Base;
using Base::field;
public:
// Copy-assigns `other` into this repeated field.
//
// This method exists because mutable proxies cannot be rebound.
void assign(RepeatedFieldProxy<const ElementType> other) const {
field().CopyFrom(other.field());
}
// Because we have an overload of `assign` in this class, we need to
// explicitly inherit the overload from the base class to avoid hiding it.
using Base::assign;
// Move-assigns `other` into this repeated field. `other` is left in a valid
// but unspecified state.
void move_assign(RepeatedFieldProxy<ElementType> other) const {
field() = std::move(other.field());
}
// Swaps the contents of this repeated field with `other`.
//
// Invalidates all iterators. Pointer stability is not guaranteed across the
// swap for any element of either repeated field.
//
// If the underlying repeated fields are on different arenas, this may force
// deep copies of the elements.
void swap(RepeatedFieldProxy other) const { field().Swap(&other.field()); }
private:
friend RepeatedFieldProxy<const ElementType>;
friend internal::RepeatedFieldOrProxy<ElementType>;
friend internal::RepeatedFieldOrProxy<const ElementType>;
friend internal::RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, /*kOrProxy=*/false>;
};
template <typename ElementType>
class PROTOBUF_DECLSPEC_EMPTY_BASES RepeatedFieldProxy<const ElementType> final
: public internal::ConstRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/false> {
// A specialization of RepeatedFieldProxy for const proxies. This is needed
// for mutating methods to not be exposed on const proxies.
private:
using Base = internal::ConstRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/false>;
// Inherit constructors, but don't publicly expose them.
//
// Repeated field proxies have no public constructors aside from a copy
// constructor. This is intentional, as layout of data that is proxied is an
// implementation detail. By not exposing a way to construct a proxy, we can
// freely change the layout of the underlying repeated field.
using Base::Base;
public:
RepeatedFieldProxy() = default;
RepeatedFieldProxy(const RepeatedFieldProxy& other) = default;
RepeatedFieldProxy& operator=(const RepeatedFieldProxy& other) = default;
// Allow implicit conversion from a mutable RepeatedFieldProxy to a const
// RepeatedFieldProxy.
//
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldProxy(RepeatedFieldProxy<ElementType> other)
: Base(other.field()) {}
private:
friend RepeatedFieldProxy<ElementType>;
friend internal::RepeatedFieldOrProxy<const ElementType>;
friend internal::RepeatedFieldProxyInternalPrivateAccessHelper<
const ElementType, /*kOrProxy=*/false>;
};
// The size of proxies is not really important, since they should mostly be
// passed around by value and inlined away to oblivion. Regardless, size
// assertions guarantee that the compiler hasn't introduced invisible members
// that we didn't notice (e.g. `PROTOBUF_DECLSPEC_EMPTY_BASES`).
static_assert(sizeof(RepeatedFieldProxy<int>) == 2 * sizeof(void*));
static_assert(sizeof(RepeatedFieldProxy<const int>) == sizeof(void*));
namespace internal {
// A helper class for accessing private members of `RepeatedFieldProxy` in
// Protobuf internal code.
//
// DO NOT USE this class for any reason outside of protobuf internal code.
template <typename ElementType, bool kOrProxy = false>
class RepeatedFieldProxyInternalPrivateAccessHelper {
using ProxyType =
std::conditional_t<kOrProxy, RepeatedFieldOrProxy<ElementType>,
RepeatedFieldProxy<ElementType>>;
// Casts up to a `MutableRepeatedFieldProxyImpl<ElementType>` from a subclass
// of `MutableRepeatedFieldProxyImpl<ElementType>`. This is used to implement
// the CRTP pattern for `*With<MethodName>` classes.
template <typename C>
static MutableRepeatedFieldProxyImpl<ElementType, kOrProxy> ToProxyType(
const C* PROTOBUF_NONNULL proxy) {
return *static_cast<
const MutableRepeatedFieldProxyImpl<ElementType, kOrProxy>*>(proxy);
}
public:
template <typename... Args>
static RepeatedFieldProxy<ElementType> Construct(Args&&... args) {
return RepeatedFieldProxy<ElementType>(std::forward<Args>(args)...);
}
static auto& field(const ProxyType& proxy) { return proxy.field(); }
// Takes any subclass of `RepeatedFieldProxy<ElementType>`, upcasts to
// `RepeatedFieldProxy<ElementType>`, then calls `field()`. This is used to
// implement the CRTP pattern for `*With<MethodName>` classes.
template <typename C>
static auto& field(const C* PROTOBUF_NONNULL proxy) {
return ToProxyType(proxy).field();
}
template <typename C, typename... Args>
static auto& Add(const C* PROTOBUF_NONNULL proxy, Args&&... args) {
return ToProxyType(proxy).Add(std::forward<Args>(args)...);
}
template <typename C, typename... Args>
static auto& Emplace(const C* PROTOBUF_NONNULL proxy, Args&&... args) {
return ToProxyType(proxy).Emplace(std::forward<Args>(args)...);
}
};
// A mutable `RepeatedFieldOrProxy` for a repeated field of type `ElementType`
// in a Protobuf message. Proxies alias the repeated field and provide an
// interface to read or modify it, following STL naming conventions.
//
// Unlike `RepeatedFieldProxy`, `RepeatedFieldOrProxy` can be constructed from
// the legacy repeated field containers (`google::protobuf::RepeatedField` and
// `google::protobuf::RepeatedPtrField`). This container can be used in code which has not
// yet fully migrated to proxies. It is particularly useful for function
// parameters that have many callers, allowing the callers to be migrated to
// proxies incrementally.
//
// Proxies themselves are value types, meaning they should be passed around by
// value similar to `absl::string_view` or `absl::Span`.
template <typename ElementType>
class RepeatedFieldOrProxy final
: public internal::MutableRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/true> {
// `const ElementType` is specialized below.
static_assert(!std::is_const_v<ElementType>);
private:
using Base = internal::MutableRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/true>;
using RepeatedFieldType = typename Base::RepeatedFieldType;
// Inherit constructors.
using Base::Base;
using Base::field;
public:
// Allow implicit conversion from a RepeatedField to a RepeatedFieldOrProxy.
//
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldOrProxy(RepeatedFieldType& field)
: Base(field, field.GetArena()) {}
// Allow implicit conversion from a RepeatedField* to a RepeatedFieldOrProxy,
// but inline this to call the RepeatedFieldOrProxy(RepeatedFieldType& field)
// constructor.
//
// This will be used to ease migration along for functions that currently take
// a RepeatedField* parameter. If we allow implicit conversion from
// RepeatedField* to RepeatedFieldOrProxy, then we can change the type of the
// parameter from RepeatedField<T>* to RepeatedFieldOrProxy<T> without
// updating any callers. Then, the C++ inliner will later come along and
// dereference the repeated field pointer argument to the method at callsites.
PROTOBUF_REFACTOR_INLINE()
// NOLINTNEXTLINE
RepeatedFieldOrProxy(RepeatedFieldType* PROTOBUF_NONNULL field)
: RepeatedFieldOrProxy(*field) {}
// Allow implicit conversion from a RepeatedFieldProxy to a
// RepeatedFieldOrProxy.
//
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldOrProxy(RepeatedFieldProxy<ElementType> proxy)
: Base(proxy.field(), proxy.arena()) {}
// Allow explicit conversion to the legacy repeated field container.
//
// Note: this performs a deep copy of the underlying repeated field.
explicit operator std::remove_const_t<RepeatedFieldType>() const {
return RepeatedFieldType(static_cast<const Base&>(*this));
}
// Note: we do not directly expose the following methods from the base class
// because they have overloads that take a `RepeatedFieldProxy`. We re-define
// them below with overloads that take `RepeatedFieldOrProxy`.
// Copy-assigns `other` into this repeated field.
//
// This method exists because mutable proxies cannot be rebound.
void assign(RepeatedFieldOrProxy<const ElementType> other) const {
Base::field().CopyFrom(other.field());
}
// Because we have an overload of `assign` in this class, we need to
// explicitly inherit the overload from the base class to avoid hiding it.
using Base::assign;
// Move-assigns `other` into this repeated field. `other` is left in a valid
// but unspecified state.
//
// This method only differs from `assign` in that it is a hint that you don't
// need the contents of `other` anymore. It is not guaranteed that the
// contents will be efficiently transferred, nor that pointer stability will
// be preserved for elements of `other`.
void move_assign(RepeatedFieldOrProxy other) const {
field() = std::move(other.field());
}
// Swaps the contents of this repeated field with `other`.
//
// Invalidates all iterators. Pointer stability is not guaranteed across the
// swap for any element of either repeated field.
//
// If the underlying repeated fields are on different arenas, this may force
// deep copies of the elements.
void swap(RepeatedFieldOrProxy other) const {
Base::field().Swap(&other.field());
}
private:
friend RepeatedFieldProxy<ElementType>;
friend RepeatedFieldOrProxy<const ElementType>;
friend internal::RepeatedFieldProxyInternalPrivateAccessHelper<
ElementType, /*kOrProxy=*/true>;
};
// A const proxy for a repeated field of type `ElementType` in a Protobuf
// message. Proxies alias the repeated field and provide an interface to read or
// modify it, following STL naming conventions.
//
// Unlike `RepeatedFieldProxy`, `RepeatedFieldOrProxy` can be constructed from
// the legacy repeated field containers (`google::protobuf::RepeatedField` and
// `google::protobuf::RepeatedPtrField`). This container can be used in code which has not
// yet fully migrated to proxies. It is particularly useful for function
// parameters that have many callers, allowing the callers to be migrated to
// proxies incrementally.
//
// Proxies themselves are value types, meaning they should be passed around by
// value similar to `absl::string_view` or `absl::Span`.
template <typename ElementType>
class RepeatedFieldOrProxy<const ElementType> final
: public internal::ConstRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/true> {
using Base = internal::ConstRepeatedFieldProxyImpl<ElementType,
/*kOrProxy=*/true>;
using RepeatedFieldType = typename Base::RepeatedFieldType;
// Inherit constructors.
using Base::Base;
using Base::field;
public:
RepeatedFieldOrProxy() = default;
RepeatedFieldOrProxy(const RepeatedFieldOrProxy& other) = default;
RepeatedFieldOrProxy& operator=(const RepeatedFieldOrProxy& other) = default;
// Allow implicit conversion from a mutable RepeatedFieldOrProxy to a const
// RepeatedFieldOrProxy.
//
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldOrProxy(RepeatedFieldOrProxy<ElementType> other)
: Base(other.field()) {}
// Allow implicit conversion from a RepeatedField to a RepeatedFieldOrProxy.
//
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldOrProxy(const RepeatedFieldType& field) : Base(field) {}
// Allow implicit conversion from a const RepeatedField* to a
// RepeatedFieldOrProxy, but inline this to call the
// RepeatedFieldOrProxy(RepeatedFieldType& field) constructor.
//
// This will be used to ease migration along for functions that currently take
// a const RepeatedField* parameter. If we allow implicit conversion from
// const RepeatedField* to RepeatedFieldOrProxy, then we can change the type
// of the parameter from const RepeatedField<T>* to
// RepeatedFieldOrProxy<const T> without updating any callers. Then, the C++
// inliner will later come along and dereference the repeated field pointer
// argument to the method at callsites.
PROTOBUF_REFACTOR_INLINE()
// NOLINTNEXTLINE
RepeatedFieldOrProxy(RepeatedFieldType* PROTOBUF_NONNULL field)
: RepeatedFieldOrProxy(*field) {}
// Allow implicit conversion from a RepeatedFieldProxy to a
// RepeatedFieldOrProxy.
//
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldOrProxy(RepeatedFieldProxy<ElementType> proxy)
: Base(proxy.field()) {}
// NOLINTNEXTLINE(google-explicit-constructor)
RepeatedFieldOrProxy(RepeatedFieldProxy<const ElementType> proxy)
: Base(proxy.field()) {}
// Allow explicit conversion to the legacy repeated field container.
//
// Note: this performs a deep copy of the underlying repeated field.
explicit operator RepeatedFieldType() const {
return RepeatedFieldType(static_cast<const Base&>(*this));
}
private:
friend RepeatedFieldOrProxy<ElementType>;
};
static_assert(sizeof(RepeatedFieldOrProxy<int>) ==
sizeof(RepeatedFieldProxy<int>),
"Mutable `RepeatedFieldOrProxy` is not the expected size");
static_assert(sizeof(RepeatedFieldOrProxy<const int>) ==
sizeof(RepeatedFieldProxy<const int>),
"Const `RepeatedFieldOrProxy` is not the expected size");
} // namespace internal
// Like C++20's std::erase_if, for RepeatedFieldProxy
template <int&... DeductionBarrier, typename T, typename Pred>
size_t erase_if(RepeatedFieldProxy<T> cont, Pred pred) {
return google::protobuf::erase_if(
internal::RepeatedFieldProxyInternalPrivateAccessHelper<
T, /*kOrProxy=*/false>::field(cont),
pred);
}
// Like C++20's std::erase, for RepeatedFieldProxy
template <int&... DeductionBarrier, typename T, typename U>
size_t erase(RepeatedFieldProxy<T> cont, const U& value) {
return google::protobuf::erase(internal::RepeatedFieldProxyInternalPrivateAccessHelper<
T, /*kOrProxy=*/false>::field(cont),
value);
}
// Like C++20's std::erase_if, for RepeatedFieldOrProxy.
template <int&... DeductionBarrier, typename T, typename Pred>
size_t erase_if(internal::RepeatedFieldOrProxy<T> cont, Pred pred) {
return google::protobuf::erase_if(
internal::RepeatedFieldProxyInternalPrivateAccessHelper<
T, /*kOrProxy=*/true>::field(cont),
pred);
}
// Like C++20's std::erase, for RepeatedFieldOrProxy.
template <int&... DeductionBarrier, typename T, typename U>
size_t erase(internal::RepeatedFieldOrProxy<T> cont, const U& value) {
return google::protobuf::erase(internal::RepeatedFieldProxyInternalPrivateAccessHelper<
T, /*kOrProxy=*/true>::field(cont),
value);
}
// Like C++20's std::sort, for RepeatedFieldProxy.
template <int&... DeductionBarrier, typename T, typename Compare>
void c_sort(RepeatedFieldProxy<T> cont, Compare cmp) {
google::protobuf::sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::sort, for RepeatedFieldProxy, with default comparison.
template <int&... DeductionBarrier, typename T>
void c_sort(RepeatedFieldProxy<T> cont) {
google::protobuf::sort(cont.begin(), cont.end());
}
// Like C++20's std::stable_sort, for RepeatedFieldProxy.
template <int&... DeductionBarrier, typename T, typename Compare>
void c_stable_sort(RepeatedFieldProxy<T> cont, Compare cmp) {
google::protobuf::stable_sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::stable_sort, for RepeatedFieldProxy, with default
// comparison.
template <int&... DeductionBarrier, typename T>
void c_stable_sort(RepeatedFieldProxy<T> cont) {
google::protobuf::stable_sort(cont.begin(), cont.end());
}
// Like C++20's std::sort, for RepeatedFieldOrProxy.
template <int&..., typename T, typename Compare>
void c_sort(internal::RepeatedFieldOrProxy<T> cont, Compare cmp) {
google::protobuf::sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::sort, for RepeatedFieldOrProxy, with default comparison.
template <int&..., typename T>
void c_sort(internal::RepeatedFieldOrProxy<T> cont) {
google::protobuf::sort(cont.begin(), cont.end());
}
// Like C++20's std::stable_sort, for RepeatedFieldOrProxy.
template <int&..., typename T, typename Compare>
void c_stable_sort(internal::RepeatedFieldOrProxy<T> cont, Compare cmp) {
google::protobuf::stable_sort(cont.begin(), cont.end(), cmp);
}
// Like C++20's std::stable_sort, for RepeatedFieldOrProxy, with default
// comparison.
template <int&..., typename T>
void c_stable_sort(internal::RepeatedFieldOrProxy<T> cont) {
google::protobuf::stable_sort(cont.begin(), cont.end());
}
} // namespace protobuf
} // namespace google
#include "google/protobuf/port_undef.inc"
#endif // GOOGLE_PROTOBUF_REPEATED_FIELD_PROXY_H__