folly/folly/functional/Invoke.h

/*
 * 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