/* * 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 namespace folly { ////////////////////////////////////////////////////////////////////// /** * Helper to generate an index sequence from a tuple like type */ template using index_sequence_for_tuple = std::make_index_sequence::value>; namespace detail { namespace apply_tuple { namespace adl { using std::get; template struct invoke_get_fn { template constexpr auto operator()(T&& t) const noexcept(noexcept(get(static_cast(t)))) -> decltype(get(static_cast(t))) { return get(static_cast(t)); } }; } // namespace adl template < typename Tuple, std::size_t... Indices, typename ReturnTuple = std::tuple< decltype(adl::invoke_get_fn{}(std::declval()))...>> auto forward_tuple(Tuple&& tuple, std::index_sequence) -> ReturnTuple { return ReturnTuple{ adl::invoke_get_fn{}(static_cast(tuple))...}; } } // namespace apply_tuple } // namespace detail struct ApplyInvoke { private: template using seq = index_sequence_for_tuple>; template using get = detail::apply_tuple::adl::invoke_get_fn; template static constexpr auto invoke_(F&& f, T&& t, std::index_sequence) noexcept( noexcept(invoke(static_cast(f), get{}(static_cast(t))...))) -> decltype(invoke( static_cast(f), get{}(static_cast(t))...)) { return invoke(static_cast(f), get{}(static_cast(t))...); } public: template constexpr auto operator()(F&& f, T&& t) const noexcept( noexcept(invoke_(static_cast(f), static_cast(t), seq{}))) -> decltype(invoke_(static_cast(f), static_cast(t), seq{})) { return invoke_(static_cast(f), static_cast(t), seq{}); } }; ////////////////////////////////////////////////////////////////////// // libc++ v3.9 has std::apply // android ndk r15c libc++ claims to be v3.9 but is missing std::apply #if __cpp_lib_apply >= 201603 || \ (((__ANDROID__ && _LIBCPP_VERSION > 3900) || \ (!__ANDROID__ && _LIBCPP_VERSION > 3800)) && \ _LIBCPP_STD_VER > 14) || \ (_MSC_VER && _HAS_CXX17) /* using override */ using std::apply; #else // __cpp_lib_apply >= 201603 // mimic: std::apply, C++17 template constexpr decltype(auto) apply(F&& func, Tuple&& tuple) { return ApplyInvoke{}(static_cast(func), static_cast(tuple)); } #endif // __cpp_lib_apply >= 201603 /** * Get a tuple of references from the passed tuple, forwarding will be applied * on the individual types of the tuple based on the value category of the * passed tuple * * For example * * forward_tuple(std::make_tuple(1, 2)) * * Returns a std::tuple, * * auto tuple = std::make_tuple(1, 2); * forward_tuple(tuple) * * Returns a std::tuple */ template auto forward_tuple(Tuple&& tuple) noexcept -> decltype(detail::apply_tuple::forward_tuple( std::declval(), std::declval< index_sequence_for_tuple>>())) { return detail::apply_tuple::forward_tuple( static_cast(tuple), index_sequence_for_tuple>{}); } /** * Mimic the invoke suite of traits for tuple based apply invocation */ template using apply_result = invoke_result; template using apply_result_t = invoke_result_t; template FOLLY_INLINE_VARIABLE constexpr bool is_applicable_v = is_invocable_v; template using is_applicable = is_invocable; template FOLLY_INLINE_VARIABLE constexpr bool is_applicable_r_v = is_invocable_r_v; template using is_applicable_r = is_invocable_r; template FOLLY_INLINE_VARIABLE constexpr bool is_nothrow_applicable_v = is_nothrow_invocable_v; template using is_nothrow_applicable = is_nothrow_invocable; template FOLLY_INLINE_VARIABLE constexpr bool is_nothrow_applicable_r_v = is_nothrow_invocable_r_v; template using is_nothrow_applicable_r = is_nothrow_invocable_r; namespace detail { namespace apply_tuple { template class Uncurry { public: explicit Uncurry(F&& func) : func_(std::move(func)) {} explicit Uncurry(const F& func) : func_(func) {} template auto operator()(Tuple&& tuple) const -> decltype(apply(std::declval(), std::forward(tuple))) { return apply(func_, std::forward(tuple)); } private: F func_; }; } // namespace apply_tuple } // namespace detail /** * Wraps a function taking N arguments into a function which accepts a tuple of * N arguments. Note: This function will also accept an std::pair if N == 2. * * For example, given the below code: * * std::vector> rows = ...; * auto test = [](std::tuple& row) { * return std::get<0>(row) * std::get<1>(row) * std::get<2>(row) == 24; * }; * auto found = std::find_if(rows.begin(), rows.end(), test); * * * 'test' could be rewritten as: * * auto test = * folly::uncurry([](int a, int b, int c) { return a * b * c == 24; }); * */ template auto uncurry(F&& f) -> detail::apply_tuple::Uncurry::type> { return detail::apply_tuple::Uncurry::type>( std::forward(f)); } #if __cpp_lib_make_from_tuple || (_MSC_VER >= 1910 && _MSVC_LANG > 201402) /* using override */ using std::make_from_tuple; #else namespace detail { namespace apply_tuple { template struct Construct { template constexpr T operator()(Args&&... args) const { return T(std::forward(args)...); } }; } // namespace apply_tuple } // namespace detail // mimic: std::make_from_tuple, C++17 template constexpr T make_from_tuple(Tuple&& t) { return apply(detail::apply_tuple::Construct(), std::forward(t)); } #endif ////////////////////////////////////////////////////////////////////// } // namespace folly