/* * 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 namespace apache { namespace thrift { namespace op { /// Resolves to the number of definitions contained in Thrift class template inline constexpr std::size_t size_v = detail::pa::__fbthrift_field_size_v; template using get_ordinal = typename detail::GetOrdinalImpl>::type; /// Gets the ordinal, for example: /// /// * using Ord = get_ordinal_v /// // Resolves to ordinal at which the field "foo" was defined in MyS. /// template inline constexpr type::Ordinal get_ordinal_v = get_ordinal::value; /// Calls the given function with ordinal<1> to ordinal. template constexpr void for_each_ordinal(F&& f) { detail::for_each_ordinal_impl( std::forward(f), std::make_integer_sequence>{}); } /// Calls the given function with with ordinal<1> to ordinal, returing the /// first 'true' result produced. template != 0>* = nullptr> decltype(auto) find_by_ordinal(F&& f) { return detail::find_by_ordinal_impl( std::forward(f), std::make_integer_sequence>{}); } template std::enable_if_t == 0, bool> find_by_ordinal(F&&) { return false; } template using get_field_id = folly::conditional_t< get_ordinal::value == type::Ordinal{}, type::field_id<0>, detail::pa::field_id>>; /// Gets the field id, for example: /// /// * using FieldId = get_field_id /// // Resolves to field id assigned to the field "foo" in MyS. template inline constexpr FieldId get_field_id_v = get_field_id::value; /// Calls the given function with each field_id<{id}> in Thrift class. template void for_each_field_id(F&& f) { for_each_ordinal([&](auto ord) { f(get_field_id{}); }); } /// Calls the given function with with each field_id<{id}>, returning the /// first 'true' result produced. template decltype(auto) find_by_field_id(F&& f) { return find_by_ordinal( [&](auto ord) { return f(get_field_id{}); }); } /// Gets the ident, for example: /// /// // Resolves to thrift::ident::* type associated with field 7 in MyS. /// using Ident = get_ident> /// template using get_ident = detail::pa::ident>; /// It calls the given function with each folly::tag{} in /// Thrift class. template void for_each_ident(F&& f) { for_each_ordinal( [&](auto ord) { f(folly::tag_t>{}); }); } /// Gets the Thrift type tag, for example: /// /// // Resolves to Thrift type tag for the field "foo" in MyS. /// using Tag = get_type_tag /// template using get_type_tag = detail::pa::type_tag>; template using get_field_tag = typename std::conditional_t< get_ordinal::value == type::Ordinal{}, void, type::field< get_type_tag, FieldContext)>>>; template using get_native_type = type::native_type>; /// Gets the thrift field name, for example: /// /// * op::get_name_v> /// // Returns the thrift field name associated with field 7 in MyStruct. /// template inline const folly::StringPiece get_name_v = detail::pa::__fbthrift_get_field_name>(); /// Gets the thrift class name, for example: /// /// * op::get_class_name_v == "MyStruct" /// template inline const folly::StringPiece get_class_name_v = detail::pa::__fbthrift_get_class_name(); /// Gets the Thrift field, for example: /// /// op::get>(myStruct) = 4; /// template inline constexpr detail::Get get = {}; /// Returns pointer to the value from the given field. /// Returns nullptr if it doesn't have a value. /// For example: /// * get_value_or_null(foo.field_ref()) /// // returns foo.field_ref().value() /// * get_value_or_null(foo.smart_ptr_ref()) /// // returns *foo.smart_ptr_ref() /// * get_value_or_null(foo.optional_ref()) /// // returns nullptr if optional field doesn't have a value. inline constexpr detail::GetValueOrNull getValueOrNull; /// Gets the field ref type of Thrift field, for example: /// /// std::is_same_v< /// get_field_ref, /// optional_field_ref>; /// template using get_field_ref = folly::remove_cvref_t(std::declval()))>; // Implementation details. namespace detail { template struct GetOrdinalImpl { // TODO(ytj): To reduce build time, only check whether Id is reflection // metadata if we couldn't find Id. static_assert(type::is_id_v, ""); using type = detail::pa::ordinal, Id>; }; template struct GetOrdinalImpl, Tag> { static_assert( folly::to_underlying(Ord) <= size_v>, "Ordinal cannot be larger than the number of definitions"); // Id is an ordinal, return itself using type = type::ordinal_tag; }; template struct GetOrdinalImpl>, Tag> : GetOrdinalImpl, Tag> {}; template constexpr void for_each_ordinal_impl(F&& f, std::index_sequence) { // This doesn't use fold expression (from C++17) as this file is used in // C++14 environment as well. int unused[] = {0, (f(type::detail::pos_to_ordinal{}), 0)...}; static_cast(unused); } template ord_result_t find_by_ordinal_impl(F&& f, std::index_sequence) { auto result = ord_result_t(); // TODO(afuller): Use a short circuting c++17 folding expression. for_each_ordinal_impl( [&](auto id) { auto found = f(id); if (static_cast(found)) { result = std::move(found); } }, std::index_sequence{}); return result; } template struct Get { template constexpr decltype(auto) operator()(U&& obj) const { return access_field>(std::forward(obj)); } }; template struct Get> { using T = type::native_type; constexpr decltype(auto) operator()(T& obj) const { return op::get(obj); } constexpr decltype(auto) operator()(T&& obj) const { return op::get(std::move(obj)); } constexpr decltype(auto) operator()(const T& obj) const { return op::get(obj); } constexpr decltype(auto) operator()(const T&& obj) const { return op::get(std::move(obj)); } }; template struct Get { template constexpr decltype(auto) operator()(U&& obj) const { return op::get>(std::forward(obj)); } }; template <> struct Get { template constexpr decltype(auto) operator()(Id, U&& obj) const { return op::get>(std::forward(obj)); } }; // Helper to get adapter type from Thrift type tag. template struct get_adapter { static_assert(sizeof(Tag) == 0, "Not adapter."); }; template struct get_adapter> : get_adapter {}; template struct get_adapter> { using type = Adapter; }; template using get_adapter_t = typename get_adapter::type; template class InvokeByFieldId { static constexpr auto N = size_v; // We use std::min to avoid index > N. template using OrdToFieldId = get_field_id>; public: template < typename F, std::enable_if_t = false> FOLLY_ALWAYS_INLINE constexpr decltype(auto) operator()( FieldId id, F&& f) const { // By default clang's maximum depth of recursive template instantiation is // 512. If we handle 8 cases at a time, it works with struct that has 4096 // fields. if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } else if (id == OrdToFieldId::value) { return std::forward(f)(OrdToFieldId{}); } return InvokeByFieldId{}(id, std::forward(f)); } template < typename F, std::enable_if_t = false> FOLLY_ALWAYS_INLINE constexpr decltype(auto) operator()( FieldId, F&& f) const { // If not found, f() will be invoked. return std::forward(f)(); } template FOLLY_ALWAYS_INLINE constexpr decltype(auto) operator()( FieldId id, F&&... f) const { return operator()(id, folly::overload(std::forward(f)...)); } }; } // namespace detail /// Given a Thrift struct, callback with a runtime dynamic field id, convert it /// to static compile-time field id and invoke the callback. For example, /// /// `invoke_by_field_id(FieldId{10}, f)` invokes `f(field_id<10>{})`. /// /// If field id is not found in T, `f()` will be invoked. /// /// In addition, `invoke_by_field_id(id, f...)` is a syntactic sugar of /// `invoke_by_field_id(id, folly::overload(f...))`. /// WARNING: inline expansion will always be applied to the call sites. template inline constexpr detail::InvokeByFieldId invoke_by_field_id{}; } // namespace op } // namespace thrift } // namespace apache