/* * 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 <functional> #include <type_traits> #include <boost/preprocessor/control/expr_iif.hpp> #include <boost/preprocessor/facilities/is_empty_variadic.hpp> #include <boost/preprocessor/list/for_each.hpp> #include <boost/preprocessor/logical/not.hpp> #include <boost/preprocessor/tuple/to_list.hpp> #include <folly/CppAttributes.h> #include <folly/Portability.h> #include <folly/Preprocessor.h> #include <folly/Traits.h> #include <folly/Utility.h> #include <folly/lang/CustomizationPoint.h> #define FOLLY_DETAIL_FORWARD_REF(a) … #define FOLLY_DETAIL_FORWARD_BODY(e) … /** * include or backport: * * std::invoke * * std::invoke_result * * std::invoke_result_t * * std::is_invocable * * std::is_invocable_v * * std::is_invocable_r * * std::is_invocable_r_v * * std::is_nothrow_invocable * * std::is_nothrow_invocable_v * * std::is_nothrow_invocable_r * * std::is_nothrow_invocable_r_v */ namespace folly { // invoke_fn // invoke // // mimic: std::invoke, C++17 struct invoke_fn { … }; inline constexpr invoke_fn invoke; } // namespace folly namespace folly { namespace invoke_detail { // ok_one_ // // A quoted-metafunction which, when applied to type T, enforces that T is a // complete type, (possibly cv-qualified) void, or an array of unknown bound. // Substitutes as void if that holds; otherwise fails a static-assert. struct ok_one_ { … }; // ok_ // // Enforce that each A... is a complete type, (possibly cv-qualified) void, or // an array of unknown bound. Substitutes as T if that holds; otherwise fails a // static-assert. // // The reason to fail a static-assert and not, say, to fail to substitute is // to force application to incomplete types to fail the compile rather than to // allow the compile to succumb to ODR violation. The failing static-assert is // a diagnosis of undefined behavior. // // See: // https://en.cppreference.com/w/cpp/types/is_invocable // https://github.com/gcc-mirror/gcc/blob/releases/gcc-13.2.0/libstdc%2B%2B-v3/include/std/type_traits#L272-L287 ok_; template <typename F> struct traits { … }; template <typename P> struct traits_member_ptr { … }; traits<M C::*>; traits<M C::*const>; traits<M C::*&>; traits<M C::*const &>; traits<M C::*&&>; traits<M C::*const &&>; #if defined(_MSC_VER) && defined(__NVCC__) template <typename P, typename... A> using is_nothrow = traits<P>::template nothrow_t<A...>; #else is_nothrow; #endif template <bool IsVoid> struct conv_r_; template <> struct conv_r_<true> { … }; template <> struct conv_r_<false> { … }; conv_r_v_; // adapted from: http://en.cppreference.com/w/cpp/types/result_of, CC-BY-SA invoke_result_t; template <typename Void, typename F, typename... A> struct invoke_result { … }; invoke_result<void_t<invoke_result_t<F, A...>>, F, A...>; is_invocable_v; is_invocable_v; is_invocable_r_v; // clang-format off is_invocable_r_v; // clang-format on is_nothrow_invocable_v; is_nothrow_invocable_v; is_nothrow_invocable_r_v; // clang-format off is_nothrow_invocable_r_v; // clang-format on } // namespace invoke_detail // mimic: std::invoke_result, C++17 invoke_result; // mimic: std::invoke_result_t, C++17 invoke_result_t; // mimic: std::is_invocable_v, C++17 is_invocable_v; // mimic: std::is_invocable, C++17 template <typename F, typename... A> struct is_invocable : std::bool_constant<is_invocable_v<F, A...>> { … }; // mimic: std::is_invocable_r_v, C++17 is_invocable_r_v; // mimic: std::is_invocable_r, C++17 template <typename R, typename F, typename... A> struct is_invocable_r : std::bool_constant<is_invocable_r_v<R, F, A...>> { … }; // mimic: std::is_nothrow_invocable_v, C++17 is_nothrow_invocable_v; // mimic: std::is_nothrow_invocable, C++17 template <typename F, typename... A> struct is_nothrow_invocable : std::bool_constant<is_nothrow_invocable_v<F, A...>> { … }; // mimic: std::is_nothrow_invocable_r_v, C++17 is_nothrow_invocable_r_v; // mimic: std::is_nothrow_invocable_r, C++17 template <typename R, typename F, typename... A> struct is_nothrow_invocable_r : std::bool_constant<is_nothrow_invocable_r_v<R, F, A...>> { … }; } // namespace folly namespace folly { namespace detail { struct invoke_private_overload; template <bool, typename I> struct invoke_traits_base_ { … }; invoke_traits_base_<false, I>; invoke_traits_base_<true, I>; invoke_traits_base; } // namespace detail // invoke_traits // // A traits container struct with the following member types, type aliases, and // variables: // // * invoke_result // * invoke_result_t // * is_invocable // * is_invocable_v // * is_invocable_r // * is_invocable_r_v // * is_nothrow_invocable // * is_nothrow_invocable_v // * is_nothrow_invocable_r // * is_nothrow_invocable_r_v // // These members have behavior matching the behavior of C++17's corresponding // invocation traits types, type aliases, and variables, but using invoke_type // as the invocable argument passed to the usual nivocation traits. // // The traits container struct also has a member type alias: // // * invoke_type // // And an invocable variable as a default-constructed instance of invoke_type, // if the latter is constexpr default-constructible: // // * invoke template <typename I> struct invoke_traits : detail::invoke_traits_base<I> { … }; // invoke_first_match // // A composite invoker which delegates to the first invoker parameter matching // the call. // // Example: // // FOLLY_CREATE_QUAL_INVOKER(invoke_x_fn, x); // FOLLY_CREATE_QUAL_INVOKER(invoke_y_fn, y); // // using invoke_x_or_y_fn = invoke_first_match<invoke_x_fn, invoke_y_fn>; // inline constexpr invoke_x_or_y_fn invoke_x_or_y; // // void go(int a, int b) { invoke_x_or_y(a, b); } // // In this example, go(...) will delegate to x(...) if it exists and is a match // for the arguments, or otherwise will delegate to y(...). template <typename... Invoker> struct invoke_first_match : private Invoker... { … }; } // namespace folly #define FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING_1(_, funcname, ns) … #define FOLLY_DETAIL_CREATE_FREE_INVOKE_TRAITS_USING(_, funcname, ...) … /*** * FOLLY_CREATE_FREE_INVOKER * * Used to create an invoker type bound to a specific free-invocable name. * * Example: * * FOLLY_CREATE_FREE_INVOKER(foo_invoker, foo); * * The type `foo_invoker` is generated in the current namespace and may be used * as follows: * * namespace Deep { * struct CanFoo {}; * int foo(CanFoo const&, Bar&) { return 1; } * int foo(CanFoo&&, Car&&) noexcept { return 2; } * } * * using traits = folly::invoke_traits<foo_invoker>; * * traits::invoke(Deep::CanFoo{}, Car{}) // 2 * * traits::invoke_result<Deep::CanFoo, Bar&> // has member * traits::invoke_result_t<Deep::CanFoo, Bar&> // int * traits::invoke_result<Deep::CanFoo, Bar&&> // empty * traits::invoke_result_t<Deep::CanFoo, Bar&&> // error * * traits::is_invocable_v<CanFoo, Bar&> // true * traits::is_invocable_v<CanFoo, Bar&&> // false * * traits::is_invocable_r_v<int, CanFoo, Bar&> // true * traits::is_invocable_r_v<char*, CanFoo, Bar&> // false * * traits::is_nothrow_invocable_v<CanFoo, Bar&> // false * traits::is_nothrow_invocable_v<CanFoo, Car&&> // true * * traits::is_nothrow_invocable_v<int, CanFoo, Bar&> // false * traits::is_nothrow_invocable_v<char*, CanFoo, Bar&> // false * traits::is_nothrow_invocable_v<int, CanFoo, Car&&> // true * traits::is_nothrow_invocable_v<char*, CanFoo, Car&&> // false * * When a name has one or more primary definition in a fixed set of namespaces * and alternate definitions in the namespaces of its arguments, the primary * definitions may automatically be found as follows: * * FOLLY_CREATE_FREE_INVOKER(swap_invoker, swap, std); * * In this case, `swap_invoke_traits::invoke(int&, int&)` will use the primary * definition found in `namespace std` relative to the current namespace, which * may be equivalent to `namespace ::std`. In contrast: * * namespace Deep { * struct HasData {}; * void swap(HasData&, HasData&) { throw 7; } * } * * using traits = invoke_traits<swap_invoker>; * * HasData a, b; * traits::invoke(a, b); // throw 7 */ #define FOLLY_CREATE_FREE_INVOKER(classname, funcname, ...) … /*** * FOLLY_CREATE_FREE_INVOKER_SUITE * * Used to create an invoker type and associated variable bound to a specific * free-invocable name. The invoker variable is named like the free-invocable * name and the invoker type is named with a suffix of _fn. * * See FOLLY_CREATE_FREE_INVOKER. */ #define FOLLY_CREATE_FREE_INVOKER_SUITE(funcname, ...) … /*** * FOLLY_CREATE_QUAL_INVOKER * * Used to create an invoker type bound to a specific free-invocable qualified * name. It is permitted that the qualification be empty and that the name be * unqualified in practice. This differs from FOLLY_CREATE_FREE_INVOKER in that * it is required that the name be in scope and that it is not possible to * provide a list of namespaces in which to look up the name.. */ #define FOLLY_CREATE_QUAL_INVOKER(classname, funcpath) … /*** * FOLLY_CREATE_QUAL_INVOKER_SUITE * * Used to create an invoker type and associated variable bound to a specific * free-invocable qualified name. * * See FOLLY_CREATE_QUAL_INVOKER. */ #define FOLLY_CREATE_QUAL_INVOKER_SUITE(name, funcpath) … /*** * FOLLY_INVOKE_QUAL * * An invoker expression resulting in an invocable which, when invoked, invokes * the free-invocable qualified name with the given arguments. */ #define FOLLY_INVOKE_QUAL(funcpath) … /*** * FOLLY_CREATE_MEMBER_INVOKER * * Used to create an invoker type bound to a specific member-invocable name. * * Example: * * FOLLY_CREATE_MEMBER_INVOKER(foo_invoker, foo); * * The type `foo_invoker` is generated in the current namespace and may be used * as follows: * * struct CanFoo { * int foo(Bar&) { return 1; } * int foo(Car&&) noexcept { return 2; } * }; * * using traits = folly::invoke_traits<foo_invoker>; * * traits::invoke(CanFoo{}, Car{}) // 2 * * traits::invoke_result<CanFoo, Bar&> // has member * traits::invoke_result_t<CanFoo, Bar&> // int * traits::invoke_result<CanFoo, Bar&&> // empty * traits::invoke_result_t<CanFoo, Bar&&> // error * * traits::is_invocable_v<CanFoo, Bar&> // true * traits::is_invocable_v<CanFoo, Bar&&> // false * * traits::is_invocable_r_v<int, CanFoo, Bar&> // true * traits::is_invocable_r_v<char*, CanFoo, Bar&> // false * * traits::is_nothrow_invocable_v<CanFoo, Bar&> // false * traits::is_nothrow_invocable_v<CanFoo, Car&&> // true * * traits::is_nothrow_invocable_v<int, CanFoo, Bar&> // false * traits::is_nothrow_invocable_v<char*, CanFoo, Bar&> // false * traits::is_nothrow_invocable_v<int, CanFoo, Car&&> // true * traits::is_nothrow_invocable_v<char*, CanFoo, Car&&> // false */ #define FOLLY_CREATE_MEMBER_INVOKER(classname, membername) … /*** * FOLLY_CREATE_MEMBER_INVOKER_SUITE * * Used to create an invoker type and associated variable bound to a specific * member-invocable name. The invoker variable is named like the member- * invocable name and the invoker type is named with a suffix of _fn. * * See FOLLY_CREATE_MEMBER_INVOKER. */ #define FOLLY_CREATE_MEMBER_INVOKER_SUITE(membername) … /*** * FOLLY_INVOKE_MEMBER * * An invoker expression resulting in an invocable which, when invoked, invokes * the member on the object with the given arguments. * * Example: * * FOLLY_INVOKE_MEMBER(find)(map, key) * * Equivalent to: * * map.find(key) * * But also equivalent to: * * std::invoke(FOLLY_INVOKE_MEMBER(find), map, key) * * As an implementation detail, the resulting callable is a lambda. This has * two observable consequences. * * Since C++17 only, lambda invocations may be marked constexpr. * * Since C++20 only, lambda definitions may appear in an unevaluated context, * namely, in an operand to decltype, noexcept, sizeof, or typeid. */ #define FOLLY_INVOKE_MEMBER(membername) … /*** * FOLLY_CREATE_STATIC_MEMBER_INVOKER * * Used to create an invoker type template bound to a specific static-member- * invocable name. * * Example: * * FOLLY_CREATE_STATIC_MEMBER_INVOKER(foo_invoker, foo); * * The type template `foo_invoker` is generated in the current namespace and * may be used as follows: * * struct CanFoo { * static int foo(Bar&) { return 1; } * static int foo(Car&&) noexcept { return 2; } * }; * * using traits = folly::invoke_traits<foo_invoker<CanFoo>>; * * traits::invoke(Car{}) // 2 * * traits::invoke_result<Bar&> // has member * traits::invoke_result_t<Bar&> // int * traits::invoke_result<Bar&&> // empty * traits::invoke_result_t<Bar&&> // error * * traits::is_invocable_v<Bar&> // true * traits::is_invocable_v<Bar&&> // false * * traits::is_invocable_r_v<int, Bar&> // true * traits::is_invocable_r_v<char*, Bar&> // false * * traits::is_nothrow_invocable_v<Bar&> // false * traits::is_nothrow_invocable_v<Car&&> // true * * traits::is_nothrow_invocable_v<int, Bar&> // false * traits::is_nothrow_invocable_v<char*, Bar&> // false * traits::is_nothrow_invocable_v<int, Car&&> // true * traits::is_nothrow_invocable_v<char*, Car&&> // false */ #define FOLLY_CREATE_STATIC_MEMBER_INVOKER(classname, membername) … /*** * FOLLY_CREATE_STATIC_MEMBER_INVOKER_SUITE * * Used to create an invoker type template and associated variable template * bound to a specific static-member-invocable name. The invoker variable * template is named like the static-member-invocable name and the invoker type * template is named with a suffix of _fn. * * See FOLLY_CREATE_STATIC_MEMBER_INVOKER. */ #define FOLLY_CREATE_STATIC_MEMBER_INVOKER_SUITE(membername) … namespace folly { namespace detail_tag_invoke_fn { void tag_invoke(); struct tag_invoke_fn { … }; // Manually implement the traits here rather than defining them in terms of // the corresponding std::invoke_result/is_invocable/is_nothrow_invocable // traits to improve compile-times. We don't need all of the generality of // the std:: traits and the tag_invoke traits can be used heavily in CPO-based // code so optimising them for compile times can make a big difference. // Use the immediately-invoked function-pointer trick here to avoid // instantiating the std::declval<T>() template. tag_invoke_result_t; template <typename Tag, typename... Args> auto try_tag_invoke(int) noexcept( noexcept(tag_invoke(FOLLY_DECLVAL(Tag&&), FOLLY_DECLVAL(Args&&)...))) -> decltype(static_cast …); template <typename Tag, typename... Args> std::false_type try_tag_invoke(...) noexcept(false); template <template <typename...> class T, typename... Args> struct defer { … }; struct empty { … }; } // namespace detail_tag_invoke_fn // The expression folly::tag_invoke(tag, args...) is equivalent to performing // a call to the expression tag_invoke(tag, args...) using argument-dependent // lookup (ADL). // // This is intended to be used by customization-point objects, which dispatch // a call to the CPO to an ADL call to tag_invoke(cpo, args...), using the type // of the first argument to disambiguate between customisations for different // CPOs rather than using different ADL names for this. // // For example: Defining a new CPO in terms of tag_invoke. // struct FooCpo { // template<typename A, typename B> // auto operator()(A&& a, B&& b) const // noexcept(folly::is_nothrow_tag_invocable_v<FooCpo, A, B>) // -> folly::tag_invoke_result_t<FooCpo, A, B> { // return folly::tag_invoke(*this, (A&&)a, (B&&)b); // } // }; // FOLLY_DEFINE_CPO(FooCpo, Foo) // // And then customising the Foo CPO for a particular type: // class SomeType { // ... // template<typename B> // friend int tag_invoke(folly::cpo_t<Foo>, const SomeType& a, B&& b) { // // implementation goes here // } // }; // // For more details see the C++ standards proposal: https://wg21.link/P1895R0. FOLLY_DEFINE_CPO(detail_tag_invoke_fn::tag_invoke_fn, tag_invoke) // Query if the 'folly::tag_invoke()' CPO can be invoked with a tag and // arguments of the the specified types. // // This checks whether an overload of the free-function tag_invoke() found // by ADL can be invoked with the specified types. is_tag_invocable_v; template <typename Tag, typename... Args> struct is_tag_invocable // : std::bool_constant<is_tag_invocable_v<Tag, Args...>> { … }; // Query whether the 'folly::tag_invoke()' CPO can be invoked with a tag // and arguments of the specified type and that such an invocation is // noexcept. is_nothrow_tag_invocable_v; template <typename Tag, typename... Args> struct is_nothrow_tag_invocable : std::bool_constant<is_nothrow_tag_invocable_v<Tag, Args...>> { … }; // Versions of the above that check in addition that the result is // convertible to the given return type R. is_tag_invocable_r; is_tag_invocable_r_v; is_nothrow_tag_invocable_r; is_nothrow_tag_invocable_r_v; tag_invoke_result_t; template <typename Tag, typename... Args> struct tag_invoke_result : conditional_t< is_tag_invocable_v<Tag, Args...>, detail_tag_invoke_fn::defer<tag_invoke_result_t, Tag, Args...>, detail_tag_invoke_fn::empty> { … }; } // namespace folly