diff --git a/common/values/list_value.cc b/common/values/list_value.cc index f996d30cf..0193b8f83 100644 --- a/common/values/list_value.cc +++ b/common/values/list_value.cc @@ -21,9 +21,8 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/cord.h" -#include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "absl/types/variant.h" +#include "common/native_type.h" #include "common/optional_ref.h" #include "common/value.h" #include "common/values/value_variant.h" @@ -34,58 +33,60 @@ namespace cel { -absl::string_view ListValue::GetTypeName() const { - return absl::visit( - [](const auto& alternative) -> absl::string_view { - return alternative.GetTypeName(); - }, - variant_); +NativeTypeId ListValue::GetTypeId() const { + return variant_.Visit([](const auto& alternative) -> NativeTypeId { + return NativeTypeId::Of(alternative); + }); } std::string ListValue::DebugString() const { - return absl::visit( - [](const auto& alternative) -> std::string { - return alternative.DebugString(); - }, - variant_); + return variant_.Visit([](const auto& alternative) -> std::string { + return alternative.DebugString(); + }); } absl::Status ListValue::SerializeTo( absl::Nonnull descriptor_pool, absl::Nonnull message_factory, absl::Nonnull value) const { - return absl::visit( - [descriptor_pool, message_factory, - value](const auto& alternative) -> absl::Status { - return alternative.SerializeTo(descriptor_pool, message_factory, value); - }, - variant_); + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(value != nullptr); + + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.SerializeTo(descriptor_pool, message_factory, value); + }); } absl::Status ListValue::ConvertToJson( absl::Nonnull descriptor_pool, absl::Nonnull message_factory, absl::Nonnull json) const { - return absl::visit( - [descriptor_pool, message_factory, - json](const auto& alternative) -> absl::Status { - return alternative.ConvertToJson(descriptor_pool, message_factory, - json); - }, - variant_); + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(json != nullptr); + ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), + google::protobuf::Descriptor::WELLKNOWNTYPE_VALUE); + + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.ConvertToJson(descriptor_pool, message_factory, json); + }); } absl::Status ListValue::ConvertToJsonArray( absl::Nonnull descriptor_pool, absl::Nonnull message_factory, absl::Nonnull json) const { - return absl::visit( - [descriptor_pool, message_factory, - json](const auto& alternative) -> absl::Status { - return alternative.ConvertToJsonArray(descriptor_pool, message_factory, - json); - }, - variant_); + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(json != nullptr); + ABSL_DCHECK_EQ(json->GetDescriptor()->well_known_type(), + google::protobuf::Descriptor::WELLKNOWNTYPE_LISTVALUE); + + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.ConvertToJsonArray(descriptor_pool, message_factory, + json); + }); } absl::Status ListValue::Equal( @@ -98,43 +99,43 @@ absl::Status ListValue::Equal( ABSL_DCHECK(arena != nullptr); ABSL_DCHECK(result != nullptr); - return absl::visit( - [&other, descriptor_pool, message_factory, arena, - result](const auto& alternative) -> absl::Status { - return alternative.Equal(other, descriptor_pool, message_factory, arena, - result); - }, - variant_); + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.Equal(other, descriptor_pool, message_factory, arena, + result); + }); } bool ListValue::IsZeroValue() const { - return absl::visit( - [](const auto& alternative) -> bool { return alternative.IsZeroValue(); }, - variant_); + return variant_.Visit([](const auto& alternative) -> bool { + return alternative.IsZeroValue(); + }); } absl::StatusOr ListValue::IsEmpty() const { - return absl::visit( - [](const auto& alternative) -> bool { return alternative.IsEmpty(); }, - variant_); + return variant_.Visit([](const auto& alternative) -> absl::StatusOr { + return alternative.IsEmpty(); + }); } absl::StatusOr ListValue::Size() const { - return absl::visit( - [](const auto& alternative) -> size_t { return alternative.Size(); }, - variant_); + return variant_.Visit([](const auto& alternative) -> absl::StatusOr { + return alternative.Size(); + }); } absl::Status ListValue::Get( size_t index, absl::Nonnull descriptor_pool, absl::Nonnull message_factory, absl::Nonnull arena, absl::Nonnull result) const { - return absl::visit( - [&](const auto& alternative) -> absl::Status { - return alternative.Get(index, descriptor_pool, message_factory, arena, - result); - }, - variant_); + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(arena != nullptr); + ABSL_DCHECK(result != nullptr); + + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.Get(index, descriptor_pool, message_factory, arena, + result); + }); } absl::Status ListValue::ForEach( @@ -142,21 +143,21 @@ absl::Status ListValue::ForEach( absl::Nonnull descriptor_pool, absl::Nonnull message_factory, absl::Nonnull arena) const { - return absl::visit( - [&](const auto& alternative) -> absl::Status { - return alternative.ForEach(callback, descriptor_pool, message_factory, - arena); - }, - variant_); + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(arena != nullptr); + + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.ForEach(callback, descriptor_pool, message_factory, + arena); + }); } absl::StatusOr> ListValue::NewIterator() const { - return absl::visit( - [](const auto& alternative) - -> absl::StatusOr> { - return alternative.NewIterator(); - }, - variant_); + return variant_.Visit([](const auto& alternative) + -> absl::StatusOr> { + return alternative.NewIterator(); + }); } absl::Status ListValue::Contains( @@ -164,12 +165,15 @@ absl::Status ListValue::Contains( absl::Nonnull descriptor_pool, absl::Nonnull message_factory, absl::Nonnull arena, absl::Nonnull result) const { - return absl::visit( - [&](const auto& alternative) -> absl::Status { - return alternative.Contains(other, descriptor_pool, message_factory, - arena, result); - }, - variant_); + ABSL_DCHECK(descriptor_pool != nullptr); + ABSL_DCHECK(message_factory != nullptr); + ABSL_DCHECK(arena != nullptr); + ABSL_DCHECK(result != nullptr); + + return variant_.Visit([&](const auto& alternative) -> absl::Status { + return alternative.Contains(other, descriptor_pool, message_factory, arena, + result); + }); } namespace common_internal { @@ -255,45 +259,46 @@ absl::Status ListValueEqual( } // namespace common_internal optional_ref ListValue::AsCustom() const& { - if (const auto* alt = absl::get_if(&variant_); - alt != nullptr) { - return *alt; + if (const auto* alternative = variant_.As(); + alternative != nullptr) { + return *alternative; } return absl::nullopt; } absl::optional ListValue::AsCustom() && { - if (auto* alt = absl::get_if(&variant_); alt != nullptr) { - return std::move(*alt); + if (auto* alternative = variant_.As(); + alternative != nullptr) { + return std::move(*alternative); } return absl::nullopt; } const CustomListValue& ListValue::GetCustom() const& { ABSL_DCHECK(IsCustom()); - return absl::get(variant_); + + return variant_.Get(); } CustomListValue ListValue::GetCustom() && { ABSL_DCHECK(IsCustom()); - return absl::get(std::move(variant_)); + + return std::move(variant_).Get(); } common_internal::ValueVariant ListValue::ToValueVariant() const& { - return absl::visit( + return variant_.Visit( [](const auto& alternative) -> common_internal::ValueVariant { return common_internal::ValueVariant(alternative); - }, - variant_); + }); } common_internal::ValueVariant ListValue::ToValueVariant() && { - return absl::visit( + return std::move(variant_).Visit( [](auto&& alternative) -> common_internal::ValueVariant { // NOLINTNEXTLINE(bugprone-move-forwarding-reference) return common_internal::ValueVariant(std::move(alternative)); - }, - std::move(variant_)); + }); } } // namespace cel diff --git a/common/values/list_value.h b/common/values/list_value.h index 346bd3923..fb69a141b 100644 --- a/common/values/list_value.h +++ b/common/values/list_value.h @@ -31,21 +31,19 @@ #include "absl/base/attributes.h" #include "absl/base/nullability.h" -#include "absl/log/absl_check.h" #include "absl/meta/type_traits.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" -#include "absl/types/variant.h" #include "absl/utility/utility.h" -#include "common/arena.h" #include "common/native_type.h" #include "common/optional_ref.h" #include "common/value_kind.h" #include "common/values/custom_list_value.h" #include "common/values/legacy_list_value.h" +#include "common/values/list_value_variant.h" #include "common/values/parsed_json_list_value.h" #include "common/values/parsed_repeated_field_value.h" #include "common/values/values.h" @@ -62,19 +60,8 @@ class TypeManager; class ListValue final : private common_internal::ListValueMixin { public: - using interface_type = ListValueInterface; - static constexpr ValueKind kKind = CustomListValueInterface::kKind; - // Copy constructor for alternative struct values. - template < - typename T, - typename = std::enable_if_t< - common_internal::IsListValueAlternativeV>>> - // NOLINTNEXTLINE(google-explicit-constructor) - ListValue(const T& value) - : variant_(absl::in_place_type>, value) {} - // Move constructor for alternative struct values. template < typename T, @@ -88,42 +75,14 @@ class ListValue final : private common_internal::ListValueMixin { ListValue() = default; ListValue(const ListValue&) = default; ListValue(ListValue&&) = default; + ListValue& operator=(const ListValue&) = default; + ListValue& operator=(ListValue&&) = default; - // NOLINTNEXTLINE(google-explicit-constructor) - ListValue(const ParsedRepeatedFieldValue& other) - : variant_(absl::in_place_type, other) {} + static constexpr ValueKind kind() { return kKind; } - // NOLINTNEXTLINE(google-explicit-constructor) - ListValue(ParsedRepeatedFieldValue&& other) - : variant_(absl::in_place_type, - std::move(other)) {} + static absl::string_view GetTypeName() { return "list"; } - // NOLINTNEXTLINE(google-explicit-constructor) - ListValue(const ParsedJsonListValue& other) - : variant_(absl::in_place_type, other) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - ListValue(ParsedJsonListValue&& other) - : variant_(absl::in_place_type, std::move(other)) {} - - ListValue& operator=(const ListValue& other) { - ABSL_DCHECK(this != std::addressof(other)) - << "ListValue should not be copied to itself"; - variant_ = other.variant_; - return *this; - } - - ListValue& operator=(ListValue&& other) noexcept { - ABSL_DCHECK(this != std::addressof(other)) - << "ListValue should not be moved to itself"; - variant_ = std::move(other.variant_); - other.variant_.emplace(); - return *this; - } - - constexpr ValueKind kind() const { return kKind; } - - absl::string_view GetTypeName() const; + NativeTypeId GetTypeId() const; std::string DebugString() const; @@ -155,8 +114,6 @@ class ListValue final : private common_internal::ListValueMixin { bool IsZeroValue() const; - void swap(ListValue& other) noexcept { variant_.swap(other.variant_); } - absl::StatusOr IsEmpty() const; absl::StatusOr Size() const; @@ -191,9 +148,7 @@ class ListValue final : private common_internal::ListValueMixin { using ListValueMixin::Contains; // Returns `true` if this value is an instance of a custom list value. - bool IsCustom() const { - return absl::holds_alternative(variant_); - } + bool IsCustom() const { return variant_.Is(); } // Convenience method for use with template metaprogramming. See // `IsParsed()`. @@ -277,12 +232,15 @@ class ListValue final : private common_internal::ListValueMixin { return std::move(*this).GetCustom(); } + friend void swap(ListValue& lhs, ListValue& rhs) noexcept { + using std::swap; + swap(lhs.variant_, rhs.variant_); + } + private: friend class Value; - friend struct NativeTypeTraits; friend class common_internal::ValueMixin; friend class common_internal::ListValueMixin; - friend struct ArenaTraits; common_internal::ValueVariant ToValueVariant() const&; common_internal::ValueVariant ToValueVariant() &&; @@ -294,32 +252,13 @@ class ListValue final : private common_internal::ListValueMixin { common_internal::ListValueVariant variant_; }; -inline void swap(ListValue& lhs, ListValue& rhs) noexcept { lhs.swap(rhs); } - inline std::ostream& operator<<(std::ostream& out, const ListValue& value) { return out << value.DebugString(); } template <> struct NativeTypeTraits final { - static NativeTypeId Id(const ListValue& value) { - return absl::visit( - [](const auto& alternative) -> NativeTypeId { - return NativeTypeId::Of(alternative); - }, - value.variant_); - } -}; - -template <> -struct ArenaTraits { - static bool trivially_destructible(const ListValue& value) { - return absl::visit( - [](const auto& alternative) -> bool { - return ArenaTraits<>::trivially_destructible(alternative); - }, - value.variant_); - } + static NativeTypeId Id(const ListValue& value) { return value.GetTypeId(); } }; class ListValueBuilder { diff --git a/common/values/list_value_variant.h b/common/values/list_value_variant.h new file mode 100644 index 000000000..c1db8dda0 --- /dev/null +++ b/common/values/list_value_variant.h @@ -0,0 +1,214 @@ +// Copyright 2025 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 +// +// https://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 THIRD_PARTY_CEL_CPP_COMMON_VALUES_LIST_VALUE_VARIANT_H_ +#define THIRD_PARTY_CEL_CPP_COMMON_VALUES_LIST_VALUE_VARIANT_H_ + +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/nullability.h" +#include "absl/log/absl_check.h" +#include "absl/meta/type_traits.h" +#include "absl/utility/utility.h" +#include "common/values/custom_list_value.h" +#include "common/values/legacy_list_value.h" +#include "common/values/parsed_json_list_value.h" +#include "common/values/parsed_repeated_field_value.h" + +namespace cel::common_internal { + +enum class ListValueIndex : uint16_t { + kCustom = 0, + kParsedField, + kParsedJson, + kLegacy, +}; + +template +struct ListValueAlternative; + +template <> +struct ListValueAlternative { + static constexpr ListValueIndex kIndex = ListValueIndex::kCustom; +}; + +template <> +struct ListValueAlternative { + static constexpr ListValueIndex kIndex = ListValueIndex::kParsedField; +}; + +template <> +struct ListValueAlternative { + static constexpr ListValueIndex kIndex = ListValueIndex::kParsedJson; +}; + +template <> +struct ListValueAlternative { + static constexpr ListValueIndex kIndex = ListValueIndex::kLegacy; +}; + +template +struct IsListValueAlternative : std::false_type {}; + +template +struct IsListValueAlternative{})>> + : std::true_type {}; + +template +inline constexpr bool IsListValueAlternativeV = + IsListValueAlternative::value; + +inline constexpr size_t kListValueVariantAlign = 8; +inline constexpr size_t kListValueVariantSize = 24; + +// ListValueVariant is a subset of alternatives from the main ValueVariant that +// is only lists. It is not stored directly in ValueVariant. +class alignas(kListValueVariantAlign) ListValueVariant final { + public: + ListValueVariant() : ListValueVariant(absl::in_place_type) {} + + ListValueVariant(const ListValueVariant&) = default; + ListValueVariant(ListValueVariant&&) = default; + ListValueVariant& operator=(const ListValueVariant&) = default; + ListValueVariant& operator=(ListValueVariant&&) = default; + + template + explicit ListValueVariant(absl::in_place_type_t, Args&&... args) + : index_(ListValueAlternative::kIndex) { + static_assert(alignof(T) <= kListValueVariantAlign); + static_assert(sizeof(T) <= kListValueVariantSize); + static_assert(std::is_trivially_copyable_v); + + ::new (static_cast(&raw_[0])) T(std::forward(args)...); + } + + template >>> + explicit ListValueVariant(T&& value) + : ListValueVariant(absl::in_place_type>, + std::forward(value)) {} + + template + void Assign(T&& value) { + using U = absl::remove_cvref_t; + + static_assert(alignof(U) <= kListValueVariantAlign); + static_assert(sizeof(U) <= kListValueVariantSize); + static_assert(std::is_trivially_copyable_v); + + index_ = ListValueAlternative::kIndex; + ::new (static_cast(&raw_[0])) U(std::forward(value)); + } + + template + bool Is() const { + return index_ == ListValueAlternative::kIndex; + } + + template + T& Get() & ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_DCHECK(Is()); + + return *At(); + } + + template + const T& Get() const& ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_DCHECK(Is()); + + return *At(); + } + + template + T&& Get() && ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_DCHECK(Is()); + + return std::move(*At()); + } + + template + const T&& Get() const&& ABSL_ATTRIBUTE_LIFETIME_BOUND { + ABSL_DCHECK(Is()); + + return std::move(*At()); + } + + template + absl::Nullable As() ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (Is()) { + return At(); + } + return nullptr; + } + + template + absl::Nullable As() const ABSL_ATTRIBUTE_LIFETIME_BOUND { + if (Is()) { + return At(); + } + return nullptr; + } + + template + decltype(auto) Visit(Visitor&& visitor) const { + switch (index_) { + case ListValueIndex::kCustom: + return std::forward(visitor)(Get()); + case ListValueIndex::kParsedField: + return std::forward(visitor)(Get()); + case ListValueIndex::kParsedJson: + return std::forward(visitor)(Get()); + case ListValueIndex::kLegacy: + return std::forward(visitor)(Get()); + } + } + + friend void swap(ListValueVariant& lhs, ListValueVariant& rhs) noexcept { + using std::swap; + swap(lhs.index_, rhs.index_); + swap(lhs.raw_, rhs.raw_); + } + + private: + template + ABSL_ATTRIBUTE_ALWAYS_INLINE absl::Nonnull At() + ABSL_ATTRIBUTE_LIFETIME_BOUND { + static_assert(alignof(T) <= kListValueVariantAlign); + static_assert(sizeof(T) <= kListValueVariantSize); + static_assert(std::is_trivially_copyable_v); + + return std::launder(reinterpret_cast(&raw_[0])); + } + + template + ABSL_ATTRIBUTE_ALWAYS_INLINE absl::Nonnull At() const + ABSL_ATTRIBUTE_LIFETIME_BOUND { + static_assert(alignof(T) <= kListValueVariantAlign); + static_assert(sizeof(T) <= kListValueVariantSize); + static_assert(std::is_trivially_copyable_v); + + return std::launder(reinterpret_cast(&raw_[0])); + } + + ListValueIndex index_ = ListValueIndex::kCustom; + alignas(8) std::byte raw_[kListValueVariantSize]; +}; + +} // namespace cel::common_internal + +#endif // THIRD_PARTY_CEL_CPP_COMMON_VALUES_LIST_VALUE_VARIANT_H_ diff --git a/common/values/values.h b/common/values/values.h index 41366cbb1..41be6db38 100644 --- a/common/values/values.h +++ b/common/values/values.h @@ -121,21 +121,7 @@ class LegacyMapValue; class LegacyStructValue; -using ListValueVariant = - absl::variant; - -template > -struct IsListValueAlternative - : std::bool_constant< - std::disjunction_v, - std::is_same, - std::is_same, - std::is_same>> {}; - -template -inline constexpr bool IsListValueAlternativeV = - IsListValueAlternative::value; +class ListValueVariant; using MapValueVariant = absl::variant;