Commit 8bb0e678 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Free function invoke traits

Summary: [Folly] Free function invoke traits.

Reviewed By: andriigrynenko

Differential Revision: D8632204

fbshipit-source-id: 1f5f6e31072ef3a711edab02b6b7bbad8c894a95
parent ae87e1d0
......@@ -96,3 +96,52 @@
* another macro expansion.
*/
#define FB_STRINGIZE(x) #x
#define FOLLY_PP_DETAIL_NARGS_1(dummy, _7, _6, _5, _4, _3, _2, _1, _0, ...) _0
#define FOLLY_PP_DETAIL_NARGS(...) \
FOLLY_PP_DETAIL_NARGS_1(dummy, ##__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_0(fn, ...)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_1(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_0(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_2(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_1(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_3(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_2(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_4(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_3(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_5(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_4(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_6(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_5(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_REC_7(fn, a, ...) \
fn(a) FOLLY_PP_DETAIL_FOR_EACH_REC_6(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_2(fn, n, ...) \
FOLLY_PP_DETAIL_FOR_EACH_REC_##n(fn, __VA_ARGS__)
#define FOLLY_PP_DETAIL_FOR_EACH_1(fn, n, ...) \
FOLLY_PP_DETAIL_FOR_EACH_2(fn, n, __VA_ARGS__)
/**
* FOLLY_PP_FOR_EACH
*
* Used to invoke a preprocessor macro, the name of which is passed as the
* first argument, once for each subsequent variadic argument.
*
* At present, supports [0, 8) arguments.
*
* This input:
*
* #define DOIT(a) go_do_it(a);
* FOLLY_PP_FOR_EACH(DOIT, 3, 5, 7)
* #undef DOIT
*
* Expands to this output (with whitespace adjusted for clarity):
*
* go_do_it(3);
* go_do_it(5);
* go_do_it(7);
*/
#define FOLLY_PP_FOR_EACH(fn, ...) \
FOLLY_PP_DETAIL_FOR_EACH_1( \
fn, FOLLY_PP_DETAIL_NARGS(__VA_ARGS__), __VA_ARGS__)
......@@ -19,6 +19,7 @@
#include <functional>
#include <type_traits>
#include <folly/Preprocessor.h>
#include <folly/Traits.h>
/**
......@@ -166,6 +167,133 @@ struct is_nothrow_invocable_r
namespace folly {
namespace detail {
template <typename Invoke>
struct free_invoke_proxy {
public:
template <typename... Args>
struct invoke_result : folly::invoke_result<Invoke, Args...> {};
template <typename... Args>
using invoke_result_t = folly::invoke_result_t<Invoke, Args...>;
template <typename... Args>
struct is_invocable : folly::is_invocable<Invoke, Args...> {};
template <typename R, typename... Args>
struct is_invocable_r : folly::is_invocable_r<R, Invoke, Args...> {};
template <typename... Args>
struct is_nothrow_invocable : folly::is_nothrow_invocable<Invoke, Args...> {};
template <typename R, typename... Args>
struct is_nothrow_invocable_r
: folly::is_nothrow_invocable_r<R, Invoke, Args...> {};
template <typename... Args>
static constexpr auto invoke(Args&&... args) noexcept(
noexcept(Invoke{}(static_cast<Args&&>(args)...)))
-> decltype(Invoke{}(static_cast<Args&&>(args)...)) {
return Invoke{}(static_cast<Args&&>(args)...);
}
};
} // namespace detail
} // namespace folly
#define FOLLY_INVOKE_DETAIL_USING_NAMESPACE(ns) using namespace ns;
/***
* FOLLY_CREATE_FREE_INVOKE_TRAITS
*
* Used to create traits container, bound to a specific free-invocable name,
* with the following member traits types and aliases:
*
* * invoke_result
* * invoke_result_t
* * is_invocable
* * is_invocable_r
* * is_nothrow_invocable
* * is_nothrow_invocable_r
*
* The container also has a static member function:
*
* * invoke
*
* These members have behavior matching the behavior of C++17's corresponding
* invocation traits types, aliases, and functions, but substituting canonical
* invocation with member invocation.
*
* Example:
*
* FOLLY_CREATE_FREE_INVOKE_TRAITS(foo_invoke_traits, foo);
*
* The traits container type `foo_invoke_traits` is generated in the current
* namespace and has the listed member types and aliases. They 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 = foo_invoke_traits;
*
* 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<CanFoo, Bar&>::value // true
* traits::is_invocable<CanFoo, Bar&&>::value // false
*
* traits::is_invocable_r<int, CanFoo, Bar&>::value // true
* traits::is_invocable_r<char*, CanFoo, Bar&>::value // false
*
* traits::is_nothrow_invocable<CanFoo, Bar&>::value // false
* traits::is_nothrow_invocable<CanFoo, Car&&>::value // true
*
* traits::is_nothrow_invocable<int, CanFoo, Bar&>::value // false
* traits::is_nothrow_invocable<char*, CanFoo, Bar&>::value // false
* traits::is_nothrow_invocable<int, CanFoo, Car&&>::value // true
* traits::is_nothrow_invocable<char*, CanFoo, Car&&>::value // false
*
* When a name has a primary definition in a fixed namespace and alternate
* definitions in the namespaces of its arguments, the primary definition may
* automatically be found as follows:
*
* FOLLY_CREATE_FREE_INVOKE_TRAITS(swap_invoke_traits, 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 = swap_invoke_traits;
*
* HasData a, b;
* traits::invoke(a, b); // throw 7
*/
#define FOLLY_CREATE_FREE_INVOKE_TRAITS(classname, funcname, ...) \
namespace classname##__folly_detail_invoke_ns { \
FOLLY_PP_FOR_EACH(FOLLY_INVOKE_DETAIL_USING_NAMESPACE, __VA_ARGS__) \
struct classname##__folly_detail_invoke { \
template <typename... Args> \
constexpr auto operator()(Args&&... args) const \
noexcept(noexcept(funcname(static_cast<Args&&>(args)...))) \
-> decltype(funcname(static_cast<Args&&>(args)...)) { \
return funcname(static_cast<Args&&>(args)...); \
} \
}; \
} \
struct classname : ::folly::detail::free_invoke_proxy< \
classname##__folly_detail_invoke_ns:: \
classname##__folly_detail_invoke> {}
namespace folly {
namespace detail {
template <typename Invoke>
struct member_invoke_proxy {
public:
......
......@@ -21,6 +21,12 @@
class InvokeTest : public testing::Test {};
namespace {
struct from_any {
template <typename T>
/* implicit */ from_any(T&&) {}
};
struct Fn {
char operator()(int, int) noexcept {
return 'a';
......@@ -49,6 +55,29 @@ struct Obj {
int volatile x_ = 17;
};
namespace x {
struct Obj {};
int go(Obj const&, int) noexcept {
return 3;
}
} // namespace x
namespace y {
struct Obj {};
char go(Obj const&, char const*) {
return 'a';
}
} // namespace y
namespace z {
struct Obj {};
} // namespace z
float go(z::Obj const&, int) {
return 9;
}
FOLLY_CREATE_FREE_INVOKE_TRAITS(go_invoke_traits, go, z);
} // namespace
TEST_F(InvokeTest, invoke) {
......@@ -98,6 +127,69 @@ TEST_F(InvokeTest, is_nothrow_invocable_r) {
EXPECT_FALSE((folly::is_nothrow_invocable_r<int, Fn, int>::value));
}
TEST_F(InvokeTest, free_invoke) {
using traits = go_invoke_traits;
x::Obj x_;
y::Obj y_;
z::Obj z_;
EXPECT_TRUE(noexcept(traits::invoke(x_, 3)));
EXPECT_FALSE(noexcept(traits::invoke(y_, "hello")));
EXPECT_FALSE(noexcept(traits::invoke(z_, 7)));
EXPECT_EQ(3, traits::invoke(x_, 3));
EXPECT_EQ('a', traits::invoke(y_, "hello"));
EXPECT_EQ(9, traits::invoke(z_, 7));
}
TEST_F(InvokeTest, free_invoke_result) {
using traits = go_invoke_traits;
EXPECT_TRUE((std::is_same<int, traits::invoke_result_t<x::Obj, int>>::value));
EXPECT_TRUE((
std::is_same<char, traits::invoke_result_t<y::Obj, char const*>>::value));
EXPECT_TRUE(
(std::is_same<float, traits::invoke_result_t<z::Obj, int>>::value));
}
TEST_F(InvokeTest, free_is_invocable) {
using traits = go_invoke_traits;
EXPECT_TRUE((traits::is_invocable<x::Obj, int>::value));
EXPECT_TRUE((traits::is_invocable<y::Obj, char const*>::value));
EXPECT_TRUE((traits::is_invocable<z::Obj, int>::value));
EXPECT_FALSE((traits::is_invocable<float>::value));
}
TEST_F(InvokeTest, free_is_invocable_r) {
using traits = go_invoke_traits;
EXPECT_TRUE((traits::is_invocable_r<int, x::Obj, int>::value));
EXPECT_TRUE((traits::is_invocable_r<char, y::Obj, char const*>::value));
EXPECT_TRUE((traits::is_invocable_r<float, z::Obj, int>::value));
EXPECT_FALSE((traits::is_invocable_r<from_any, float>::value));
}
TEST_F(InvokeTest, free_is_nothrow_invocable) {
using traits = go_invoke_traits;
EXPECT_TRUE((traits::is_nothrow_invocable<x::Obj, int>::value));
EXPECT_FALSE((traits::is_nothrow_invocable<y::Obj, char const*>::value));
EXPECT_FALSE((traits::is_nothrow_invocable<z::Obj, int>::value));
EXPECT_FALSE((traits::is_nothrow_invocable<float>::value));
}
TEST_F(InvokeTest, free_is_nothrow_invocable_r) {
using traits = go_invoke_traits;
EXPECT_TRUE((traits::is_nothrow_invocable_r<int, x::Obj, int>::value));
EXPECT_FALSE(
(traits::is_nothrow_invocable_r<char, y::Obj, char const*>::value));
EXPECT_FALSE((traits::is_nothrow_invocable_r<float, z::Obj, int>::value));
EXPECT_FALSE((traits::is_nothrow_invocable_r<from_any, float>::value));
}
TEST_F(InvokeTest, member_invoke) {
using traits = test_invoke_traits;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment