/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace apache { namespace thrift { namespace detail { template using is_set_t = std::conditional_t::value, const uint8_t, uint8_t>; [[noreturn]] void throw_on_bad_optional_field_access(); [[noreturn]] void throw_on_bad_union_field_access(); [[noreturn]] void throw_on_nullptr_dereferencing(); struct ensure_isset_unsafe_fn; struct unset_unsafe_fn; struct alias_isset_fn; struct move_to_unique_ptr_fn; struct assign_from_unique_ptr_fn; struct union_value_unsafe_fn; // IntWrapper is a wrapper of integer that's always copy/move assignable // even if integer is atomic template struct IntWrapper { IntWrapper() = default; explicit IntWrapper(T t) : value(t) {} T value{0}; }; // If the integer is atomic, operations use only relaxed memory-order since the // requirement is only to protect the integer against torn reads and writes and // not to protect any other objects. template struct IntWrapper> { IntWrapper() = default; IntWrapper(const IntWrapper& other) noexcept : value(other.value.load(std::memory_order_relaxed)) {} IntWrapper(IntWrapper&& other) noexcept : value(other.value.load(std::memory_order_relaxed)) {} IntWrapper& operator=(const IntWrapper& other) noexcept { value.store( other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } IntWrapper& operator=(IntWrapper&& other) noexcept { value.store( other.value.load(std::memory_order_relaxed), std::memory_order_relaxed); return *this; } std::atomic value{0}; }; template class BitSet { public: BitSet() = default; explicit BitSet(T value) : int_(value) {} BitSet(const BitSet&) = default; BitSet& operator=(const BitSet& other) = default; class reference { public: reference(BitSet& bitSet, const uint8_t bit) : bitSet_(bitSet), bit_(bit) {} reference& operator=(bool flag) { if (flag) { bitSet_.set(bit_); } else { bitSet_.reset(bit_); } return *this; } operator bool() const { return bitSet_.get(bit_); } reference& operator=(reference& other) { return *this = bool(other); } private: BitSet& bitSet_; const uint8_t bit_; }; bool operator[](const uint8_t bit) const { assert(bit < NUM_BITS); return get(bit); } reference operator[](const uint8_t bit) { assert(bit < NUM_BITS); return reference(*this, bit); } T& value() { return int_.value; } const T& value() const { return int_.value; } private: template static bool get(U u, std::size_t bit) { return u & (U(1) << bit); } template static void set(U& u, std::size_t bit) { u |= (U(1) << bit); } template static void reset(U& u, std::size_t bit) { u &= ~(U(1) << bit); } // If the integer is atomic, operations use only relaxed memory-order since // the requirement is only to protect the integer against torn reads and // writes and not to protect any other objects. template static bool get(const std::atomic& u, std::size_t bit) { return u.load(std::memory_order_relaxed) & (U(1) << bit); } template static void set(std::atomic& u, std::size_t bit) { folly::atomic_fetch_set(u, bit, std::memory_order_relaxed); } template static void reset(std::atomic& u, std::size_t bit) { folly::atomic_fetch_reset(u, bit, std::memory_order_relaxed); } bool get(std::size_t bit) const { return get(int_.value, bit); } void set(std::size_t bit) { set(int_.value, bit); } void reset(std::size_t bit) { reset(int_.value, bit); } IntWrapper int_; static constexpr int NUM_BITS = sizeof(T) * CHAR_BIT; }; template class BitRef { template friend class BitRef; public: using Isset = std::conditional_t; using AtomicIsset = std:: conditional_t, std::atomic>; FOLLY_ERASE BitRef(Isset& isset, uint8_t bit_index) : value_(isset), bit_index_(bit_index) {} FOLLY_ERASE BitRef(AtomicIsset& isset, uint8_t bit_index) : value_(isset), bit_index_(bit_index), is_atomic_(true) {} template explicit BitRef(const BitRef& other) : value_( other.is_atomic_ ? IssetBitSet(other.value_.atomic.value()) : IssetBitSet(other.value_.non_atomic.value())), bit_index_(other.bit_index_), is_atomic_(other.is_atomic_) {} #if FOLLY_MOBILE // We have this attribute to prevent binary size regression // TODO: Remove special attribute for MOBILE FOLLY_ERASE #endif void operator=(bool flag) { if (is_atomic_) { value_.atomic[bit_index_] = flag; } else { value_.non_atomic[bit_index_] = flag; } } explicit operator bool() const { if (is_atomic_) { return value_.atomic[bit_index_]; } else { return value_.non_atomic[bit_index_]; } } private: union IssetBitSet { explicit IssetBitSet(Isset& isset) : non_atomic(isset) {} explicit IssetBitSet(AtomicIsset& isset) : atomic(isset) {} apache::thrift::detail::BitSet non_atomic; apache::thrift::detail::BitSet atomic; } value_; const uint8_t bit_index_; const bool is_atomic_ = false; }; template using EnableIfConst = std::enable_if_t::value, return_type>; template using EnableIfNonConst = std::enable_if_t::value, return_type>; template using EnableIfImplicit = std::enable_if_t< std::is_same< std::add_const_t>, std::remove_reference_t>{} && !(std::is_rvalue_reference{} && std::is_lvalue_reference{})>; } // namespace detail /// A reference to an unqualified field of the possibly const-qualified type /// std::remove_reference_t in a Thrift-generated struct. template class field_ref { static_assert(std::is_reference::value, "not a reference"); template friend class field_ref; friend struct apache::thrift::detail::unset_unsafe_fn; public: using value_type = std::remove_reference_t; using reference_type = T; private: using BitRef = apache::thrift::detail::BitRef::value>; public: /// Internal constructor FOLLY_ERASE field_ref( reference_type value, typename BitRef::Isset& is_set, const uint8_t bit_index = 0) noexcept : value_(value), bitref_(is_set, bit_index) {} /// Internal constructor FOLLY_ERASE field_ref( reference_type value, typename BitRef::AtomicIsset& is_set, const uint8_t bit_index = 0) noexcept : value_(value), bitref_(is_set, bit_index) {} template > FOLLY_ERASE /* implicit */ field_ref(const field_ref& other) noexcept : value_(other.value_), bitref_(other.bitref_) {} template FOLLY_ERASE std::enable_if_t::value, field_ref&> operator=(U&& value) noexcept( std::is_nothrow_assignable::value) { value_ = static_cast(value); bitref_ = true; return *this; } // Workaround for https://bugs.llvm.org/show_bug.cgi?id=49442 FOLLY_ERASE field_ref& operator=(value_type&& value) noexcept( std::is_nothrow_move_assignable::value) { value_ = static_cast(value); bitref_ = true; return *this; value.~value_type(); // Force emit destructor... } /// Assignment from field_ref is intentionally not provided to prevent /// potential confusion between two possible behaviors, copying and reference /// rebinding. The copy_from method is provided instead. template FOLLY_ERASE void copy_from(field_ref other) noexcept( std::is_nothrow_assignable::value) { value_ = other.value(); bitref_ = other.is_set(); } [[deprecated("Use is_set() method instead")]] FOLLY_ERASE bool has_value() const noexcept { return bool(bitref_); } /// Returns true iff the field is set. field_ref doesn't provide conversion to /// bool to avoid confusion between checking if the field is set and getting /// the field's value, particularly for bool fields. FOLLY_ERASE bool is_set() const noexcept { return bool(bitref_); } /// Returns a reference to the value. FOLLY_ERASE reference_type value() const noexcept { return static_cast(value_); } /// Returns a reference to the value. FOLLY_ERASE reference_type operator*() const noexcept { return static_cast(value_); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() const noexcept { return &value_; } /// Returns a pointer to the value. template FOLLY_ERASE detail::EnableIfConst* operator->() const noexcept { return &value_; } /// Returns a pointer to the value. FOLLY_ERASE value_type* operator->() noexcept { return &value_; } FOLLY_ERASE reference_type ensure() noexcept { bitref_ = true; return static_cast(value_); } /// Forward operator[] to the value. template FOLLY_ERASE auto operator[](const Index& index) const -> decltype(auto) { return value_[index]; } /// Constructs the value in-place. template FOLLY_ERASE value_type& emplace(Args&&... args) { bitref_ = false; // C++ Standard requires *this to be empty if // `std::optional::emplace(...)` throws value_ = value_type(static_cast(args)...); bitref_ = true; return value_; } /// Constructs the value in-place. template FOLLY_ERASE std::enable_if_t< std::is_constructible, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { bitref_ = false; value_ = value_type(ilist, static_cast(args)...); bitref_ = true; return value_; } private: value_type& value_; BitRef bitref_; }; template bool operator==(field_ref lhs, field_ref rhs) { return *lhs == *rhs; } template bool operator!=(field_ref lhs, field_ref rhs) { return *lhs != *rhs; } template bool operator<(field_ref lhs, field_ref rhs) { return *lhs < *rhs; } template bool operator>(field_ref lhs, field_ref rhs) { return *lhs > *rhs; } template bool operator<=(field_ref lhs, field_ref rhs) { return *lhs <= *rhs; } template bool operator>=(field_ref lhs, field_ref rhs) { return *lhs >= *rhs; } template bool operator==(field_ref lhs, const U& rhs) { return *lhs == rhs; } template bool operator!=(field_ref lhs, const U& rhs) { return *lhs != rhs; } template bool operator<(field_ref lhs, const U& rhs) { return *lhs < rhs; } template bool operator>(field_ref lhs, const U& rhs) { return *lhs > rhs; } template bool operator<=(field_ref lhs, const U& rhs) { return *lhs <= rhs; } template bool operator>=(field_ref lhs, const U& rhs) { return *lhs >= rhs; } template bool operator==(const T& lhs, field_ref rhs) { return lhs == *rhs; } template bool operator!=(const T& lhs, field_ref rhs) { return lhs != *rhs; } template bool operator<(const T& lhs, field_ref rhs) { return lhs < *rhs; } template bool operator>(const T& lhs, field_ref rhs) { return lhs > *rhs; } template bool operator<=(const T& lhs, field_ref rhs) { return lhs <= *rhs; } template bool operator>=(const T& lhs, field_ref rhs) { return lhs >= *rhs; } // A reference to an optional field of the possibly const-qualified type // std::remove_reference_t in a Thrift-generated struct. template class optional_field_ref { static_assert(std::is_reference::value, "not a reference"); template friend class optional_field_ref; friend struct apache::thrift::detail::ensure_isset_unsafe_fn; friend struct apache::thrift::detail::unset_unsafe_fn; friend struct apache::thrift::detail::alias_isset_fn; public: using value_type = std::remove_reference_t; using reference_type = T; private: using BitRef = apache::thrift::detail::BitRef::value>; // for alias_isset_fn FOLLY_ERASE optional_field_ref(reference_type value, BitRef bitref) : value_(value), bitref_(bitref) {} public: FOLLY_ERASE optional_field_ref( reference_type value, typename BitRef::Isset& is_set, const uint8_t bit_index = 0) noexcept : value_(value), bitref_(is_set, bit_index) {} FOLLY_ERASE optional_field_ref( reference_type value, typename BitRef::AtomicIsset& is_set, const uint8_t bit_index = 0) noexcept : value_(value), bitref_(is_set, bit_index) {} template > FOLLY_ERASE /* implicit */ optional_field_ref( const optional_field_ref& other) noexcept : value_(other.value_), bitref_(other.bitref_) {} template < typename U, std::enable_if_t< std::is_same{} || std::is_same{}, int> = 0> FOLLY_ERASE explicit optional_field_ref( const optional_field_ref& other) noexcept : value_(other.value_), bitref_(other.bitref_) {} template FOLLY_ERASE std::enable_if_t< std::is_assignable::value, optional_field_ref&> operator=(U&& value) noexcept( std::is_nothrow_assignable::value) { value_ = static_cast(value); bitref_ = true; return *this; } // Workaround for https://bugs.llvm.org/show_bug.cgi?id=49442 FOLLY_ERASE optional_field_ref& operator=(value_type&& value) noexcept( std::is_nothrow_move_assignable::value) { value_ = static_cast(value); bitref_ = true; return *this; value.~value_type(); // Force emit destructor... } // Copies the data (the set flag and the value if available) from another // optional_field_ref object. // // Assignment from optional_field_ref is intentionally not provided to prevent // potential confusion between two possible behaviors, copying and reference // rebinding. This copy_from method is provided instead. template FOLLY_ERASE void copy_from(const optional_field_ref& other) noexcept( std::is_nothrow_assignable::value) { value_ = other.value_unchecked(); bitref_ = other.has_value(); } template FOLLY_ERASE void move_from(optional_field_ref other) noexcept( std::is_nothrow_assignable&&>:: value) { value_ = static_cast&&>(other.value_); bitref_ = other.has_value(); } template FOLLY_ERASE void from_optional(const std::optional& other) noexcept( std::is_nothrow_assignable::value) { // Use if instead of a shorter ternary expression to prevent a potential // copy if T and U mismatch. if (other) { value_ = *other; } else { value_ = {}; } bitref_ = other.has_value(); } // Moves the value from std::optional. As std::optional's move constructor, // move_from doesn't make other empty. template FOLLY_ERASE void from_optional(std::optional&& other) noexcept( std::is_nothrow_assignable::value) { // Use if instead of a shorter ternary expression to prevent a potential // copy if T and U mismatch. if (other) { value_ = static_cast(*other); } else { value_ = {}; } bitref_ = other.has_value(); } FOLLY_ERASE std::optional> to_optional() const { using type = std::optional>; return bitref_ ? type(value_) : type(); } FOLLY_ERASE bool has_value() const noexcept { return bool(bitref_); } FOLLY_ERASE explicit operator bool() const noexcept { return bool(bitref_); } FOLLY_ERASE void reset() noexcept( std::is_nothrow_move_assignable::value) { value_ = value_type(); bitref_ = false; } // Returns a reference to the value if this optional_field_ref has one; throws // bad_field_access otherwise. FOLLY_ERASE reference_type value() const { throw_if_unset(); return static_cast(value_); } template > FOLLY_ERASE std::remove_const_t value_or( U&& default_value) const { using type = std::remove_const_t; return bitref_ ? type(static_cast(value_)) : type(static_cast(default_value)); } // Returns a reference to the value without checking whether it is available. FOLLY_ERASE reference_type value_unchecked() const { return static_cast(value_); } FOLLY_ERASE reference_type operator*() const { return value(); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() const { throw_if_unset(); return &value_; } template FOLLY_ERASE detail::EnableIfConst* operator->() const { throw_if_unset(); return &value_; } FOLLY_ERASE value_type* operator->() { throw_if_unset(); return &value_; } FOLLY_ERASE reference_type ensure() noexcept(std::is_nothrow_move_assignable::value) { if (!bitref_) { value_ = value_type(); bitref_ = true; } return static_cast(value_); } template FOLLY_ERASE value_type& emplace(Args&&... args) { reset(); // C++ Standard requires *this to be empty if // `std::optional::emplace(...)` throws value_ = value_type(static_cast(args)...); bitref_ = true; return value_; } template FOLLY_ERASE std::enable_if_t< std::is_constructible&, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { reset(); value_ = value_type(ilist, static_cast(args)...); bitref_ = true; return value_; } private: FOLLY_ERASE void throw_if_unset() const { if (!bitref_) { apache::thrift::detail::throw_on_bad_optional_field_access(); } } value_type& value_; BitRef bitref_; }; template bool operator==(optional_field_ref a, optional_field_ref b) { return a && b ? *a == *b : a.has_value() == b.has_value(); } template bool operator!=(optional_field_ref a, optional_field_ref b) { return !(a == b); } template bool operator<(optional_field_ref a, optional_field_ref b) { if (a.has_value() != b.has_value()) { return a.has_value() < b.has_value(); } return a ? *a < *b : false; } template bool operator>(optional_field_ref a, optional_field_ref b) { return b < a; } template bool operator<=(optional_field_ref a, optional_field_ref b) { return !(a > b); } template bool operator>=(optional_field_ref a, optional_field_ref b) { return !(a < b); } template bool operator==(optional_field_ref a, const U& b) { return a ? *a == b : false; } template bool operator!=(optional_field_ref a, const U& b) { return !(a == b); } template bool operator==(const U& a, optional_field_ref b) { return b == a; } template bool operator!=(const U& a, optional_field_ref b) { return b != a; } template bool operator<(optional_field_ref a, const U& b) { return a ? *a < b : true; } template bool operator>(optional_field_ref a, const U& b) { return a ? *a > b : false; } template bool operator<=(optional_field_ref a, const U& b) { return !(a > b); } template bool operator>=(optional_field_ref a, const U& b) { return !(a < b); } template bool operator<(const U& a, optional_field_ref b) { return b > a; } template bool operator<=(const U& a, optional_field_ref b) { return b >= a; } template bool operator>(const U& a, optional_field_ref b) { return b < a; } template bool operator>=(const U& a, optional_field_ref b) { return b <= a; } template bool operator==(const optional_field_ref& a, std::nullopt_t) { return !a.has_value(); } template bool operator==(std::nullopt_t, const optional_field_ref& a) { return !a.has_value(); } template bool operator!=(const optional_field_ref& a, std::nullopt_t) { return a.has_value(); } template bool operator!=(std::nullopt_t, const optional_field_ref& a) { return a.has_value(); } namespace detail { template inline constexpr bool is_boxed_value_ptr_v = false; template inline constexpr bool is_boxed_value_ptr_v> = true; template inline constexpr bool is_boxed_value_v = false; template inline constexpr bool is_boxed_value_v> = true; template using copy_reference_t = std::conditional_t< std::is_lvalue_reference{}, std::add_lvalue_reference_t, std::add_rvalue_reference_t>; template using copy_const_t = std::conditional_t< std::is_const>{}, std::add_const_t, To>; } // namespace detail template class optional_boxed_field_ref { static_assert(std::is_reference::value, "not a reference"); static_assert( detail::is_boxed_value_ptr_v>, "not a boxed_value_ptr"); using element_type = typename folly::remove_cvref_t::element_type; template friend class optional_boxed_field_ref; friend struct apache::thrift::detail::move_to_unique_ptr_fn; friend struct apache::thrift::detail::assign_from_unique_ptr_fn; public: using value_type = detail::copy_const_t; using reference_type = detail::copy_reference_t; FOLLY_ERASE explicit optional_boxed_field_ref(T value) noexcept : value_(value) {} template > FOLLY_ERASE /* implicit */ optional_boxed_field_ref(const optional_boxed_field_ref& other) noexcept : value_(other.value_) {} template < typename U, std::enable_if_t< std::is_same{} || std::is_same{}, int> = 0> FOLLY_ERASE explicit optional_boxed_field_ref( const optional_boxed_field_ref& other) noexcept : value_(other.value_) {} template FOLLY_ERASE std::enable_if_t< std::is_assignable::value, optional_boxed_field_ref&> operator=(U&& value) { value_ = static_cast(value); return *this; } // Copies the data (the set flag and the value if available) from another // optional_boxed_field_ref object. // // Assignment from optional_boxed_field_ref is intentionally not provided to // prevent potential confusion between two possible behaviors, copying and // reference rebinding. This copy_from method is provided instead. template FOLLY_ERASE void copy_from(const optional_boxed_field_ref& other) { value_ = T(other.value_); } template FOLLY_ERASE void move_from(optional_boxed_field_ref other) noexcept { value_ = static_cast&&>(other.value_); } template FOLLY_ERASE void from_optional(const std::optional& other) { // Use if instead of a shorter ternary expression to prevent a potential // copy if T and U mismatch. if (other) { value_ = *other; } else { value_ = {}; } } // Moves the value from std::optional. As std::optional's move constructor, // move_from doesn't make other empty. template FOLLY_ERASE void from_optional(std::optional&& other) { // Use if instead of a shorter ternary expression to prevent a potential // copy if T and U mismatch. if (other) { value_ = static_cast(*other); } else { value_ = {}; } } FOLLY_ERASE std::optional> to_optional() const { using type = std::optional>; return has_value() ? type(*value_) : type(); } FOLLY_ERASE bool has_value() const noexcept { return static_cast(value_); } FOLLY_ERASE explicit operator bool() const noexcept { return has_value(); } FOLLY_ERASE void reset() noexcept { value_.reset(); } // Returns a reference to the value if this optional_boxed_field_ref has one; // throws bad_field_access otherwise. FOLLY_ERASE reference_type value() const { throw_if_unset(); return static_cast(*value_); } template > FOLLY_ERASE std::remove_const_t value_or( U&& default_value) const { using type = std::remove_const_t; return has_value() ? type(static_cast(*value_)) : type(static_cast(default_value)); } FOLLY_ERASE reference_type operator*() const { return value(); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() const { throw_if_unset(); return &*value_; } template FOLLY_ERASE detail::EnableIfConst* operator->() const { throw_if_unset(); return &*value_; } FOLLY_ERASE value_type* operator->() { throw_if_unset(); return &*value_; } FOLLY_ERASE reference_type ensure() { if (!has_value()) { emplace(); } return static_cast(*value_); } template FOLLY_ERASE value_type& emplace(Args&&... args) { reset(); // C++ Standard requires *this to be empty if // `std::optional::emplace(...)` throws value_ = value_type(static_cast(args)...); return *value_; } template FOLLY_ERASE std::enable_if_t< std::is_constructible&, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { reset(); value_ = value_type(ilist, static_cast(args)...); return *value_; } private: FOLLY_ERASE void throw_if_unset() const { if (!has_value()) { apache::thrift::detail::throw_on_bad_optional_field_access(); } } FOLLY_ERASE std::unique_ptr release() noexcept { return value_.release(); } FOLLY_ERASE void reset(std::unique_ptr ptr) noexcept { value_.reset(ptr.release()); } std::remove_reference_t& value_; }; template bool operator==( optional_boxed_field_ref a, optional_boxed_field_ref b) { return a && b ? *a == *b : a.has_value() == b.has_value(); } template bool operator!=( optional_boxed_field_ref a, optional_boxed_field_ref b) { return !(a == b); } template bool operator<(optional_boxed_field_ref a, optional_boxed_field_ref b) { if (a.has_value() != b.has_value()) { return a.has_value() < b.has_value(); } return a ? *a < *b : false; } template bool operator>(optional_boxed_field_ref a, optional_boxed_field_ref b) { return b < a; } template bool operator<=( optional_boxed_field_ref a, optional_boxed_field_ref b) { return !(a > b); } template bool operator>=( optional_boxed_field_ref a, optional_boxed_field_ref b) { return !(a < b); } template bool operator==(optional_boxed_field_ref a, const U& b) { return a ? *a == b : false; } template bool operator!=(optional_boxed_field_ref a, const U& b) { return !(a == b); } template bool operator==(const U& a, optional_boxed_field_ref b) { return b == a; } template bool operator!=(const U& a, optional_boxed_field_ref b) { return b != a; } template bool operator<(optional_boxed_field_ref a, const U& b) { return a ? *a < b : true; } template bool operator>(optional_boxed_field_ref a, const U& b) { return a ? *a > b : false; } template bool operator<=(optional_boxed_field_ref a, const U& b) { return !(a > b); } template bool operator>=(optional_boxed_field_ref a, const U& b) { return !(a < b); } template bool operator<(const U& a, optional_boxed_field_ref b) { return b > a; } template bool operator<=(const U& a, optional_boxed_field_ref b) { return b >= a; } template bool operator>(const U& a, optional_boxed_field_ref b) { return b < a; } template bool operator>=(const U& a, optional_boxed_field_ref b) { return b <= a; } template bool operator==(const optional_boxed_field_ref& a, std::nullopt_t) { return !a.has_value(); } template bool operator==(std::nullopt_t, const optional_boxed_field_ref& a) { return !a.has_value(); } template bool operator!=(const optional_boxed_field_ref& a, std::nullopt_t) { return a.has_value(); } template bool operator!=(std::nullopt_t, const optional_boxed_field_ref& a) { return a.has_value(); } // A reference to a 'Fill' intern boxed field. // // It currently only supports Thrift structs. template class intern_boxed_field_ref { static_assert(std::is_reference::value, "not a reference"); static_assert( detail::is_boxed_value_v>, "not a boxed_value"); using element_type = typename folly::remove_cvref_t::element_type; using boxed_value_type = std::remove_reference_t; template friend class intern_boxed_field_ref; // TODO(dokwon): Consider removing `get_default_t` after resolving // dependency issue. using get_default_t = folly::FunctionRef; public: using value_type = detail::copy_const_t; using reference_type = detail::copy_reference_t; private: using BitRef = apache::thrift::detail::BitRef::value>; public: FOLLY_ERASE intern_boxed_field_ref( T value, get_default_t get_default, typename BitRef::Isset& is_set, const uint8_t bit_index = 0) noexcept : value_(value), get_default_(get_default), bitref_(is_set, bit_index) {} FOLLY_ERASE intern_boxed_field_ref( T value, get_default_t get_default, typename BitRef::AtomicIsset& is_set, const uint8_t bit_index = 0) noexcept : value_(value), get_default_(get_default), bitref_(is_set, bit_index) {} template > FOLLY_ERASE /* implicit */ intern_boxed_field_ref( const intern_boxed_field_ref& other) noexcept : value_(other.value_), bitref_(other.bitref_) {} template FOLLY_ERASE std::enable_if_t< std::is_assignable::value, intern_boxed_field_ref&> operator=(U&& value) { value_.mut() = static_cast(value); bitref_ = true; return *this; } // Workaround for https://bugs.llvm.org/show_bug.cgi?id=49442 FOLLY_ERASE intern_boxed_field_ref& operator=(value_type&& value) noexcept( std::is_nothrow_move_assignable::value) { value_.mut() = static_cast(value); bitref_ = true; return *this; value.~value_type(); // Force emit destructor... } // If the other field owns the value, it will perform deep copy. If the other // field does not own the value, it will perform a shallow copy. template FOLLY_ERASE void copy_from(const intern_boxed_field_ref& other) { value_ = other.value_; bitref_ = other.is_set(); } [[deprecated("Use is_set() method instead")]] FOLLY_ERASE bool has_value() const noexcept { return bool(bitref_); } // Returns true iff the field is set. 'intern_boxed_field_ref' doesn't provide // conversion to bool to avoid confusion between checking if the field is set // and getting the field's value, particularly for bool fields. FOLLY_ERASE bool is_set() const noexcept { return bool(bitref_); } FOLLY_ERASE void reset() noexcept { // reset to the intern default. value_ = boxed_value_type::fromStaticConstant(&get_default_()); bitref_ = false; } template FOLLY_ERASE detail::EnableIfNonConst value() { return static_cast(value_.mut()); } template FOLLY_ERASE detail::EnableIfConst value() const { return static_cast(value_.value()); } FOLLY_ERASE reference_type ensure() noexcept { bitref_ = true; return static_cast(value_.mut()); } FOLLY_ERASE reference_type operator*() { return value(); } FOLLY_ERASE reference_type operator*() const { return value(); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() { return &value_.mut(); } template FOLLY_ERASE detail::EnableIfConst* operator->() const { return &value_.value(); } template FOLLY_ERASE value_type& emplace(Args&&... args) { bitref_ = false; // C++ Standard requires *this to be empty if // `std::optional::emplace(...)` throws value_.reset(std::make_unique(static_cast(args)...)); bitref_ = true; return value_.mut(); } template FOLLY_ERASE std::enable_if_t< std::is_constructible&, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { bitref_ = false; value_.reset( std::make_unique(ilist, static_cast(args)...)); bitref_ = true; return value_.value(); } private: boxed_value_type& value_; get_default_t get_default_; BitRef bitref_; }; template bool operator==(intern_boxed_field_ref a, intern_boxed_field_ref b) { return *a == *b; } template bool operator!=(intern_boxed_field_ref a, intern_boxed_field_ref b) { return !(a == b); } template bool operator<(intern_boxed_field_ref a, intern_boxed_field_ref b) { return *a < *b; } template bool operator>(intern_boxed_field_ref a, intern_boxed_field_ref b) { return b < a; } template bool operator<=(intern_boxed_field_ref a, intern_boxed_field_ref b) { return !(a > b); } template bool operator>=(intern_boxed_field_ref a, intern_boxed_field_ref b) { return !(a < b); } template bool operator==(intern_boxed_field_ref a, const U& b) { return *a == b; } template bool operator!=(intern_boxed_field_ref a, const U& b) { return !(a == b); } template bool operator==(const U& a, intern_boxed_field_ref b) { return b == a; } template bool operator!=(const U& a, intern_boxed_field_ref b) { return b != a; } template bool operator<(intern_boxed_field_ref a, const U& b) { return *a < b; } template bool operator>(intern_boxed_field_ref a, const U& b) { return *a > b; } template bool operator<=(intern_boxed_field_ref a, const U& b) { return !(a > b); } template bool operator>=(intern_boxed_field_ref a, const U& b) { return !(a < b); } template bool operator<(const U& a, intern_boxed_field_ref b) { return b > a; } template bool operator<=(const U& a, intern_boxed_field_ref b) { return b >= a; } template bool operator>(const U& a, intern_boxed_field_ref b) { return b < a; } template bool operator>=(const U& a, intern_boxed_field_ref b) { return b <= a; } // A reference to a 'terse' intern boxed field. // // It currently only supports Thrift structs. template class terse_intern_boxed_field_ref { static_assert(std::is_reference::value, "not a reference"); static_assert( detail::is_boxed_value_v>, "not a boxed_value"); using element_type = typename folly::remove_cvref_t::element_type; using boxed_value_type = std::remove_reference_t; template friend class terse_intern_boxed_field_ref; // TODO(dokwon): Consider removing `get_default_t` after resolving // dependency issue. using get_default_t = folly::FunctionRef; public: using value_type = detail::copy_const_t; using reference_type = detail::copy_reference_t; FOLLY_ERASE terse_intern_boxed_field_ref( T value, get_default_t get_default) noexcept : value_(value), get_default_(get_default) {} template > FOLLY_ERASE /* implicit */ terse_intern_boxed_field_ref( const terse_intern_boxed_field_ref& other) noexcept : value_(other.value_) {} template FOLLY_ERASE std::enable_if_t< std::is_assignable::value, terse_intern_boxed_field_ref&> operator=(U&& value) { value_.mut() = static_cast(value); return *this; } // Workaround for https://bugs.llvm.org/show_bug.cgi?id=49442 FOLLY_ERASE terse_intern_boxed_field_ref& operator=(value_type&& value) noexcept( std::is_nothrow_move_assignable::value) { value_.mut() = static_cast(value); return *this; value.~value_type(); // Force emit destructor... } template FOLLY_ERASE void copy_from(const terse_intern_boxed_field_ref& other) { value_ = other.value_; } template FOLLY_ERASE void move_from(terse_intern_boxed_field_ref other) noexcept( std::is_nothrow_assignable&&>:: value) { value_ = static_cast&&>(other.value_); } FOLLY_ERASE void reset() noexcept { // reset to the intern intrinsic default. value_ = boxed_value_type::fromStaticConstant(&get_default_()); } template FOLLY_ERASE detail::EnableIfNonConst value() { return static_cast(value_.mut()); } template FOLLY_ERASE detail::EnableIfConst value() const { return static_cast(value_.value()); } FOLLY_ERASE reference_type ensure() noexcept { return static_cast(value_.mut()); } FOLLY_ERASE reference_type operator*() { return value(); } FOLLY_ERASE reference_type operator*() const { return value(); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() { return &value_.mut(); } template FOLLY_ERASE detail::EnableIfConst* operator->() const { return &value_.value(); } template FOLLY_ERASE value_type& emplace(Args&&... args) { value_.reset(std::make_unique(static_cast(args)...)); return value_.mut(); } template FOLLY_ERASE std::enable_if_t< std::is_constructible&, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { value_.reset( std::make_unique(ilist, static_cast(args)...)); return value_.value(); } private: boxed_value_type& value_; get_default_t get_default_; }; template terse_intern_boxed_field_ref as_const_intern_box( terse_intern_boxed_field_ref val) { return val; } template bool operator==( terse_intern_boxed_field_ref a, terse_intern_boxed_field_ref b) { return *a == *b; } template bool operator!=( terse_intern_boxed_field_ref a, terse_intern_boxed_field_ref b) { return !(a == b); } template bool operator<( terse_intern_boxed_field_ref a, terse_intern_boxed_field_ref b) { return *a < *b; } template bool operator>( terse_intern_boxed_field_ref a, terse_intern_boxed_field_ref b) { return b < a; } template bool operator<=( terse_intern_boxed_field_ref a, terse_intern_boxed_field_ref b) { return !(a > b); } template bool operator>=( terse_intern_boxed_field_ref a, terse_intern_boxed_field_ref b) { return !(a < b); } template bool operator==(terse_intern_boxed_field_ref a, const U& b) { return *a == b; } template bool operator!=(terse_intern_boxed_field_ref a, const U& b) { return !(a == b); } template bool operator==(const U& a, terse_intern_boxed_field_ref b) { return b == a; } template bool operator!=(const U& a, terse_intern_boxed_field_ref b) { return b != a; } template bool operator<(terse_intern_boxed_field_ref a, const U& b) { return *a < b; } template bool operator>(terse_intern_boxed_field_ref a, const U& b) { return *a > b; } template bool operator<=(terse_intern_boxed_field_ref a, const U& b) { return !(a > b); } template bool operator>=(terse_intern_boxed_field_ref a, const U& b) { return !(a < b); } template bool operator<(const U& a, terse_intern_boxed_field_ref b) { return b > a; } template bool operator<=(const U& a, terse_intern_boxed_field_ref b) { return b >= a; } template bool operator>(const U& a, terse_intern_boxed_field_ref b) { return b < a; } template bool operator>=(const U& a, terse_intern_boxed_field_ref b) { return b <= a; } namespace detail { struct get_pointer_fn { template T* operator()(optional_field_ref field) const { return field ? &*field : nullptr; } template auto* operator()(optional_boxed_field_ref field) const { return field ? &*field : nullptr; } }; struct can_throw_fn { template FOLLY_ERASE T&& operator()(T&& value) const { return static_cast(value); } }; struct ensure_isset_unsafe_fn { template void operator()(optional_field_ref ref) const noexcept { ref.bitref_ = true; } }; struct unset_unsafe_fn { template void operator()(field_ref ref) const noexcept { ref.bitref_ = false; } template void operator()(optional_field_ref ref) const noexcept { ref.bitref_ = false; } }; struct alias_isset_fn { template auto operator()(optional_field_ref ref, F functor) const noexcept(noexcept(functor(ref.value_))) { auto&& result = functor(ref.value_); return optional_field_ref( static_cast(result), ref.bitref_); } }; template FOLLY_ERASE apache::thrift::optional_field_ref make_optional_field_ref( T&& ref, apache::thrift::detail::is_set_t>& is_set) { return {std::forward(ref), is_set}; } template FOLLY_ERASE apache::thrift::field_ref make_field_ref( T&& ref, apache::thrift::detail::is_set_t>& is_set) { return {std::forward(ref), is_set}; } struct move_to_unique_ptr_fn { template FOLLY_ERASE auto operator()(optional_boxed_field_ref ref) const noexcept { return ref.release(); } }; struct assign_from_unique_ptr_fn { template FOLLY_ERASE void operator()( optional_boxed_field_ref ref, std::unique_ptr::element_type> ptr) const noexcept { ref.reset(std::move(ptr)); } }; } // namespace detail // get_pointer // // Access optional fields without throwing. If the field is set, it returns the // pointer to the field. If the field is not set, it returns nullptr. // // Example: // // auto* ptr = apache::thrift::get_pointer(obj.optional_field()); constexpr apache::thrift::detail::get_pointer_fn get_pointer; // can_throw // // Used to annotate optional field accesses that can throw, // suppressing any linter warning about unchecked access. // // Example: // // auto value = apache::thrift::can_throw(*obj.field_ref()); constexpr apache::thrift::detail::can_throw_fn can_throw; // move_to_unique_ptr // // Transfer ownership of underlying boxed field to std::unique_ptr. // // Example: // // auto ptr = apache::thrift::move_to_unique_ptr(obj.field_ref()); constexpr apache::thrift::detail::move_to_unique_ptr_fn move_to_unique_ptr; // assign_from_unique_ptr // // Transfer ownership of std::unique_ptr to underlying boxed field. // // Example: // // apache::thrift::assign_from_unique_ptr(obj.field_ref(), // std::make_unique(42)); constexpr apache::thrift::detail::assign_from_unique_ptr_fn assign_from_unique_ptr; [[deprecated("Use `emplace` or `operator=` to set Thrift fields.")]] // constexpr apache::thrift::detail::ensure_isset_unsafe_fn ensure_isset_unsafe; constexpr apache::thrift::detail::ensure_isset_unsafe_fn ensure_isset_unsafe_deprecated; [[deprecated("Use `reset` to clear Thrift fields.")]] // constexpr apache::thrift::detail::unset_unsafe_fn unset_unsafe; constexpr apache::thrift::detail::unset_unsafe_fn unset_unsafe_deprecated; [[deprecated]] // constexpr apache::thrift::detail::alias_isset_fn alias_isset; // A reference to an required field of the possibly const-qualified type // std::remove_reference_t in a Thrift-generated struct. template class required_field_ref { static_assert(std::is_reference::value, "not a reference"); template friend class required_field_ref; public: using value_type = std::remove_reference_t; using reference_type = T; FOLLY_ERASE explicit required_field_ref(reference_type value) noexcept : value_(value) {} template > FOLLY_ERASE /* implicit */ required_field_ref( const required_field_ref& other) noexcept : value_(other.value_) {} template FOLLY_ERASE std::enable_if_t< std::is_assignable::value, required_field_ref&> operator=(U&& value) noexcept( std::is_nothrow_assignable::value) { value_ = static_cast(value); return *this; } // Workaround for https://bugs.llvm.org/show_bug.cgi?id=49442 FOLLY_ERASE required_field_ref& operator=(value_type&& value) noexcept( std::is_nothrow_move_assignable::value) { value_ = static_cast(value); return *this; value.~value_type(); // Force emit destructor... } // Assignment from required_field_ref is intentionally not provided to prevent // potential confusion between two possible behaviors, copying and reference // rebinding. The copy_from method is provided instead. template FOLLY_ERASE void copy_from(required_field_ref other) noexcept( std::is_nothrow_assignable::value) { value_ = other.value(); } // Returns true iff the field is set. required_field_ref doesn't provide // conversion to bool to avoid confusion between checking if the field is set // and getting the field's value, particularly for bool fields. FOLLY_ERASE bool has_value() const noexcept { return true; } // Returns a reference to the value. FOLLY_ERASE reference_type value() const noexcept { return static_cast(value_); } FOLLY_ERASE reference_type operator*() const noexcept { return static_cast(value_); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() const noexcept { return &value_; } template FOLLY_ERASE detail::EnableIfConst* operator->() const noexcept { return &value_; } FOLLY_ERASE value_type* operator->() noexcept { return &value_; } FOLLY_ERASE reference_type ensure() noexcept { return static_cast(value_); } template FOLLY_ERASE auto operator[](const Index& index) const -> decltype(auto) { return value_[index]; } template FOLLY_ERASE value_type& emplace(Args&&... args) { return value_ = value_type(static_cast(args)...); } template FOLLY_ERASE std::enable_if_t< std::is_constructible, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { return value_ = value_type(ilist, static_cast(args)...); } private: value_type& value_; }; template bool operator==(required_field_ref lhs, required_field_ref rhs) { return *lhs == *rhs; } template bool operator!=(required_field_ref lhs, required_field_ref rhs) { return *lhs != *rhs; } template bool operator<(required_field_ref lhs, required_field_ref rhs) { return *lhs < *rhs; } template bool operator>(required_field_ref lhs, required_field_ref rhs) { return *lhs > *rhs; } template bool operator<=(required_field_ref lhs, required_field_ref rhs) { return *lhs <= *rhs; } template bool operator>=(required_field_ref lhs, required_field_ref rhs) { return *lhs >= *rhs; } template bool operator==(required_field_ref lhs, const U& rhs) { return *lhs == rhs; } template bool operator!=(required_field_ref lhs, const U& rhs) { return *lhs != rhs; } template bool operator<(required_field_ref lhs, const U& rhs) { return *lhs < rhs; } template bool operator>(required_field_ref lhs, const U& rhs) { return *lhs > rhs; } template bool operator<=(required_field_ref lhs, const U& rhs) { return *lhs <= rhs; } template bool operator>=(required_field_ref lhs, const U& rhs) { return *lhs >= rhs; } template bool operator==(const T& lhs, required_field_ref rhs) { return lhs == *rhs; } template bool operator!=(const T& lhs, required_field_ref rhs) { return lhs != *rhs; } template bool operator<(const T& lhs, required_field_ref rhs) { return lhs < *rhs; } template bool operator>(const T& lhs, required_field_ref rhs) { return lhs > *rhs; } template bool operator<=(const T& lhs, required_field_ref rhs) { return lhs <= *rhs; } template bool operator>=(const T& lhs, required_field_ref rhs) { return lhs >= *rhs; } namespace detail { struct union_field_ref_owner_vtable { using reset_t = void(void*); reset_t* reset; }; struct union_field_ref_owner_vtable_impl { template static void reset(void* obj) { apache::thrift::clear(*static_cast(obj)); } }; template inline constexpr union_field_ref_owner_vtable // union_field_ref_owner_vtable_for{nullptr}; template inline constexpr union_field_ref_owner_vtable // union_field_ref_owner_vtable_for{ &union_field_ref_owner_vtable_impl::reset}; template inline constexpr union_field_ref_owner_vtable // union_field_ref_owner_vtable_for{nullptr}; } // namespace detail // A reference to an union field of the possibly const-qualified type template class union_field_ref { static_assert(std::is_reference::value, "not a reference"); template friend class union_field_ref; friend struct detail::union_value_unsafe_fn; using is_cpp_ref_or_boxed = std::bool_constant< detail::is_boxed_value_ptr_v> || detail::is_shared_or_unique_ptr_v>>; struct element_type_adapter { using element_type = folly::remove_cvref_t; }; using element_type = typename std::conditional_t< is_cpp_ref_or_boxed::value, folly::remove_cvref_t, element_type_adapter>::element_type; using storage_reference_type = T; using storage_value_type = std::remove_reference_t; public: using value_type = detail::copy_const_t; using reference_type = detail::copy_reference_t; private: using int_t = detail::copy_const_t; using owner = detail::copy_const_t*; using vtable = apache::thrift::detail::union_field_ref_owner_vtable; public: FOLLY_ERASE union_field_ref( storage_reference_type storage_value, int_t& type, int field_type, owner ow, const vtable& vt) noexcept : storage_value_(storage_value), type_(type), field_type_(field_type), owner_(ow), vtable_(vt) {} template < typename U = value_type, std::enable_if_t< std::is_assignable::value && std::is_constructible::value, int> = 0> FOLLY_ERASE union_field_ref& operator=(U&& other) noexcept( std::is_nothrow_constructible::value && std::is_nothrow_assignable::value) { if (has_value() && !detail::is_shared_or_unique_ptr_v>) { get_value() = static_cast(other); } else { emplace(static_cast(other)); } return *this; } FOLLY_ERASE bool has_value() const { return type_ == field_type_; } FOLLY_ERASE explicit operator bool() const { return has_value(); } // Returns a reference to the value if this is union's active field, // bad_field_access otherwise. FOLLY_ERASE reference_type value() const { throw_if_unset(); return static_cast(get_value()); } template > FOLLY_ERASE std::remove_const_t value_or( U&& default_value) const { using type = std::remove_const_t; return has_value() ? type(static_cast(get_value())) : type(static_cast(default_value)); } FOLLY_ERASE reference_type operator*() const { return value(); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() const { throw_if_unset(); return &get_value(); } template FOLLY_ERASE detail::EnableIfConst* operator->() const { throw_if_unset(); return &get_value(); } FOLLY_ERASE value_type* operator->() { throw_if_unset(); return &get_value(); } FOLLY_ERASE reference_type ensure() { if (!has_value()) { emplace(); } return static_cast(get_value()); } template FOLLY_ERASE value_type& emplace(Args&&... args) { vtable_.reset(owner_); emplace_impl(is_cpp_ref_or_boxed{}, static_cast(args)...); type_ = field_type_; return get_value(); } template FOLLY_ERASE std::enable_if_t< std::is_constructible, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { vtable_.reset(owner_); emplace_impl(is_cpp_ref_or_boxed{}, ilist, static_cast(args)...); type_ = field_type_; return get_value(); } private: FOLLY_ERASE void throw_if_unset() const { if (!has_value()) { apache::thrift::detail::throw_on_bad_union_field_access(); } } FOLLY_ERASE value_type& get_value() const { return get_value(is_cpp_ref_or_boxed{}); } FOLLY_ERASE value_type& get_value(std::false_type) const { return storage_value_; } FOLLY_ERASE value_type& get_value(std::true_type) const { if (storage_value_ == nullptr) { // This can only happen if user used setter/getter to clear cpp.ref // pointer. It won't happen if user didn't use setter/getter API at all. apache::thrift::detail::throw_on_nullptr_dereferencing(); } return *storage_value_; } template FOLLY_ERASE void emplace_impl(std::false_type, Args&&... args) { ::new (&storage_value_) storage_value_type(static_cast(args)...); } template FOLLY_ERASE void emplace_impl(std::true_type, Args&&... args) { ::new (&storage_value_) storage_value_type(); // TODO: use make_shared to initialize cpp.ref_type = "shared" field storage_value_.reset(new element_type(static_cast(args)...)); } storage_value_type& storage_value_; int_t& type_; const int field_type_; owner owner_; const vtable& vtable_; }; template bool operator==(union_field_ref a, union_field_ref b) { return a && b ? *a == *b : a.has_value() == b.has_value(); } template bool operator!=(union_field_ref a, union_field_ref b) { return !(a == b); } template bool operator<(union_field_ref a, union_field_ref b) { if (a.has_value() != b.has_value()) { return a.has_value() < b.has_value(); } return a ? *a < *b : false; } template bool operator>(union_field_ref a, union_field_ref b) { return b < a; } template bool operator<=(union_field_ref a, union_field_ref b) { return !(a > b); } template bool operator>=(union_field_ref a, union_field_ref b) { return !(a < b); } template bool operator==(union_field_ref a, const U& b) { return a ? *a == b : false; } template bool operator!=(union_field_ref a, const U& b) { return !(a == b); } template bool operator==(const U& a, union_field_ref b) { return b == a; } template bool operator!=(const U& a, union_field_ref b) { return b != a; } template bool operator<(union_field_ref a, const U& b) { return a ? *a < b : true; } template bool operator>(union_field_ref a, const U& b) { return a ? *a > b : false; } template bool operator<=(union_field_ref a, const U& b) { return !(a > b); } template bool operator>=(union_field_ref a, const U& b) { return !(a < b); } template bool operator<(const U& a, union_field_ref b) { return b > a; } template bool operator<=(const U& a, union_field_ref b) { return b >= a; } template bool operator>(const U& a, union_field_ref b) { return b < a; } template bool operator>=(const U& a, union_field_ref b) { return b <= a; } namespace detail { struct union_value_unsafe_fn { template auto&& operator()(union_field_ref ref) const { return static_cast::reference_type>( ref.get_value()); } }; } // namespace detail // A reference to a terse field of the possibly const-qualified type // std::remove_reference_t in a Thrift-generated struct. Note, a terse field // does not need isset since we do not need to distinguish if the field is set // or unset. template class terse_field_ref { static_assert(std::is_reference::value, "not a reference"); template friend class terse_field_ref; public: using value_type = std::remove_reference_t; using reference_type = T; FOLLY_ERASE terse_field_ref(reference_type value) noexcept : value_(value) {} template > FOLLY_ERASE /* implicit */ terse_field_ref( const terse_field_ref& other) noexcept : value_(other.value_) {} template < typename U, std::enable_if_t< std::is_same{} || std::is_same{}, int> = 0> FOLLY_ERASE explicit terse_field_ref( const terse_field_ref& other) noexcept : value_(other.value_) {} template FOLLY_ERASE std:: enable_if_t::value, terse_field_ref&> operator=(U&& value) noexcept( std::is_nothrow_assignable::value) { value_ = static_cast(value); return *this; } // Workaround for https://bugs.llvm.org/show_bug.cgi?id=49442 FOLLY_ERASE terse_field_ref& operator=(value_type&& value) noexcept( std::is_nothrow_move_assignable::value) { value_ = static_cast(value); return *this; value.~value_type(); // Force emit destructor... } template FOLLY_ERASE void copy_from(const terse_field_ref& other) noexcept( std::is_nothrow_assignable::value) { value_ = other.value_; } template FOLLY_ERASE void move_from(terse_field_ref other) noexcept( std::is_nothrow_assignable&&>:: value) { value_ = static_cast&&>(other.value_); } FOLLY_ERASE reference_type value() const noexcept { return static_cast(value_); } FOLLY_ERASE reference_type operator*() const noexcept { return static_cast(value_); } template [[deprecated( "Please use `foo.value().bar()` instead of `foo->bar()` " "since const is not propagated correctly in `operator->` API")]] FOLLY_ERASE detail::EnableIfNonConst* operator->() const noexcept { return &value_; } template FOLLY_ERASE detail::EnableIfConst* operator->() const noexcept { return &value_; } FOLLY_ERASE value_type* operator->() noexcept { return &value_; } template FOLLY_ERASE auto operator[](const Index& index) const -> decltype(auto) { return value_[index]; } template FOLLY_ERASE value_type& emplace(Args&&... args) { value_ = value_type(static_cast(args)...); return value_; } template FOLLY_ERASE std::enable_if_t< std::is_constructible, Args&&...>:: value, value_type&> emplace(std::initializer_list ilist, Args&&... args) { value_ = value_type(ilist, static_cast(args)...); return value_; } private: value_type& value_; }; template bool operator==(terse_field_ref lhs, terse_field_ref rhs) { return *lhs == *rhs; } template bool operator!=(terse_field_ref lhs, terse_field_ref rhs) { return *lhs != *rhs; } template bool operator<(terse_field_ref lhs, terse_field_ref rhs) { return *lhs < *rhs; } template bool operator>(terse_field_ref lhs, terse_field_ref rhs) { return *lhs > *rhs; } template bool operator<=(terse_field_ref lhs, terse_field_ref rhs) { return *lhs <= *rhs; } template bool operator>=(terse_field_ref lhs, terse_field_ref rhs) { return *lhs >= *rhs; } template bool operator==(terse_field_ref lhs, const U& rhs) { return *lhs == rhs; } template bool operator!=(terse_field_ref lhs, const U& rhs) { return *lhs != rhs; } template bool operator<(terse_field_ref lhs, const U& rhs) { return *lhs < rhs; } template bool operator>(terse_field_ref lhs, const U& rhs) { return *lhs > rhs; } template bool operator<=(terse_field_ref lhs, const U& rhs) { return *lhs <= rhs; } template bool operator>=(terse_field_ref lhs, const U& rhs) { return *lhs >= rhs; } template bool operator==(const T& lhs, terse_field_ref rhs) { return lhs == *rhs; } template bool operator!=(const T& lhs, terse_field_ref rhs) { return lhs != *rhs; } template bool operator<(const T& lhs, terse_field_ref rhs) { return lhs < *rhs; } template bool operator>(const T& lhs, terse_field_ref rhs) { return lhs > *rhs; } template bool operator<=(const T& lhs, terse_field_ref rhs) { return lhs <= *rhs; } template bool operator>=(const T& lhs, terse_field_ref rhs) { return lhs >= *rhs; } } // namespace thrift } // namespace apache