thrift/lib/cpp2/FieldRef.h (1,397 lines of code) (raw):
/*
* 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 <initializer_list>
#include <memory>
#include <type_traits>
#if (!defined(_MSC_VER) && __has_include(<optional>)) || \
(defined(_MSC_VER) && (__cplusplus >= 201703L || _MSVC_LANG >= 201703L))
#include <optional>
// Technically it should be 201606 but std::optional is present with 201603.
#if __cpp_lib_optional >= 201603 || _LIBCPP_STD_VER > 14
#define THRIFT_HAS_OPTIONAL
#endif
#endif
#include <assert.h>
#include <limits.h>
#include <folly/CPortability.h>
#include <folly/CppAttributes.h>
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/synchronization/AtomicUtil.h>
#include <thrift/lib/cpp2/BoxedValuePtr.h>
#include <thrift/lib/cpp2/Thrift.h>
namespace apache {
namespace thrift {
namespace detail {
template <typename T>
using is_set_t =
std::conditional_t<std::is_const<T>::value, const uint8_t, uint8_t>;
[[noreturn]] void throw_on_bad_field_access();
struct ensure_isset_unsafe_fn;
struct unset_unsafe_fn;
struct alias_isset_fn;
// IntWrapper is a wrapper of integer that's always copy/move assignable
// even if integer is atomic
template <typename T>
struct IntWrapper {
IntWrapper() = default;
explicit IntWrapper(T t) : value(t) {}
T value{0};
};
template <typename U>
struct IntWrapper<std::atomic<U>> {
IntWrapper() = default;
IntWrapper(const IntWrapper& other) noexcept : value(other.value.load()) {}
IntWrapper(IntWrapper&& other) noexcept : value(other.value.load()) {}
IntWrapper& operator=(const IntWrapper& other) noexcept {
value = other.value.load();
return *this;
}
IntWrapper& operator=(IntWrapper&& other) noexcept {
value = other.value.load();
return *this;
}
std::atomic<U> value{0};
};
template <typename T>
class BitSet {
public:
BitSet() = default;
explicit BitSet(T value) : int_(value) {}
BitSet(const BitSet&) = 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 <class U>
static bool get(U u, std::size_t bit) {
return u & (U(1) << bit);
}
template <class U>
static void set(U& u, std::size_t bit) {
u |= (U(1) << bit);
}
template <class U>
static void reset(U& u, std::size_t bit) {
u &= ~(U(1) << bit);
}
template <class U>
static bool get(const std::atomic<U>& u, std::size_t bit) {
return u.load(std::memory_order_relaxed) & (U(1) << bit);
}
template <class U>
static void set(std::atomic<U>& u, std::size_t bit) {
folly::atomic_fetch_set(u, bit, std::memory_order_relaxed);
}
template <class U>
static void reset(std::atomic<U>& 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<T> int_;
static constexpr int NUM_BITS = sizeof(T) * CHAR_BIT;
};
template <bool kIsConst>
class BitRef {
template <bool B>
friend class BitRef;
public:
using Isset = std::conditional_t<kIsConst, const uint8_t, uint8_t>;
using AtomicIsset = std::
conditional_t<kIsConst, const std::atomic<uint8_t>, std::atomic<uint8_t>>;
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 <bool B>
explicit BitRef(const BitRef<B>& 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<Isset&> non_atomic;
apache::thrift::detail::BitSet<AtomicIsset&> atomic;
} value_;
const uint8_t bit_index_;
const bool is_atomic_ = false;
};
} // namespace detail
// A reference to an unqualified field of the possibly const-qualified type
// std::remove_reference_t<T> in a Thrift-generated struct.
template <typename T>
class field_ref {
static_assert(std::is_reference<T>::value, "not a reference");
template <typename U>
friend class field_ref;
friend struct apache::thrift::detail::unset_unsafe_fn;
public:
using value_type = std::remove_reference_t<T>;
using reference_type = T;
private:
using BitRef =
apache::thrift::detail::BitRef<std::is_const<value_type>::value>;
public:
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) {}
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 <
typename U,
std::enable_if_t<
std::is_same<
std::add_const_t<std::remove_reference_t<U>>,
value_type>{} &&
!(std::is_rvalue_reference<T>{} && std::is_lvalue_reference<U>{}),
int> = 0>
FOLLY_ERASE /* implicit */ field_ref(const field_ref<U>& other) noexcept
: value_(other.value_), bitref_(other.bitref_) {}
template <typename U = value_type>
FOLLY_ERASE
std::enable_if_t<std::is_assignable<value_type&, U&&>::value, field_ref&>
operator=(U&& value) noexcept(
std::is_nothrow_assignable<value_type&, U&&>::value) {
value_ = static_cast<U&&>(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_type>::value) {
value_ = static_cast<value_type&&>(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 <typename U>
FOLLY_ERASE void copy_from(field_ref<U> other) noexcept(
std::is_nothrow_assignable<value_type&, U>::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<reference_type>(value_);
}
FOLLY_ERASE reference_type operator*() const noexcept {
return static_cast<reference_type>(value_);
}
FOLLY_ERASE value_type* operator->() const noexcept { return &value_; }
FOLLY_ERASE reference_type ensure() noexcept {
bitref_ = true;
return static_cast<reference_type>(value_);
}
template <typename Index>
FOLLY_ERASE auto operator[](const Index& index) const -> decltype(auto) {
return value_[index];
}
template <typename... Args>
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&&>(args)...);
bitref_ = true;
return value_;
}
template <class U, class... Args>
FOLLY_ERASE std::enable_if_t<
std::is_constructible<value_type, std::initializer_list<U>, Args&&...>::
value,
value_type&>
emplace(std::initializer_list<U> ilist, Args&&... args) {
bitref_ = false;
value_ = value_type(ilist, static_cast<Args&&>(args)...);
bitref_ = true;
return value_;
}
private:
value_type& value_;
BitRef bitref_;
};
template <typename T, typename U>
bool operator==(field_ref<T> lhs, field_ref<U> rhs) {
return *lhs == *rhs;
}
template <typename T, typename U>
bool operator!=(field_ref<T> lhs, field_ref<U> rhs) {
return *lhs != *rhs;
}
template <typename T, typename U>
bool operator<(field_ref<T> lhs, field_ref<U> rhs) {
return *lhs < *rhs;
}
template <typename T, typename U>
bool operator>(field_ref<T> lhs, field_ref<U> rhs) {
return *lhs > *rhs;
}
template <typename T, typename U>
bool operator<=(field_ref<T> lhs, field_ref<U> rhs) {
return *lhs <= *rhs;
}
template <typename T, typename U>
bool operator>=(field_ref<T> lhs, field_ref<U> rhs) {
return *lhs >= *rhs;
}
template <typename T, typename U>
bool operator==(field_ref<T> lhs, const U& rhs) {
return *lhs == rhs;
}
template <typename T, typename U>
bool operator!=(field_ref<T> lhs, const U& rhs) {
return *lhs != rhs;
}
template <typename T, typename U>
bool operator<(field_ref<T> lhs, const U& rhs) {
return *lhs < rhs;
}
template <typename T, typename U>
bool operator>(field_ref<T> lhs, const U& rhs) {
return *lhs > rhs;
}
template <typename T, typename U>
bool operator<=(field_ref<T> lhs, const U& rhs) {
return *lhs <= rhs;
}
template <typename T, typename U>
bool operator>=(field_ref<T> lhs, const U& rhs) {
return *lhs >= rhs;
}
template <typename T, typename U>
bool operator==(const T& lhs, field_ref<U> rhs) {
return lhs == *rhs;
}
template <typename T, typename U>
bool operator!=(const T& lhs, field_ref<U> rhs) {
return lhs != *rhs;
}
template <typename T, typename U>
bool operator<(const T& lhs, field_ref<U> rhs) {
return lhs < *rhs;
}
template <typename T, typename U>
bool operator>(const T& lhs, field_ref<U> rhs) {
return lhs > *rhs;
}
template <typename T, typename U>
bool operator<=(const T& lhs, field_ref<U> rhs) {
return lhs <= *rhs;
}
template <typename T, typename U>
bool operator>=(const T& lhs, field_ref<U> rhs) {
return lhs >= *rhs;
}
// A reference to an optional field of the possibly const-qualified type
// std::remove_reference_t<T> in a Thrift-generated struct.
template <typename T>
class optional_field_ref {
static_assert(std::is_reference<T>::value, "not a reference");
template <typename U>
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<T>;
using reference_type = T;
private:
using BitRef =
apache::thrift::detail::BitRef<std::is_const<value_type>::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 <
typename U,
std::enable_if_t<
std::is_same<
std::add_const_t<std::remove_reference_t<U>>,
value_type>{} &&
!(std::is_rvalue_reference<T>{} && std::is_lvalue_reference<U>{}),
int> = 0>
FOLLY_ERASE /* implicit */ optional_field_ref(
const optional_field_ref<U>& other) noexcept
: value_(other.value_), bitref_(other.bitref_) {}
template <
typename U,
std::enable_if_t<
std::is_same<T, U&&>{} || std::is_same<T, const U&&>{},
int> = 0>
FOLLY_ERASE explicit optional_field_ref(
const optional_field_ref<U&>& other) noexcept
: value_(other.value_), bitref_(other.bitref_) {}
template <typename U = value_type>
FOLLY_ERASE std::enable_if_t<
std::is_assignable<value_type&, U&&>::value,
optional_field_ref&>
operator=(U&& value) noexcept(
std::is_nothrow_assignable<value_type&, U&&>::value) {
value_ = static_cast<U&&>(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_type>::value) {
value_ = static_cast<value_type&&>(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 <typename U>
FOLLY_ERASE void copy_from(const optional_field_ref<U>& other) noexcept(
std::is_nothrow_assignable<value_type&, U>::value) {
value_ = other.value_unchecked();
bitref_ = other.has_value();
}
template <typename U>
FOLLY_ERASE void move_from(optional_field_ref<U> other) noexcept(
std::is_nothrow_assignable<value_type&, std::remove_reference_t<U>&&>::
value) {
value_ = static_cast<std::remove_reference_t<U>&&>(other.value_);
bitref_ = other.has_value();
}
#ifdef THRIFT_HAS_OPTIONAL
template <typename U>
FOLLY_ERASE void from_optional(const std::optional<U>& other) noexcept(
std::is_nothrow_assignable<value_type&, const U&>::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 <typename U>
FOLLY_ERASE void from_optional(std::optional<U>&& other) noexcept(
std::is_nothrow_assignable<value_type&, U&&>::value) {
// Use if instead of a shorter ternary expression to prevent a potential
// copy if T and U mismatch.
if (other) {
value_ = static_cast<U&&>(*other);
} else {
value_ = {};
}
bitref_ = other.has_value();
}
FOLLY_ERASE std::optional<std::remove_const_t<value_type>> to_optional()
const {
using type = std::optional<std::remove_const_t<value_type>>;
return bitref_ ? type(value_) : type();
}
#endif
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_type>::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<reference_type>(value_);
}
template <typename U = std::remove_const_t<value_type>>
FOLLY_ERASE std::remove_const_t<value_type> value_or(
U&& default_value) const {
using type = std::remove_const_t<value_type>;
return bitref_ ? type(static_cast<reference_type>(value_))
: type(static_cast<U&&>(default_value));
}
// Returns a reference to the value without checking whether it is available.
FOLLY_ERASE reference_type value_unchecked() const {
return static_cast<reference_type>(value_);
}
FOLLY_ERASE reference_type operator*() const { return value(); }
FOLLY_ERASE value_type* operator->() const {
throw_if_unset();
return &value_;
}
FOLLY_ERASE reference_type
ensure() noexcept(std::is_nothrow_move_assignable<value_type>::value) {
if (!bitref_) {
value_ = value_type();
bitref_ = true;
}
return static_cast<reference_type>(value_);
}
template <typename... Args>
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&&>(args)...);
bitref_ = true;
return value_;
}
template <class U, class... Args>
FOLLY_ERASE std::enable_if_t<
std::is_constructible<value_type, std::initializer_list<U>&, Args&&...>::
value,
value_type&>
emplace(std::initializer_list<U> ilist, Args&&... args) {
reset();
value_ = value_type(ilist, static_cast<Args&&>(args)...);
bitref_ = true;
return value_;
}
private:
FOLLY_ERASE void throw_if_unset() const {
if (!bitref_) {
apache::thrift::detail::throw_on_bad_field_access();
}
}
value_type& value_;
BitRef bitref_;
};
template <typename T1, typename T2>
bool operator==(optional_field_ref<T1> a, optional_field_ref<T2> b) {
return a && b ? *a == *b : a.has_value() == b.has_value();
}
template <typename T1, typename T2>
bool operator!=(optional_field_ref<T1> a, optional_field_ref<T2> b) {
return !(a == b);
}
template <typename T1, typename T2>
bool operator<(optional_field_ref<T1> a, optional_field_ref<T2> b) {
if (a.has_value() != b.has_value()) {
return a.has_value() < b.has_value();
}
return a ? *a < *b : false;
}
template <typename T1, typename T2>
bool operator>(optional_field_ref<T1> a, optional_field_ref<T2> b) {
return b < a;
}
template <typename T1, typename T2>
bool operator<=(optional_field_ref<T1> a, optional_field_ref<T2> b) {
return !(a > b);
}
template <typename T1, typename T2>
bool operator>=(optional_field_ref<T1> a, optional_field_ref<T2> b) {
return !(a < b);
}
template <typename T, typename U>
bool operator==(optional_field_ref<T> a, const U& b) {
return a ? *a == b : false;
}
template <typename T, typename U>
bool operator!=(optional_field_ref<T> a, const U& b) {
return !(a == b);
}
template <typename T, typename U>
bool operator==(const U& a, optional_field_ref<T> b) {
return b == a;
}
template <typename T, typename U>
bool operator!=(const U& a, optional_field_ref<T> b) {
return b != a;
}
template <typename T, typename U>
bool operator<(optional_field_ref<T> a, const U& b) {
return a ? *a < b : true;
}
template <typename T, typename U>
bool operator>(optional_field_ref<T> a, const U& b) {
return a ? *a > b : false;
}
template <typename T, typename U>
bool operator<=(optional_field_ref<T> a, const U& b) {
return !(a > b);
}
template <typename T, typename U>
bool operator>=(optional_field_ref<T> a, const U& b) {
return !(a < b);
}
template <typename T, typename U>
bool operator<(const U& a, optional_field_ref<T> b) {
return b > a;
}
template <typename T, typename U>
bool operator<=(const U& a, optional_field_ref<T> b) {
return b >= a;
}
template <typename T, typename U>
bool operator>(const U& a, optional_field_ref<T> b) {
return b < a;
}
template <typename T, typename U>
bool operator>=(const U& a, optional_field_ref<T> b) {
return b <= a;
}
#ifdef THRIFT_HAS_OPTIONAL
template <class T>
bool operator==(const optional_field_ref<T>& a, std::nullopt_t) {
return !a.has_value();
}
template <class T>
bool operator==(std::nullopt_t, const optional_field_ref<T>& a) {
return !a.has_value();
}
template <class T>
bool operator!=(const optional_field_ref<T>& a, std::nullopt_t) {
return a.has_value();
}
template <class T>
bool operator!=(std::nullopt_t, const optional_field_ref<T>& a) {
return a.has_value();
}
#endif
namespace detail {
template <typename T>
struct is_boxed_value_ptr : std::false_type {};
template <typename T>
struct is_boxed_value_ptr<boxed_value_ptr<T>> : std::true_type {};
template <typename From, typename To>
using copy_reference_t = std::conditional_t<
std::is_lvalue_reference<From>{},
std::add_lvalue_reference_t<To>,
std::add_rvalue_reference_t<To>>;
template <typename From, typename To>
using copy_const_t = std::conditional_t<
std::is_const<std::remove_reference_t<From>>{},
std::add_const_t<To>,
To>;
} // namespace detail
template <typename T>
class optional_boxed_field_ref {
static_assert(std::is_reference<T>::value, "not a reference");
static_assert(
detail::is_boxed_value_ptr<folly::remove_cvref_t<T>>::value,
"not a boxed_value_ptr");
using element_type = typename folly::remove_cvref_t<T>::element_type;
template <typename U>
friend class optional_boxed_field_ref;
public:
using value_type = detail::copy_const_t<T, element_type>;
using reference_type = detail::copy_reference_t<T, value_type>;
FOLLY_ERASE explicit optional_boxed_field_ref(T value) noexcept
: value_(value) {}
template <
typename U,
std::enable_if_t<
std::is_same<
std::add_const_t<std::remove_reference_t<U>>,
std::remove_reference_t<T>>{} &&
!(std::is_rvalue_reference<T>{} && std::is_lvalue_reference<U>{}),
int> = 0>
FOLLY_ERASE /* implicit */ optional_boxed_field_ref(
const optional_boxed_field_ref<U>& other) noexcept
: value_(other.value_) {}
template <
typename U,
std::enable_if_t<
std::is_same<T, U&&>{} || std::is_same<T, const U&&>{},
int> = 0>
FOLLY_ERASE explicit optional_boxed_field_ref(
const optional_boxed_field_ref<U&>& other) noexcept
: value_(other.value_) {}
template <typename U = value_type>
FOLLY_ERASE std::enable_if_t<
std::is_assignable<value_type&, U&&>::value,
optional_boxed_field_ref&>
operator=(U&& value) {
value_ = static_cast<U&&>(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 <typename U>
FOLLY_ERASE void copy_from(const optional_boxed_field_ref<U>& other) {
value_ = T(other.value_);
}
template <typename U>
FOLLY_ERASE void move_from(optional_boxed_field_ref<U> other) noexcept {
value_ = static_cast<std::remove_reference_t<U>&&>(other.value_);
}
#ifdef THRIFT_HAS_OPTIONAL
template <typename U>
FOLLY_ERASE void from_optional(const std::optional<U>& 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 <typename U>
FOLLY_ERASE void from_optional(std::optional<U>&& other) {
// Use if instead of a shorter ternary expression to prevent a potential
// copy if T and U mismatch.
if (other) {
value_ = static_cast<U&&>(*other);
} else {
value_ = {};
}
}
FOLLY_ERASE std::optional<std::remove_const_t<value_type>> to_optional()
const {
using type = std::optional<std::remove_const_t<value_type>>;
return has_value() ? type(*value_) : type();
}
#endif
FOLLY_ERASE bool has_value() const noexcept {
return static_cast<bool>(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<reference_type>(*value_);
}
template <typename U = std::remove_const_t<value_type>>
FOLLY_ERASE std::remove_const_t<value_type> value_or(
U&& default_value) const {
using type = std::remove_const_t<value_type>;
return has_value() ? type(static_cast<reference_type>(*value_))
: type(static_cast<U&&>(default_value));
}
FOLLY_ERASE reference_type operator*() const { return value(); }
FOLLY_ERASE value_type* operator->() const {
throw_if_unset();
return &*value_;
}
FOLLY_ERASE reference_type ensure() {
if (!has_value()) {
emplace();
}
return static_cast<reference_type>(*value_);
}
template <typename... Args>
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&&>(args)...);
return *value_;
}
template <class U, class... Args>
FOLLY_ERASE std::enable_if_t<
std::is_constructible<value_type, std::initializer_list<U>&, Args&&...>::
value,
value_type&>
emplace(std::initializer_list<U> ilist, Args&&... args) {
reset();
value_ = value_type(ilist, static_cast<Args&&>(args)...);
return *value_;
}
private:
FOLLY_ERASE void throw_if_unset() const {
if (!has_value()) {
apache::thrift::detail::throw_on_bad_field_access();
}
}
std::remove_reference_t<T>& value_;
};
template <typename T1, typename T2>
bool operator==(
optional_boxed_field_ref<T1> a, optional_boxed_field_ref<T2> b) {
return a && b ? *a == *b : a.has_value() == b.has_value();
}
template <typename T1, typename T2>
bool operator!=(
optional_boxed_field_ref<T1> a, optional_boxed_field_ref<T2> b) {
return !(a == b);
}
template <typename T1, typename T2>
bool operator<(optional_boxed_field_ref<T1> a, optional_boxed_field_ref<T2> b) {
if (a.has_value() != b.has_value()) {
return a.has_value() < b.has_value();
}
return a ? *a < *b : false;
}
template <typename T1, typename T2>
bool operator>(optional_boxed_field_ref<T1> a, optional_boxed_field_ref<T2> b) {
return b < a;
}
template <typename T1, typename T2>
bool operator<=(
optional_boxed_field_ref<T1> a, optional_boxed_field_ref<T2> b) {
return !(a > b);
}
template <typename T1, typename T2>
bool operator>=(
optional_boxed_field_ref<T1> a, optional_boxed_field_ref<T2> b) {
return !(a < b);
}
template <typename T, typename U>
bool operator==(optional_boxed_field_ref<T> a, const U& b) {
return a ? *a == b : false;
}
template <typename T, typename U>
bool operator!=(optional_boxed_field_ref<T> a, const U& b) {
return !(a == b);
}
template <typename T, typename U>
bool operator==(const U& a, optional_boxed_field_ref<T> b) {
return b == a;
}
template <typename T, typename U>
bool operator!=(const U& a, optional_boxed_field_ref<T> b) {
return b != a;
}
template <typename T, typename U>
bool operator<(optional_boxed_field_ref<T> a, const U& b) {
return a ? *a < b : true;
}
template <typename T, typename U>
bool operator>(optional_boxed_field_ref<T> a, const U& b) {
return a ? *a > b : false;
}
template <typename T, typename U>
bool operator<=(optional_boxed_field_ref<T> a, const U& b) {
return !(a > b);
}
template <typename T, typename U>
bool operator>=(optional_boxed_field_ref<T> a, const U& b) {
return !(a < b);
}
template <typename T, typename U>
bool operator<(const U& a, optional_boxed_field_ref<T> b) {
return b > a;
}
template <typename T, typename U>
bool operator<=(const U& a, optional_boxed_field_ref<T> b) {
return b >= a;
}
template <typename T, typename U>
bool operator>(const U& a, optional_boxed_field_ref<T> b) {
return b < a;
}
template <typename T, typename U>
bool operator>=(const U& a, optional_boxed_field_ref<T> b) {
return b <= a;
}
#ifdef THRIFT_HAS_OPTIONAL
template <class T>
bool operator==(const optional_boxed_field_ref<T>& a, std::nullopt_t) {
return !a.has_value();
}
template <class T>
bool operator==(std::nullopt_t, const optional_boxed_field_ref<T>& a) {
return !a.has_value();
}
template <class T>
bool operator!=(const optional_boxed_field_ref<T>& a, std::nullopt_t) {
return a.has_value();
}
template <class T>
bool operator!=(std::nullopt_t, const optional_boxed_field_ref<T>& a) {
return a.has_value();
}
#endif
namespace detail {
struct get_pointer_fn {
template <class T>
T* operator()(optional_field_ref<T&> field) const {
return field ? &*field : nullptr;
}
template <class T>
auto* operator()(optional_boxed_field_ref<T&> field) const {
return field ? &*field : nullptr;
}
};
struct can_throw_fn {
template <typename T>
FOLLY_ERASE T&& operator()(T&& value) const {
return static_cast<T&&>(value);
}
};
struct ensure_isset_unsafe_fn {
template <typename T>
void operator()(optional_field_ref<T> ref) const noexcept {
ref.bitref_ = true;
}
};
struct unset_unsafe_fn {
template <typename T>
void operator()(field_ref<T> ref) const noexcept {
ref.bitref_ = false;
}
template <typename T>
void operator()(optional_field_ref<T> ref) const noexcept {
ref.bitref_ = false;
}
};
struct alias_isset_fn {
template <typename T, typename F>
auto operator()(optional_field_ref<T> ref, F functor) const
noexcept(noexcept(functor(ref.value_))) {
auto&& result = functor(ref.value_);
return optional_field_ref<decltype(result)>(
static_cast<decltype(result)>(result), ref.bitref_);
}
};
template <typename T>
FOLLY_ERASE apache::thrift::optional_field_ref<T&&> make_optional_field_ref(
T&& ref,
apache::thrift::detail::is_set_t<std::remove_reference_t<T>>& is_set) {
return {std::forward<T>(ref), is_set};
}
template <typename T>
FOLLY_ERASE apache::thrift::field_ref<T&&> make_field_ref(
T&& ref,
apache::thrift::detail::is_set_t<std::remove_reference_t<T>>& is_set) {
return {std::forward<T>(ref), is_set};
}
} // namespace detail
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;
[[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<T> in a Thrift-generated struct.
template <typename T>
class required_field_ref {
static_assert(std::is_reference<T>::value, "not a reference");
template <typename U>
friend class required_field_ref;
public:
using value_type = std::remove_reference_t<T>;
using reference_type = T;
FOLLY_ERASE explicit required_field_ref(reference_type value) noexcept
: value_(value) {}
template <
typename U,
std::enable_if_t<
std::is_same<
std::add_const_t<std::remove_reference_t<U>>,
value_type>{} &&
!(std::is_rvalue_reference<T>{} && std::is_lvalue_reference<U>{}),
int> = 0>
FOLLY_ERASE /* implicit */ required_field_ref(
const required_field_ref<U>& other) noexcept
: value_(other.value_) {}
template <typename U = value_type>
FOLLY_ERASE std::enable_if_t<
std::is_assignable<value_type&, U&&>::value,
required_field_ref&>
operator=(U&& value) noexcept(
std::is_nothrow_assignable<value_type&, U&&>::value) {
value_ = static_cast<U&&>(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_type>::value) {
value_ = static_cast<value_type&&>(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 <typename U>
FOLLY_ERASE void copy_from(required_field_ref<U> other) noexcept(
std::is_nothrow_assignable<value_type&, U>::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<reference_type>(value_);
}
FOLLY_ERASE reference_type operator*() const noexcept {
return static_cast<reference_type>(value_);
}
FOLLY_ERASE value_type* operator->() const noexcept { return &value_; }
FOLLY_ERASE reference_type ensure() noexcept {
return static_cast<reference_type>(value_);
}
template <typename Index>
FOLLY_ERASE auto operator[](const Index& index) const -> decltype(auto) {
return value_[index];
}
template <typename... Args>
FOLLY_ERASE value_type& emplace(Args&&... args) {
return value_ = value_type(static_cast<Args&&>(args)...);
}
template <class U, class... Args>
FOLLY_ERASE std::enable_if_t<
std::is_constructible<value_type, std::initializer_list<U>, Args&&...>::
value,
value_type&>
emplace(std::initializer_list<U> ilist, Args&&... args) {
return value_ = value_type(ilist, static_cast<Args&&>(args)...);
}
private:
value_type& value_;
};
template <typename T, typename U>
bool operator==(required_field_ref<T> lhs, required_field_ref<U> rhs) {
return *lhs == *rhs;
}
template <typename T, typename U>
bool operator!=(required_field_ref<T> lhs, required_field_ref<U> rhs) {
return *lhs != *rhs;
}
template <typename T, typename U>
bool operator<(required_field_ref<T> lhs, required_field_ref<U> rhs) {
return *lhs < *rhs;
}
template <typename T, typename U>
bool operator>(required_field_ref<T> lhs, required_field_ref<U> rhs) {
return *lhs > *rhs;
}
template <typename T, typename U>
bool operator<=(required_field_ref<T> lhs, required_field_ref<U> rhs) {
return *lhs <= *rhs;
}
template <typename T, typename U>
bool operator>=(required_field_ref<T> lhs, required_field_ref<U> rhs) {
return *lhs >= *rhs;
}
template <typename T, typename U>
bool operator==(required_field_ref<T> lhs, const U& rhs) {
return *lhs == rhs;
}
template <typename T, typename U>
bool operator!=(required_field_ref<T> lhs, const U& rhs) {
return *lhs != rhs;
}
template <typename T, typename U>
bool operator<(required_field_ref<T> lhs, const U& rhs) {
return *lhs < rhs;
}
template <typename T, typename U>
bool operator>(required_field_ref<T> lhs, const U& rhs) {
return *lhs > rhs;
}
template <typename T, typename U>
bool operator<=(required_field_ref<T> lhs, const U& rhs) {
return *lhs <= rhs;
}
template <typename T, typename U>
bool operator>=(required_field_ref<T> lhs, const U& rhs) {
return *lhs >= rhs;
}
template <typename T, typename U>
bool operator==(const T& lhs, required_field_ref<U> rhs) {
return lhs == *rhs;
}
template <typename T, typename U>
bool operator!=(const T& lhs, required_field_ref<U> rhs) {
return lhs != *rhs;
}
template <typename T, typename U>
bool operator<(const T& lhs, required_field_ref<U> rhs) {
return lhs < *rhs;
}
template <typename T, typename U>
bool operator>(const T& lhs, required_field_ref<U> rhs) {
return lhs > *rhs;
}
template <typename T, typename U>
bool operator<=(const T& lhs, required_field_ref<U> rhs) {
return lhs <= *rhs;
}
template <typename T, typename U>
bool operator>=(const T& lhs, required_field_ref<U> 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 <typename T>
static void reset(void* obj) {
apache::thrift::clear(*static_cast<T*>(obj));
}
};
template <typename T>
FOLLY_INLINE_VARIABLE constexpr union_field_ref_owner_vtable //
union_field_ref_owner_vtable_for{nullptr};
template <typename T>
FOLLY_INLINE_VARIABLE constexpr union_field_ref_owner_vtable //
union_field_ref_owner_vtable_for<T&>{
&union_field_ref_owner_vtable_impl::reset<T>};
template <typename T>
FOLLY_INLINE_VARIABLE constexpr union_field_ref_owner_vtable //
union_field_ref_owner_vtable_for<T const&>{nullptr};
template <class T, class = void>
struct element_type {
using type = T;
};
template <class T>
struct element_type<T, folly::void_t<typename T::element_type>> {
using type = typename T::element_type;
};
template <class T>
using is_boxed = folly::detail::is_instantiation_of<boxed_value_ptr, T>;
} // namespace detail
// A reference to an union field of the possibly const-qualified type
template <typename T>
class union_field_ref {
static_assert(std::is_reference<T>::value, "not a reference");
template <typename>
friend class union_field_ref;
using element_type =
typename detail::element_type<folly::remove_cvref_t<T>>::type;
using is_boxed = detail::is_boxed<folly::remove_cvref_t<T>>;
using storage_reference_type = T;
using storage_value_type = std::remove_reference_t<T>;
public:
using value_type = detail::copy_const_t<T, element_type>;
using reference_type = detail::copy_reference_t<T, value_type>;
private:
using int_t =
std::conditional_t<std::is_const<value_type>::value, const int, int>;
using owner =
std::conditional_t<std::is_const<value_type>::value, void const*, void*>;
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,
vtable const& 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<reference_type, U&&>::value &&
std::is_constructible<value_type, U&&>::value,
int> = 0>
FOLLY_ERASE union_field_ref& operator=(U&& other) noexcept(
std::is_nothrow_constructible<value_type, U>::value&&
std::is_nothrow_assignable<value_type, U>::value) {
if (has_value()) {
get_value() = static_cast<U&&>(other);
} else {
emplace(static_cast<U&&>(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<reference_type>(get_value());
}
FOLLY_ERASE reference_type operator*() const { return value(); }
FOLLY_ERASE value_type* operator->() const {
throw_if_unset();
return &get_value();
}
FOLLY_ERASE reference_type ensure() {
if (!has_value()) {
emplace();
}
return static_cast<reference_type>(get_value());
}
template <typename... Args>
FOLLY_ERASE value_type& emplace(Args&&... args) {
vtable_.reset(owner_);
::new (&storage_value_) storage_value_type(static_cast<Args&&>(args)...);
type_ = field_type_;
return get_value();
}
template <class U, class... Args>
FOLLY_ERASE std::enable_if_t<
std::is_constructible<value_type, std::initializer_list<U>, Args&&...>::
value,
value_type&>
emplace(std::initializer_list<U> ilist, Args&&... args) {
vtable_.reset(owner_);
::new (&storage_value_)
storage_value_type(ilist, static_cast<Args&&>(args)...);
type_ = field_type_;
return get_value();
}
private:
FOLLY_ERASE void throw_if_unset() const {
if (!has_value()) {
apache::thrift::detail::throw_on_bad_field_access();
}
}
FOLLY_ERASE value_type& get_value() const { return get_value(is_boxed{}); }
FOLLY_ERASE value_type& get_value(std::true_type) const {
return *storage_value_;
}
FOLLY_ERASE value_type& get_value(std::false_type) const {
return storage_value_;
}
storage_value_type& storage_value_;
int_t& type_;
const int field_type_;
owner owner_;
vtable const& vtable_;
};
template <typename T1, typename T2>
bool operator==(union_field_ref<T1> a, union_field_ref<T2> b) {
return a && b ? *a == *b : a.has_value() == b.has_value();
}
template <typename T1, typename T2>
bool operator!=(union_field_ref<T1> a, union_field_ref<T2> b) {
return !(a == b);
}
template <typename T1, typename T2>
bool operator<(union_field_ref<T1> a, union_field_ref<T2> b) {
if (a.has_value() != b.has_value()) {
return a.has_value() < b.has_value();
}
return a ? *a < *b : false;
}
template <typename T1, typename T2>
bool operator>(union_field_ref<T1> a, union_field_ref<T2> b) {
return b < a;
}
template <typename T1, typename T2>
bool operator<=(union_field_ref<T1> a, union_field_ref<T2> b) {
return !(a > b);
}
template <typename T1, typename T2>
bool operator>=(union_field_ref<T1> a, union_field_ref<T2> b) {
return !(a < b);
}
template <typename T, typename U>
bool operator==(union_field_ref<T> a, const U& b) {
return a ? *a == b : false;
}
template <typename T, typename U>
bool operator!=(union_field_ref<T> a, const U& b) {
return !(a == b);
}
template <typename T, typename U>
bool operator==(const U& a, union_field_ref<T> b) {
return b == a;
}
template <typename T, typename U>
bool operator!=(const U& a, union_field_ref<T> b) {
return b != a;
}
template <typename T, typename U>
bool operator<(union_field_ref<T> a, const U& b) {
return a ? *a < b : true;
}
template <typename T, typename U>
bool operator>(union_field_ref<T> a, const U& b) {
return a ? *a > b : false;
}
template <typename T, typename U>
bool operator<=(union_field_ref<T> a, const U& b) {
return !(a > b);
}
template <typename T, typename U>
bool operator>=(union_field_ref<T> a, const U& b) {
return !(a < b);
}
template <typename T, typename U>
bool operator<(const U& a, union_field_ref<T> b) {
return b > a;
}
template <typename T, typename U>
bool operator<=(const U& a, union_field_ref<T> b) {
return b >= a;
}
template <typename T, typename U>
bool operator>(const U& a, union_field_ref<T> b) {
return b < a;
}
template <typename T, typename U>
bool operator>=(const U& a, union_field_ref<T> b) {
return b <= a;
}
// A reference to a terse field of the possibly const-qualified type
// std::remove_reference_t<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 <typename T>
class terse_field_ref {
static_assert(std::is_reference<T>::value, "not a reference");
template <typename U>
friend class terse_field_ref;
public:
using value_type = std::remove_reference_t<T>;
using reference_type = T;
FOLLY_ERASE terse_field_ref(reference_type value) noexcept : value_(value) {}
template <
typename U,
std::enable_if_t<
std::is_same<
std::add_const_t<std::remove_reference_t<U>>,
value_type>{} &&
!(std::is_rvalue_reference<T>{} && std::is_lvalue_reference<U>{}),
int> = 0>
FOLLY_ERASE /* implicit */ terse_field_ref(
const terse_field_ref<U>& other) noexcept
: value_(other.value_) {}
template <
typename U,
std::enable_if_t<
std::is_same<T, U&&>{} || std::is_same<T, const U&&>{},
int> = 0>
FOLLY_ERASE explicit terse_field_ref(
const terse_field_ref<U&>& other) noexcept
: value_(other.value_) {}
template <typename U = value_type>
FOLLY_ERASE std::
enable_if_t<std::is_assignable<value_type&, U&&>::value, terse_field_ref&>
operator=(U&& value) noexcept(
std::is_nothrow_assignable<value_type&, U&&>::value) {
value_ = static_cast<U&&>(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_type>::value) {
value_ = static_cast<value_type&&>(value);
return *this;
value.~value_type(); // Force emit destructor...
}
template <typename U>
FOLLY_ERASE void copy_from(const terse_field_ref<U>& other) noexcept(
std::is_nothrow_assignable<value_type&, U>::value) {
value_ = other.value_;
}
template <typename U>
FOLLY_ERASE void move_from(terse_field_ref<U> other) noexcept(
std::is_nothrow_assignable<value_type&, std::remove_reference_t<U>&&>::
value) {
value_ = static_cast<std::remove_reference_t<U>&&>(other.value_);
}
FOLLY_ERASE reference_type value() const noexcept {
return static_cast<reference_type>(value_);
}
FOLLY_ERASE reference_type operator*() const noexcept {
return static_cast<reference_type>(value_);
}
FOLLY_ERASE value_type* operator->() const noexcept { return &value_; }
template <typename Index>
FOLLY_ERASE auto operator[](const Index& index) const -> decltype(auto) {
return value_[index];
}
template <typename... Args>
FOLLY_ERASE value_type& emplace(Args&&... args) {
value_ = value_type(static_cast<Args&&>(args)...);
return value_;
}
template <class U, class... Args>
FOLLY_ERASE std::enable_if_t<
std::is_constructible<value_type, std::initializer_list<U>, Args&&...>::
value,
value_type&>
emplace(std::initializer_list<U> ilist, Args&&... args) {
value_ = value_type(ilist, static_cast<Args&&>(args)...);
return value_;
}
private:
value_type& value_;
};
template <typename T, typename U>
bool operator==(terse_field_ref<T> lhs, terse_field_ref<U> rhs) {
return *lhs == *rhs;
}
template <typename T, typename U>
bool operator!=(terse_field_ref<T> lhs, terse_field_ref<U> rhs) {
return *lhs != *rhs;
}
template <typename T, typename U>
bool operator<(terse_field_ref<T> lhs, terse_field_ref<U> rhs) {
return *lhs < *rhs;
}
template <typename T, typename U>
bool operator>(terse_field_ref<T> lhs, terse_field_ref<U> rhs) {
return *lhs > *rhs;
}
template <typename T, typename U>
bool operator<=(terse_field_ref<T> lhs, terse_field_ref<U> rhs) {
return *lhs <= *rhs;
}
template <typename T, typename U>
bool operator>=(terse_field_ref<T> lhs, terse_field_ref<U> rhs) {
return *lhs >= *rhs;
}
template <typename T, typename U>
bool operator==(terse_field_ref<T> lhs, const U& rhs) {
return *lhs == rhs;
}
template <typename T, typename U>
bool operator!=(terse_field_ref<T> lhs, const U& rhs) {
return *lhs != rhs;
}
template <typename T, typename U>
bool operator<(terse_field_ref<T> lhs, const U& rhs) {
return *lhs < rhs;
}
template <typename T, typename U>
bool operator>(terse_field_ref<T> lhs, const U& rhs) {
return *lhs > rhs;
}
template <typename T, typename U>
bool operator<=(terse_field_ref<T> lhs, const U& rhs) {
return *lhs <= rhs;
}
template <typename T, typename U>
bool operator>=(terse_field_ref<T> lhs, const U& rhs) {
return *lhs >= rhs;
}
template <typename T, typename U>
bool operator==(const T& lhs, terse_field_ref<U> rhs) {
return lhs == *rhs;
}
template <typename T, typename U>
bool operator!=(const T& lhs, terse_field_ref<U> rhs) {
return lhs != *rhs;
}
template <typename T, typename U>
bool operator<(const T& lhs, terse_field_ref<U> rhs) {
return lhs < *rhs;
}
template <typename T, typename U>
bool operator>(const T& lhs, terse_field_ref<U> rhs) {
return lhs > *rhs;
}
template <typename T, typename U>
bool operator<=(const T& lhs, terse_field_ref<U> rhs) {
return lhs <= *rhs;
}
template <typename T, typename U>
bool operator>=(const T& lhs, terse_field_ref<U> rhs) {
return lhs >= *rhs;
}
} // namespace thrift
} // namespace apache