[Upb C++] Implement RepeatedField

PiperOrigin-RevId: 538944010
pull/13171/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent cfb8b3dd6d
commit 628c261e52
  1. 41
      protos/BUILD
  2. 1
      protos/bazel/upb_cc_proto_library.bzl
  3. 41
      protos/protos_traits.h
  4. 303
      protos/repeated_field.h
  5. 158
      protos/repeated_field_iterator.h
  6. 20
      protos_generator/gen_messages.cc
  7. 96
      protos_generator/gen_repeated_fields.cc
  8. 1
      protos_generator/protoc-gen-upb-protos.cc
  9. 1
      protos_generator/tests/BUILD
  10. 187
      protos_generator/tests/test_generated.cc
  11. 2
      protos_generator/tests/test_model.proto
  12. 6
      upb/collections/array.c
  13. 6
      upb/collections/array.h

@ -38,10 +38,35 @@ load(
licenses(["notice"])
cc_library(
name = "repeated_field",
hdrs = [
"repeated_field.h",
"repeated_field_iterator.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
":protos",
":protos_traits",
"//:collections_internal",
"//:message_copy",
"//:mini_table",
"//:port",
"//:upb",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "protos",
srcs = ["protos.cc"],
hdrs = ["protos.h"],
srcs = [
"protos.cc",
],
hdrs = [
"protos.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:public"],
deps = [
@ -50,10 +75,21 @@ cc_library(
"//:upb",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
],
)
# Internally used type traits.
cc_library(
name = "protos_traits",
hdrs = [
"protos_traits.h",
],
copts = UPB_DEFAULT_CPPOPTS,
visibility = ["//visibility:private"],
)
cc_library(
name = "protos_internal",
hdrs = ["protos_internal.h"],
@ -80,6 +116,7 @@ cc_library(
deps = [
":protos",
":protos_internal",
":repeated_field",
],
)

@ -286,6 +286,7 @@ _upb_cc_proto_library_aspect = aspect(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/status:statusor",
"//protos",
"//protos:repeated_field",
],
),
}),

@ -0,0 +1,41 @@
/*
* Copyright (c) 2009-2023, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_
#define THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_
#include <type_traits>
namespace protos::internal {
template <typename T, typename T2>
using add_const_if_T_is_const =
std::conditional_t<std::is_const_v<T>, const T2, T2>;
} // namespace protos::internal
#endif // THIRD_PARTY_UPB_PROTOS_PROTOS_TRAITS_H_

@ -0,0 +1,303 @@
/*
* Copyright (c) 2009-2023, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UPB_PROTOS_REPEATED_FIELD_H_
#define UPB_PROTOS_REPEATED_FIELD_H_
#include <cstddef>
#include <iterator>
#include <type_traits>
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "protos/protos.h"
#include "protos/protos_traits.h"
#include "protos/repeated_field_iterator.h"
#include "upb/base/string_view.h"
#include "upb/collections/array.h"
#include "upb/collections/array_internal.h"
#include "upb/mem/arena.h"
#include "upb/message/copy.h"
#include "upb/mini_table/types.h"
// Must be last:
#include "upb/port/def.inc"
namespace protos {
namespace internal {
// Shared implementation of repeated fields for absl::string_view and
// message types for mutable and immutable variants.
//
// Immutable (const accessor), constructs this class with a nullptr upb_Array*
// when the underlying array in the message is empty.
//
// Mutable accessors on the other hand, will allocate a new empty non-null
// upb_Array* for the message when the RepeatedFieldProxy is constructed.
template <class T>
class RepeatedFieldProxyBase {
using Array = add_const_if_T_is_const<T, upb_Array>;
public:
explicit RepeatedFieldProxyBase(Array* arr) : arr_(arr) {}
size_t size() const { return arr_ != nullptr ? upb_Array_Size(arr_) : 0; }
bool empty() const { return size() == 0; }
protected:
// Returns upb_Array string member.
inline absl::string_view GetString(size_t n) const;
// Returns upb_Array message member.
inline upb_Message* GetMessage(size_t n) const;
Array* arr_;
};
template <class T>
inline absl::string_view RepeatedFieldProxyBase<T>::GetString(size_t n) const {
upb_MessageValue message_value = upb_Array_Get(arr_, n);
return absl::string_view(message_value.str_val.data,
message_value.str_val.size);
}
template <class T>
upb_Message* RepeatedFieldProxyBase<T>::GetMessage(size_t n) const {
auto** messages =
static_cast<upb_Message**>(upb_Array_MutableDataPtr(this->arr_));
return messages[n];
}
template <class T>
class RepeatedFieldProxyMutableBase : public RepeatedFieldProxyBase<T> {
public:
RepeatedFieldProxyMutableBase(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyBase<T>(arr), arena_(arena) {}
void clear() { upb_Array_Resize(this->arr_, 0, arena_); }
protected:
upb_Arena* arena_;
};
// RepeatedField proxy for repeated messages.
template <class T>
class RepeatedFieldProxy
: public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
RepeatedFieldProxyMutableBase<T>> {
static_assert(!std::is_same_v<T, absl::string_view>, "");
static_assert(!std::is_same_v<T, const absl::string_view>, "");
static_assert(!std::is_arithmetic_v<T>, "");
static constexpr bool kIsConst = std::is_const_v<T>;
public:
explicit RepeatedFieldProxy(const upb_Array* arr)
: RepeatedFieldProxyBase<T>(arr) {}
RepeatedFieldProxy(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyMutableBase<T>(arr, arena) {}
// Constructor used by ::protos::Ptr.
RepeatedFieldProxy(const RepeatedFieldProxy&) = default;
// T::CProxy [] operator specialization.
typename T::CProxy operator[](size_t n) const {
upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
return ::protos::internal::CreateMessage<typename std::remove_const_t<T>>(
(upb_Message*)message_value.msg_val);
}
// TODO(b:/280069986) : Audit/Finalize based on Iterator Design.
// T::Proxy [] operator specialization.
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
typename T::Proxy operator[](size_t n) {
return ::protos::internal::CreateMessageProxy<T>(this->GetMessage(n),
this->arena_);
}
// Mutable message reference specialization.
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(const T& t) {
upb_MessageValue message_value;
message_value.msg_val =
upb_Message_DeepClone(GetInternalMsg(t), T::minitable(), this->arena_);
upb_Array_Append(this->arr_, message_value, this->arena_);
}
// Mutable message add using move.
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(T&& msg) {
upb_MessageValue message_value;
message_value.msg_val = GetInternalMsg(msg);
upb_Arena_Fuse(GetArena(msg), this->arena_);
upb_Array_Append(this->arr_, message_value, this->arena_);
T moved_msg = std::move(msg);
}
private:
friend class ::protos::Ptr<T>;
};
// RepeatedField proxy for repeated strings.
template <class T>
class RepeatedFieldStringProxy
: public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
RepeatedFieldProxyMutableBase<T>> {
static_assert(std::is_same_v<T, absl::string_view> ||
std::is_same_v<T, const absl::string_view>,
"");
static constexpr bool kIsConst = std::is_const_v<T>;
public:
// Immutable constructor.
explicit RepeatedFieldStringProxy(const upb_Array* arr)
: RepeatedFieldProxyBase<T>(arr) {}
// Mutable constructor.
RepeatedFieldStringProxy(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyMutableBase<T>(arr, arena) {}
// Constructor used by ::protos::Ptr.
RepeatedFieldStringProxy(const RepeatedFieldStringProxy&) = default;
T operator[](size_t n) const { return this->GetString(n); }
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(T t) {
upb_MessageValue message_value;
// Copy string to arena.
UPB_ASSERT(this->arena_);
char* data = (char*)upb_Arena_Malloc(this->arena_, t.size());
UPB_ASSERT(data);
memcpy(data, t.data(), t.size());
message_value.str_val = upb_StringView_FromDataAndSize(data, t.size());
upb_Array_Append(this->arr_, message_value, this->arena_);
}
};
// RepeatedField proxy for repeated scalar types.
template <typename T>
class RepeatedFieldScalarProxy
: public std::conditional_t<std::is_const_v<T>, RepeatedFieldProxyBase<T>,
RepeatedFieldProxyMutableBase<T>> {
static_assert(std::is_arithmetic_v<T>, "");
static constexpr bool kIsConst = std::is_const_v<T>;
public:
explicit RepeatedFieldScalarProxy(const upb_Array* arr)
: RepeatedFieldProxyBase<T>(arr) {}
RepeatedFieldScalarProxy(upb_Array* arr, upb_Arena* arena)
: RepeatedFieldProxyMutableBase<T>(arr, arena) {}
// Constructor used by ::protos::Ptr.
RepeatedFieldScalarProxy(const RepeatedFieldScalarProxy&) = default;
T operator[](size_t n) const {
upb_MessageValue message_value = upb_Array_Get(this->arr_, n);
typename std::remove_const_t<T> val;
memcpy(&val, &message_value, sizeof(T));
return val;
}
template <int&... DeductionBlocker, bool b = !kIsConst,
typename = std::enable_if_t<b>>
void push_back(T t) {
upb_MessageValue message_value;
memcpy(&message_value, &t, sizeof(T));
upb_Array_Append(this->arr_, message_value, this->arena_);
}
// Iterator support.
using iterator = internal::RepeatedScalarIterator<T>;
iterator begin() const { return iterator(unsafe_array()); }
iterator cbegin() const { return begin(); }
iterator end() const { return iterator(unsafe_array() + this->size()); }
iterator cend() const { return end(); }
// Reverse iterator support.
using const_reverse_iterator = std::reverse_iterator<iterator>;
using reverse_iterator = std::reverse_iterator<iterator>;
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
private:
T* unsafe_array() const {
if (kIsConst) {
const void* unsafe_ptr = ::upb_Array_DataPtr(this->arr_);
return static_cast<T*>(const_cast<void*>(unsafe_ptr));
}
if (!kIsConst) {
void* unsafe_ptr =
::upb_Array_MutableDataPtr(const_cast<upb_Array*>(this->arr_));
return static_cast<T*>(unsafe_ptr);
}
}
};
} // namespace internal
template <typename T>
class RepeatedField {
static constexpr bool kIsString = std::is_same_v<T, absl::string_view>;
static constexpr bool kIsScalar = std::is_arithmetic_v<T>;
public:
using Proxy = std::conditional_t<
kIsScalar, internal::RepeatedFieldScalarProxy<T>,
std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
internal::RepeatedFieldProxy<T>>>;
using CProxy = std::conditional_t<
kIsScalar, internal::RepeatedFieldScalarProxy<const T>,
std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<const T>,
internal::RepeatedFieldProxy<const T>>>;
// TODO(b/286451125): T supports incomplete type from fwd.h forwarding headers
// We would like to reference T::CProxy. Validate forwarding header design.
using ValueProxy = std::conditional_t<
kIsScalar, T,
std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<T>>>;
using ValueCProxy = std::conditional_t<
kIsScalar, const T,
std::conditional_t<kIsString, absl::string_view, ::protos::Ptr<const T>>>;
using Access = std::conditional_t<
kIsScalar, internal::RepeatedFieldScalarProxy<T>,
std::conditional_t<kIsString, internal::RepeatedFieldStringProxy<T>,
internal::RepeatedFieldProxy<T>>>;
};
} // namespace protos
#include "upb/port/undef.inc"
#endif // UPB_PROTOS_REPEATED_FIELD_H_

@ -0,0 +1,158 @@
/*
* Copyright (c) 2009-2023, Google LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Google LLC nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_
#define UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_
#include <cstddef>
#include <iterator>
#include <type_traits>
#include "absl/strings/string_view.h"
#include "protos/protos.h"
#include "upb/base/string_view.h"
#include "upb/collections/array.h"
#include "upb/mem/arena.h"
#include "upb/message/copy.h"
#include "upb/mini_table/types.h"
// Must be last:
#include "upb/port/def.inc"
namespace protos {
namespace internal {
// TODO(b/279086429): Implement std iterator for strings and messages
template <typename T>
class RepeatedFieldScalarProxy;
template <typename T>
class RepeatedScalarIterator {
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = typename std::remove_const<T>::type;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
constexpr RepeatedScalarIterator() noexcept : it_(nullptr) {}
RepeatedScalarIterator(const RepeatedScalarIterator& other) = default;
RepeatedScalarIterator& operator=(const RepeatedScalarIterator& other) =
default;
// deref TODO(b/286450722) Change to use a proxy.
constexpr reference operator*() const noexcept { return *it_; }
constexpr pointer operator->() const noexcept { return it_; }
private:
// Hide the internal type.
using iterator = RepeatedScalarIterator;
public:
// {inc,dec}rementable
constexpr iterator& operator++() noexcept {
++it_;
return *this;
}
constexpr iterator operator++(int) noexcept { return iterator(it_++); }
constexpr iterator& operator--() noexcept {
--it_;
return *this;
}
constexpr iterator operator--(int) noexcept { return iterator(it_--); }
// equality_comparable
friend constexpr bool operator==(const iterator x,
const iterator y) noexcept {
return x.it_ == y.it_;
}
friend constexpr bool operator!=(const iterator x,
const iterator y) noexcept {
return x.it_ != y.it_;
}
// less_than_comparable
friend constexpr bool operator<(const iterator x, const iterator y) noexcept {
return x.it_ < y.it_;
}
friend constexpr bool operator<=(const iterator x,
const iterator y) noexcept {
return x.it_ <= y.it_;
}
friend constexpr bool operator>(const iterator x, const iterator y) noexcept {
return x.it_ > y.it_;
}
friend constexpr bool operator>=(const iterator x,
const iterator y) noexcept {
return x.it_ >= y.it_;
}
constexpr iterator& operator+=(difference_type d) noexcept {
it_ += d;
return *this;
}
constexpr iterator operator+(difference_type d) const noexcept {
return iterator(it_ + d);
}
friend constexpr iterator operator+(const difference_type d,
iterator it) noexcept {
return it + d;
}
constexpr iterator& operator-=(difference_type d) noexcept {
it_ -= d;
return *this;
}
constexpr iterator operator-(difference_type d) const noexcept {
return iterator(it_ - d);
}
// indexable
constexpr reference operator[](difference_type d) const noexcept {
return it_[d];
}
// random access iterator
friend constexpr difference_type operator-(iterator x, iterator y) noexcept {
return x.it_ - y.it_;
}
private:
friend class RepeatedFieldScalarProxy<T>;
// Create from internal::RepeatedFieldScalarProxy.
explicit RepeatedScalarIterator(T* it) noexcept : it_(it) {}
// The internal iterator.
T* it_;
};
} // namespace internal
} // namespace protos
#endif // UPB_PROTOS_REPEATED_FIELD_ITERATOR_H_

@ -89,25 +89,24 @@ void WriteMessageClassDeclarations(
}
// Forward declaration of Proto Class for GCC handling of free friend method.
output("class $0;", ClassName(descriptor));
output("namespace internal {\n");
output("class $0;\n", ClassName(descriptor));
output("namespace internal {\n\n");
WriteModelAccessDeclaration(descriptor, output);
output("\n");
WriteInternalForwardDeclarationsInHeader(descriptor, output);
output("\n");
output("} // namespace internal\n");
output("} // namespace internal\n\n");
WriteModelPublicDeclaration(descriptor, file_exts, file_enums, output);
output("namespace internal {\n");
WriteModelCProxyDeclaration(descriptor, output);
WriteModelProxyDeclaration(descriptor, output);
output("} // namespace internal\n");
output("} // namespace internal\n\n");
}
void WriteModelAccessDeclaration(const protobuf::Descriptor* descriptor,
Output& output) {
output(
R"cc(
class $0Access {
public:
$0Access() {}
@ -189,8 +188,6 @@ void WriteModelPublicDeclaration(
WriteUsingEnumsInHeader(descriptor, file_enums, output);
WriteDefaultInstanceHeader(descriptor, output);
WriteExtensionIdentifiersInClassHeader(descriptor, file_exts, output);
output.Indent();
output.Indent();
if (descriptor->extension_range_count()) {
// for typetrait checking
output("using ExtendableType = $0;\n", ClassName(descriptor));
@ -200,11 +197,16 @@ void WriteModelPublicDeclaration(
// with gcc otherwise the compiler will fail with
// "has not been declared within namespace" error. Even though there is a
// namespace qualifier, cross namespace matching fails.
output.Indent();
output(
R"cc(
static const upb_MiniTable* minitable();
using $0Access::GetInternalArena;
)cc",
ClassName(descriptor));
output("\n");
output(
R"cc(
private:
$0(upb_Message* msg, upb_Arena* arena) : $0Access() {
msg_ = ($1*)msg;
@ -264,6 +266,7 @@ void WriteModelProxyDeclaration(const protobuf::Descriptor* descriptor,
friend $0::Proxy(::protos::CreateMessage<$0>(::protos::Arena& arena));
friend $0::Proxy(::protos::internal::CreateMessageProxy<$0>(
upb_Message*, upb_Arena*));
friend class RepeatedFieldProxy;
friend class $0CProxy;
friend class $0Access;
friend class ::protos::Ptr<$0>;
@ -307,6 +310,7 @@ void WriteModelCProxyDeclaration(const protobuf::Descriptor* descriptor,
private:
$0CProxy(void* msg) : internal::$0Access(($1*)msg, nullptr){};
friend $0::CProxy(::protos::internal::CreateMessage<$0>(upb_Message* msg));
friend class RepeatedFieldProxy;
friend class ::protos::Ptr<$0>;
friend class ::protos::Ptr<const $0>;
static void Rebind($0CProxy& lhs, const $0CProxy& rhs) {

@ -30,6 +30,7 @@
#include <vector>
#include "google/protobuf/descriptor.pb.h"
#include "absl/strings/string_view.h"
#include "google/protobuf/descriptor.h"
#include "protos_generator/gen_accessors.h"
#include "protos_generator/gen_enums.h"
@ -39,6 +40,7 @@
#include "protos_generator/output.h"
#include "upbc/common.h"
#include "upbc/file_layout.h"
#include "upbc/names.h"
namespace protos_generator {
namespace protobuf = ::google::protobuf;
@ -53,14 +55,13 @@ void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field,
R"cc(
using $0Access::$1;
using $0Access::$1_size;
using $0Access::mutable_$1;
)cc",
class_name, resolved_field_name);
if (!read_only) {
output(
R"cc(
using $0Access::add_$1;
using $0Access::clear_$1;
using $0Access::mutable_$1;
)cc",
class_name, resolved_field_name);
}
@ -75,7 +76,7 @@ void WriteRepeatedFieldUsingAccessors(const protobuf::FieldDescriptor* field,
output(
R"cc(
using $0Access::add_$1;
using $0Access::clear_$1;
using $0Access::mutable_$1;
using $0Access::resize_$1;
using $0Access::set_$1;
)cc",
@ -96,8 +97,6 @@ void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc,
$0_$2(msg_, &len);
return len;
}
inline void clear_$1() { $0_clear_$2(msg_); }
)cc",
MessageName(desc), resolved_field_name, resolved_upbc_name);
@ -105,16 +104,34 @@ void WriteRepeatedFieldsInMessageHeader(const protobuf::Descriptor* desc,
output(
R"cc(
$1 $2(size_t index) const;
const ::protos::RepeatedField<const $4>::CProxy $2() const;
::protos::Ptr<::protos::RepeatedField<$4>> mutable_$2();
absl::StatusOr<$0> add_$2();
$0 mutable_$2(size_t index) const;
)cc",
MessagePtrConstType(field, /* const */ false),
MessagePtrConstType(field, /* const */ true), resolved_field_name,
resolved_upbc_name);
MessagePtrConstType(field, /* const */ false), // $0
MessagePtrConstType(field, /* const */ true), // $1
resolved_field_name, // $2
resolved_upbc_name, // $3
MessageBaseType(field, /* maybe_const */ false) // $4
);
} else if (field->cpp_type() == protobuf::FieldDescriptor::CPPTYPE_STRING) {
output(
R"cc(
$0 $1(size_t index) const;
const ::protos::RepeatedField<$0>::CProxy $1() const;
::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1();
bool add_$1($0 val);
void set_$1(size_t index, $0 val);
bool resize_$1(size_t len);
)cc",
CppConstType(field), resolved_field_name);
} else {
output(
R"cc(
$0 $1(size_t index) const;
const ::protos::RepeatedField<$0>::CProxy $1() const;
::protos::Ptr<::protos::RepeatedField<$0>> mutable_$1();
bool add_$1($0 val);
void set_$1(size_t index, $0 val);
bool resize_$1(size_t len);
@ -170,6 +187,27 @@ void WriteRepeatedMessageAccessor(const protobuf::Descriptor* message,
resolved_field_name, MessageName(message),
MessageBaseType(field, /* maybe_const */ false), arena_expression,
upbc_name);
output(
R"cc(
const ::protos::RepeatedField<const $1>::CProxy $0::$2() const {
size_t size;
const upb_Array* arr = _$3_$4_$5(msg_, &size);
return ::protos::RepeatedField<const $1>::CProxy(arr);
};
::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
size_t size;
upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
}
)cc",
class_name, // $0
MessageBaseType(field, /* maybe_const */ false), // $1
resolved_field_name, // $2
MessageName(message), // $3
upbc_name, // $4
upbc::kRepeatedFieldArrayGetterPostfix, // $5
upbc::kRepeatedFieldMutableArrayGetterPostfix // $6
);
}
void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
@ -215,6 +253,27 @@ void WriteRepeatedStringAccessor(const protobuf::Descriptor* message,
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
const ::protos::RepeatedField<$1>::CProxy $0::$2() const {
size_t size;
const upb_Array* arr = _$3_$4_$5(msg_, &size);
return ::protos::RepeatedField<$1>::CProxy(arr);
};
::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
size_t size;
upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
}
)cc",
class_name, // $0
CppConstType(field), // $1
resolved_field_name, // $2
MessageName(message), // $3
upbc_name, // $4
upbc::kRepeatedFieldArrayGetterPostfix, // $5
upbc::kRepeatedFieldMutableArrayGetterPostfix // $6
);
}
void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
@ -258,6 +317,27 @@ void WriteRepeatedScalarAccessor(const protobuf::Descriptor* message,
)cc",
class_name, CppConstType(field), resolved_field_name,
MessageName(message), upbc_name);
output(
R"cc(
const ::protos::RepeatedField<$1>::CProxy $0::$2() const {
size_t size;
const upb_Array* arr = _$3_$4_$5(msg_, &size);
return ::protos::RepeatedField<$1>::CProxy(arr);
};
::protos::Ptr<::protos::RepeatedField<$1>> $0::mutable_$2() {
size_t size;
upb_Array* arr = _$3_$4_$6(msg_, &size, arena_);
return ::protos::RepeatedField<$1>::Proxy(arr, arena_);
}
)cc",
class_name, // $0
CppConstType(field), // $1
resolved_field_name, // $2
MessageName(message), // $3
upbc_name, // $4
upbc::kRepeatedFieldArrayGetterPostfix, // $5
upbc::kRepeatedFieldMutableArrayGetterPostfix // $6
);
}
} // namespace protos_generator

