/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include using apache::thrift::bad_field_access; using apache::thrift::field_ref; using apache::thrift::intern_boxed_field_ref; using apache::thrift::optional_boxed_field_ref; using apache::thrift::optional_field_ref; using apache::thrift::terse_field_ref; using apache::thrift::terse_intern_boxed_field_ref; using apache::thrift::detail::boxed_ptr; using apache::thrift::detail::boxed_value; using apache::thrift::detail::boxed_value_ptr; // A struct which is assignable but not constructible from int or other types // to test forwarding in field_ref::operator=. struct IntAssignable { IntAssignable& operator=(int v) noexcept { value = v; return *this; } int value = 0; }; // A struct assignable from std::string with a non-noexcept assignment operator // to test conditional noexcept in field_ref::operator=. struct StringAssignable { StringAssignable& operator=(const std::string& v) { value = v; return *this; } std::string value; }; struct Nested { int value = 0; }; class TestStruct { public: field_ref name() { return {name_, __isset.at(folly::index_constant<0>())}; } field_ref name() const { return {name_, __isset.at(folly::index_constant<0>())}; } optional_field_ref opt_name() & { return {name_, __isset.at(folly::index_constant<0>())}; } optional_field_ref opt_name() && { return {std::move(name_), __isset.at(folly::index_constant<0>())}; } optional_field_ref opt_name() const& { return {name_, __isset.at(folly::index_constant<0>())}; } optional_field_ref opt_name() const&& { return {std::move(name_), __isset.at(folly::index_constant<0>())}; } terse_field_ref terse_name() & { return {name_}; } terse_field_ref terse_name() && { return {std::move(name_)}; } terse_field_ref terse_name() const& { return {name_}; } terse_field_ref terse_name() const&& { return {static_cast(name_)}; } field_ref int_assign() { return {int_assign_, __isset.at(folly::index_constant<1>())}; } optional_field_ref opt_int_assign() { return {int_assign_, __isset.at(folly::index_constant<1>())}; } terse_field_ref terse_int_assign() { return {int_assign_}; } optional_field_ref&> ptr_ref() { return {ptr_, __isset.at(folly::index_constant<2>())}; } field_ref int_val() { return {int_val_, __isset.at(folly::index_constant<3>())}; } optional_field_ref opt_int_val() { return {int_val_, __isset.at(folly::index_constant<3>())}; } field_ref&> uptr() & { return {uptr_, __isset.at(folly::index_constant<4>())}; } field_ref&&> uptr() && { return {std::move(uptr_), __isset.at(folly::index_constant<4>())}; } optional_field_ref&> opt_uptr() & { return {uptr_, __isset.at(folly::index_constant<4>())}; } optional_field_ref&&> opt_uptr() && { return {std::move(uptr_), __isset.at(folly::index_constant<4>())}; } field_ref&> vec() & { return {vec_, __isset.at(folly::index_constant<5>())}; } terse_field_ref&> terse_vec() & { return {vec_}; } optional_field_ref opt_nested() & { return {nested_, __isset.at(folly::index_constant<5>())}; } terse_field_ref terse_int_val() & { return {int_val_}; } terse_field_ref terse_int_val() && { return {std::move(int_val_)}; } terse_field_ref terse_int_val() const& { return {int_val_}; } terse_field_ref terse_int_val() const&& { return {static_cast(int_val_)}; } private: std::string name_ = "default"; IntAssignable int_assign_; std::shared_ptr ptr_; int int_val_; std::unique_ptr uptr_; std::vector vec_; Nested nested_; apache::thrift::detail::isset_bitset<7> __isset{}; }; class TestStructBoxedValuePtr { public: auto opt_name() & { return optional_boxed_field_ref&>{name_}; } auto opt_name() && { return optional_boxed_field_ref&&>{ std::move(name_)}; } auto opt_name() const& { return optional_boxed_field_ref&>{name_}; } auto opt_name() const&& { return optional_boxed_field_ref&&>{ std::move(name_)}; } auto opt_int_assign() { return optional_boxed_field_ref&>{ int_assign_}; } auto ptr_ref() { return optional_boxed_field_ref>&>{ ptr_}; } auto opt_int_val() { return optional_boxed_field_ref&>{int_val_}; } auto opt_uptr() & { return optional_boxed_field_ref>&>{ uptr_}; } auto opt_uptr() && { return optional_boxed_field_ref>&&>{ std::move(uptr_)}; } auto opt_nested() & { return optional_boxed_field_ref&>{nested_}; } private: boxed_value_ptr name_; boxed_value_ptr name_default_ = "default"; boxed_value_ptr int_assign_; boxed_value_ptr int_assign_default_ = IntAssignable{}; boxed_value_ptr> ptr_; boxed_value_ptr int_val_; boxed_value_ptr int_val_default_ = 0; boxed_value_ptr> uptr_; boxed_value_ptr nested_; }; class ThriftStruct { public: using __fbthrift_cpp2_type = ThriftStruct; static constexpr bool __fbthrift_cpp2_is_union = false; terse_field_ref name() & { return {name_}; } terse_field_ref name() const& { return {name_}; } terse_field_ref name() && { return {std::move(name_)}; } terse_field_ref name() const&& { return {std::move(name_)}; } void __fbthrift_clear() { name_.clear(); } private: std::string name_ = "default"; friend bool operator==(const ThriftStruct& lhs, const ThriftStruct& rhs) { return lhs.name_ == rhs.name_; } }; namespace { enum class FieldQualifier { Fill, Optional, Terse }; } // namespace template class TestStructInternBoxedValue { public: static constexpr FieldQualifier qual = Qual; auto struct_field1() & { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&>{ __fbthrift_struct_field1, apache::thrift::op::getDefault, __isset.at(folly::index_constant<0>())}; } else { return terse_intern_boxed_field_ref&>{ __fbthrift_struct_field1, apache::thrift::op::getDefault}; } } auto struct_field1() && { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field1), apache::thrift::op::getDefault, __isset.at(folly::index_constant<0>())}; } else { return terse_intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field1), apache::thrift::op::getDefault}; } } auto struct_field1() const& { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&>{ std::as_const(__fbthrift_struct_field1), apache::thrift::op::getDefault, __isset.at(folly::index_constant<0>())}; } else { return terse_intern_boxed_field_ref&>{ std::as_const(__fbthrift_struct_field1), apache::thrift::op::getDefault}; } } auto struct_field1() const&& { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field1), apache::thrift::op::getDefault, __isset.at(folly::index_constant<0>())}; } else { return terse_intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field1), apache::thrift::op::getDefault}; } } auto struct_field2() & { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&>{ __fbthrift_struct_field2, apache::thrift::op::getDefault, __isset.at(folly::index_constant<1>())}; } else { return terse_intern_boxed_field_ref&>{ __fbthrift_struct_field2, apache::thrift::op::getDefault}; } } auto struct_field2() && { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field2), apache::thrift::op::getDefault, __isset.at(folly::index_constant<1>())}; } else { return terse_intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field2), apache::thrift::op::getDefault}; } } auto struct_field2() const& { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&>{ std::as_const(__fbthrift_struct_field2), apache::thrift::op::getDefault, __isset.at(folly::index_constant<1>())}; } else { return terse_intern_boxed_field_ref&>{ std::as_const(__fbthrift_struct_field2), apache::thrift::op::getDefault}; } } auto struct_field2() const&& { if constexpr (Qual == FieldQualifier::Fill) { return intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field2), apache::thrift::op::getDefault, __isset.at(folly::index_constant<1>())}; } else { return terse_intern_boxed_field_ref&&>{ std::move(__fbthrift_struct_field2), apache::thrift::op::getDefault}; } } private: boxed_value __fbthrift_struct_field1{ boxed_value::fromStaticConstant( &apache::thrift::op::getDefault())}; boxed_value __fbthrift_struct_field2{ boxed_value::fromStaticConstant( &apache::thrift::op::getDefault())}; apache::thrift::detail::isset_bitset<2> __isset{}; }; // TODO(dokwon): Clean up FieldRefTest using TYPED_TEST. TEST(field_ref_test, access_default_value) { auto s = TestStruct(); EXPECT_EQ(*s.name(), "default"); } TEST(field_ref_test, has_value) { auto s = TestStruct(); EXPECT_FALSE(s.name().is_set()); s.name() = "foo"; EXPECT_TRUE(s.name().is_set()); } TEST(field_ref_test, assign) { auto s = TestStruct(); EXPECT_FALSE(s.name().is_set()); EXPECT_EQ(*s.name(), "default"); s.name() = "foo"; EXPECT_TRUE(s.name().is_set()); EXPECT_EQ(*s.name(), "foo"); } TEST(field_ref_test, copy_from) { auto s = TestStruct(); auto s2 = TestStruct(); s.name() = "foo"; s.name().copy_from(s2.name()); EXPECT_FALSE(s.name().is_set()); s2.name() = "foo"; s.name().copy_from(s2.name()); EXPECT_TRUE(s.name().is_set()); EXPECT_EQ(*s.name(), "foo"); } TEST(field_ref_test, copy_from_const) { auto s = TestStruct(); auto s2 = TestStruct(); const auto& s_const = s2; s2.name() = "foo"; s.name().copy_from(s_const.name()); EXPECT_TRUE(s.name().is_set()); EXPECT_EQ(*s.name(), "foo"); } TEST(field_ref_test, copy_from_other_type) { auto s = TestStruct(); auto s2 = TestStruct(); s2.int_val() = 42; s.int_assign().copy_from(s2.int_val()); EXPECT_TRUE(s.int_assign().is_set()); EXPECT_EQ(s.int_assign()->value, 42); } template