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

Simpler free-invoke traits

Summary: [Folly] Simpler free-invoke traits. Only supports one namespace housing a base definition. For the typical case of `swap`, with a base definition in `namespace std`, this works.

Reviewed By: aary

Differential Revision: D8666221

fbshipit-source-id: abfd302e430ad7151c119e98a706201265d302d3
parent a65e66ab
......@@ -67,6 +67,8 @@
*/
#define FB_SINGLE_ARG(...) __VA_ARGS__
#define FOLLY_PP_DETAIL_APPEND_VA_ARG(...) , ##__VA_ARGS__
/**
* Helper macro that just ignores its parameters.
*/
......
......@@ -167,6 +167,8 @@ struct is_nothrow_invocable_r
namespace folly {
namespace detail {
struct invoke_private_overload;
template <typename Invoke>
struct free_invoke_proxy {
public:
......@@ -195,8 +197,6 @@ struct free_invoke_proxy {
} // namespace detail
} // namespace folly
#define FOLLY_INVOKE_DETAIL_USING_NAMESPACE(ns) using namespace ns;
/***
* FOLLY_CREATE_FREE_INVOKE_TRAITS
*
......@@ -275,20 +275,28 @@ struct free_invoke_proxy {
* 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:: \
#define FOLLY_CREATE_FREE_INVOKE_TRAITS(classname, funcname, ...) \
namespace classname##__folly_detail_invoke_ns { \
namespace classname##__folly_detail_invoke_ns_inline { \
FOLLY_PUSH_WARNING \
FOLLY_CLANG_DISABLE_WARNING("-Wunused-function") \
void funcname(::folly::detail::invoke_private_overload&); \
FOLLY_POP_WARNING \
} \
using FB_ARG_2_OR_1( \
classname##__folly_detail_invoke_ns_inline \
FOLLY_PP_DETAIL_APPEND_VA_ARG(__VA_ARGS__))::funcname; \
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 {
......
......@@ -76,7 +76,16 @@ float go(z::Obj const&, int) {
return 9;
}
FOLLY_CREATE_FREE_INVOKE_TRAITS(go_invoke_traits, go, z);
namespace swappable {
struct Obj {
int x_;
};
void swap(Obj&, Obj&) noexcept {} // no-op
} // namespace swappable
FOLLY_CREATE_FREE_INVOKE_TRAITS(go_invoke_traits, go);
FOLLY_CREATE_FREE_INVOKE_TRAITS(swap_invoke_traits, swap, std);
FOLLY_CREATE_FREE_INVOKE_TRAITS(unused_invoke_traits, definitely_unused_name_);
} // namespace
......@@ -132,15 +141,12 @@ TEST_F(InvokeTest, free_invoke) {
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) {
......@@ -149,8 +155,6 @@ TEST_F(InvokeTest, free_invoke_result) {
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) {
......@@ -158,7 +162,7 @@ TEST_F(InvokeTest, free_is_invocable) {
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<z::Obj, int>::value));
EXPECT_FALSE((traits::is_invocable<float>::value));
}
......@@ -167,7 +171,7 @@ TEST_F(InvokeTest, free_is_invocable_r) {
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<float, z::Obj, int>::value));
EXPECT_FALSE((traits::is_invocable_r<from_any, float>::value));
}
......@@ -190,6 +194,28 @@ TEST_F(InvokeTest, free_is_nothrow_invocable_r) {
EXPECT_FALSE((traits::is_nothrow_invocable_r<from_any, float>::value));
}
TEST_F(InvokeTest, free_invoke_swap) {
using traits = swap_invoke_traits;
int a = 3;
int b = 4;
traits::invoke(a, b);
EXPECT_EQ(4, a);
EXPECT_EQ(3, b);
swappable::Obj x{3};
swappable::Obj y{4};
traits::invoke(x, y);
EXPECT_EQ(3, x.x_);
EXPECT_EQ(4, y.x_);
std::swap(x, y);
EXPECT_EQ(4, x.x_);
EXPECT_EQ(3, y.x_);
}
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