@ -130,6 +130,7 @@ void WriteHeader(const protobuf::FileDescriptor* file, Output& output) {
#include "protos/protos.h"
#include "protos/protos_internal.h"
#include "protos/repeated_field.h"
#include "upb/upb.hpp"
#include "absl/strings/string_view.h"

@ -149,5 +149,6 @@ cc_test(
"@com_google_googletest//:gtest_main",
"//:upb",
"//protos",
"//protos:repeated_field",
],
)

@ -29,6 +29,8 @@
#include "gtest/gtest.h"
#include "protos/protos.h"
#include "protos/repeated_field.h"
#include "protos/repeated_field_iterator.h"
#include "protos_generator/tests/child_model.upb.proto.h"
#include "protos_generator/tests/no_package.upb.proto.h"
#include "protos_generator/tests/test_model.upb.proto.h"
@ -333,29 +335,29 @@ TEST(CppGeneratedCode, NestedMessages) {
TEST(CppGeneratedCode, RepeatedMessages) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.child_model_2_size());
EXPECT_EQ(0, test_model.child_models_size());
// Should be able to clear repeated field when empty.
test_model.clear_child_model_2();
EXPECT_EQ(0, test_model.child_model_2_size());
test_model.mutable_child_models()->clear();
EXPECT_EQ(0, test_model.child_models_size());
// Add 2 children.
auto new_child = test_model.add_child_model_2();
auto new_child = test_model.add_child_models();
EXPECT_EQ(true, new_child.ok());
new_child.value()->set_child_str1(kTestStr1);
new_child = test_model.add_child_model_2();
new_child = test_model.add_child_models();
EXPECT_EQ(true, new_child.ok());
new_child.value()->set_child_str1(kTestStr2);
EXPECT_EQ(2, test_model.child_model_2_size());
EXPECT_EQ(2, test_model.child_models_size());
// Mutable access.
auto mutable_first = test_model.mutable_child_model_2(0);
auto mutable_first = test_model.mutable_child_models(0);
EXPECT_EQ(mutable_first->child_str1(), kTestStr1);
mutable_first->set_child_str1("change1");
auto mutable_second = test_model.mutable_child_model_2(1);
auto mutable_second = test_model.mutable_child_models(1);
EXPECT_EQ(mutable_second->child_str1(), kTestStr2);
mutable_second->set_child_str1("change2");
// Check mutations using views.
auto view_first = test_model.child_model_2(0);
auto view_first = test_model.child_models(0);
EXPECT_EQ(view_first->child_str1(), "change1");
auto view_second = test_model.child_model_2(1);
auto view_second = test_model.child_models(1);
EXPECT_EQ(view_second->child_str1(), "change2");
}
@ -364,7 +366,7 @@ TEST(CppGeneratedCode, RepeatedScalar) {
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.value_array_size());
// Should be able to clear repeated field when empty.
test_model.clear_value_array();
test_model.mutable_value_array()->clear();
EXPECT_EQ(0, test_model.value_array_size());
// Add 2 children.
EXPECT_EQ(true, test_model.add_value_array(5));
@ -380,12 +382,173 @@ TEST(CppGeneratedCode, RepeatedScalar) {
EXPECT_EQ(7, test_model.value_array(2));
}
TEST(CppGeneratedCode, RepeatedFieldClear) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
test_model.mutable_value_array()->push_back(5);
test_model.mutable_value_array()->push_back(16);
test_model.mutable_value_array()->push_back(27);
ASSERT_EQ(test_model.mutable_value_array()->size(), 3);
test_model.mutable_value_array()->clear();
EXPECT_EQ(test_model.mutable_value_array()->size(), 0);
}
TEST(CppGeneratedCode, RepeatedFieldProxyForScalars) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.value_array().size());
EXPECT_EQ(0, test_model.mutable_value_array()->size());
test_model.mutable_value_array()->push_back(5);
test_model.mutable_value_array()->push_back(16);
test_model.mutable_value_array()->push_back(27);
ASSERT_EQ(test_model.mutable_value_array()->size(), 3);
EXPECT_EQ((*test_model.mutable_value_array())[0], 5);
EXPECT_EQ((*test_model.mutable_value_array())[1], 16);
EXPECT_EQ((*test_model.mutable_value_array())[2], 27);
ASSERT_EQ(test_model.value_array().size(), 3);
EXPECT_EQ(test_model.value_array()[0], 5);
EXPECT_EQ(test_model.value_array()[1], 16);
EXPECT_EQ(test_model.value_array()[2], 27);
}
TEST(CppGeneratedCode, RepeatedScalarIterator) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
test_model.mutable_value_array()->push_back(5);
test_model.mutable_value_array()->push_back(16);
test_model.mutable_value_array()->push_back(27);
int sum = 0;
// Access by value.
const ::protos::RepeatedField<int32_t>::CProxy rep1 =
test_model.value_array();
for (auto i : rep1) {
sum += i;
}
EXPECT_EQ(sum, 5 + 16 + 27);
// Access by const reference.
sum = 0;
for (const int& i : *test_model.mutable_value_array()) {
sum += i;
}
EXPECT_EQ(sum, 5 + 16 + 27);
// Access by forwarding reference.
sum = 0;
for (auto&& i : *test_model.mutable_value_array()) {
sum += i;
}
EXPECT_EQ(sum, 5 + 16 + 27);
// Test iterator operators.
auto begin = test_model.value_array().begin();
auto end = test_model.value_array().end();
sum = 0;
for (auto it = begin; it != end; ++it) {
sum += *it;
}
EXPECT_EQ(sum, 5 + 16 + 27);
auto it = begin;
++it;
EXPECT_TRUE(begin < it);
EXPECT_TRUE(begin <= it);
it = end;
EXPECT_TRUE(it == end);
EXPECT_TRUE(it > begin);
EXPECT_TRUE(it >= begin);
EXPECT_TRUE(it != begin);
// difference type
it = end;
--it;
--it;
EXPECT_EQ(end - it, 2);
it = begin;
EXPECT_EQ(it[0], 5);
EXPECT_EQ(it[1], 16);
EXPECT_EQ(it[2], 27);
// ValueProxy.
sum = 0;
for (::protos::RepeatedField<int32_t>::ValueCProxy c :
test_model.value_array()) {
sum += c;
}
EXPECT_EQ(sum, 5 + 16 + 27);
sum = 0;
for (::protos::RepeatedField<int32_t>::ValueProxy c :
*test_model.mutable_value_array()) {
sum += c;
}
EXPECT_EQ(sum, 5 + 16 + 27);
}
TEST(CppGeneratedCode, RepeatedFieldProxyForStrings) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.repeated_string().size());
EXPECT_EQ(0, test_model.mutable_repeated_string()->size());
test_model.mutable_repeated_string()->push_back("a");
test_model.mutable_repeated_string()->push_back("b");
test_model.mutable_repeated_string()->push_back("c");
ASSERT_EQ(test_model.repeated_string().size(), 3);
EXPECT_EQ(test_model.repeated_string()[0], "a");
EXPECT_EQ(test_model.repeated_string()[1], "b");
EXPECT_EQ(test_model.repeated_string()[2], "c");
ASSERT_EQ(test_model.mutable_repeated_string()->size(), 3);
EXPECT_EQ((*test_model.mutable_repeated_string())[0], "a");
EXPECT_EQ((*test_model.mutable_repeated_string())[1], "b");
EXPECT_EQ((*test_model.mutable_repeated_string())[2], "c");
test_model.mutable_repeated_string()->clear();
EXPECT_EQ(test_model.mutable_repeated_string()->size(), 0);
}
TEST(CppGeneratedCode, RepeatedFieldProxyForMessages) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.child_models().size());
ChildModel1 child1;
child1.set_child_str1(kTestStr1);
test_model.mutable_child_models()->push_back(child1);
ChildModel1 child2;
child2.set_child_str1(kTestStr2);
test_model.mutable_child_models()->push_back(std::move(child2));
EXPECT_EQ(test_model.child_models().size(), 2);
EXPECT_EQ(test_model.child_models()[0].child_str1(), kTestStr1);
EXPECT_EQ(test_model.child_models()[1].child_str1(), kTestStr2);
EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), kTestStr1);
EXPECT_EQ((*test_model.mutable_child_models())[1].child_str1(), kTestStr2);
(*test_model.mutable_child_models())[0].set_child_str1("change1");
EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1");
test_model.mutable_child_models()->clear();
EXPECT_EQ(test_model.mutable_child_models()->size(), 0);
}
TEST(CppGeneratedCode, RepeatedFieldProxyForMessagesIndexOperator) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.child_models().size());
ChildModel1 child1;
child1.set_child_str1(kTestStr1);
test_model.mutable_child_models()->push_back(child1);
ChildModel1 child2;
child2.set_child_str1(kTestStr2);
test_model.mutable_child_models()->push_back(std::move(child2));
ASSERT_EQ(test_model.child_models().size(), 2);
// test_model.child_models()[0].set_child_str1("change1");
(*test_model.mutable_child_models())[0].set_child_str1("change1");
EXPECT_EQ((*test_model.mutable_child_models())[0].child_str1(), "change1");
}
TEST(CppGeneratedCode, RepeatedStrings) {
::protos::Arena arena;
auto test_model = ::protos::CreateMessage<TestModel>(arena);
EXPECT_EQ(0, test_model.repeated_string_size());
// Should be able to clear repeated field when empty.
test_model.clear_repeated_string();
test_model.mutable_repeated_string()->clear();
EXPECT_EQ(0, test_model.repeated_string_size());
// Add 2 children.
EXPECT_EQ(true, test_model.add_repeated_string("Hello"));

@ -43,7 +43,7 @@ message TestModel {
}
optional NestedChild nested_child_1 = 212;
optional ChildModel1 child_model_1 = 222;
repeated ChildModel1 child_model_2 = 223;
repeated ChildModel1 child_models = 223;
optional ChildModel1 bar = 224;
oneof child_oneof1 {
string oneof_member1 = 98;

@ -50,6 +50,12 @@ upb_Array* upb_Array_New(upb_Arena* a, upb_CType type) {
return _upb_Array_New(a, 4, _upb_Array_CTypeSizeLg2(type));
}
const void* upb_Array_DataPtr(const upb_Array* arr) {
return _upb_array_ptr((upb_Array*)arr);
}
void* upb_Array_MutableDataPtr(upb_Array* arr) { return _upb_array_ptr(arr); }
size_t upb_Array_Size(const upb_Array* arr) { return arr->size; }
upb_MessageValue upb_Array_Get(const upb_Array* arr, size_t i) {

@ -76,6 +76,12 @@ UPB_API void upb_Array_Delete(upb_Array* array, size_t i, size_t count);
// Returns false on allocation failure.
UPB_API bool upb_Array_Resize(upb_Array* array, size_t size, upb_Arena* arena);
// Returns pointer to array data.
UPB_API const void* upb_Array_DataPtr(const upb_Array* arr);
// Returns mutable pointer to array data.
UPB_API void* upb_Array_MutableDataPtr(upb_Array* arr);
#ifdef __cplusplus
} /* extern "C" */
#endif

Loading…
Cancel
Save