From: Yedidya Feldblum Date: Wed, 1 Nov 2017 05:12:53 +0000 (-0700) Subject: A macro for creating member-invoke traits X-Git-Tag: v2017.11.06.00~22 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=3e4e8fe8704c6ca717cb1de74dd7313b2a33bb48 A macro for creating member-invoke traits Summary: [Folly] A macro for creating member-invoke traits. The macro creates a specialized traits container with member types and aliases mimicking `std::invoke_result` and the related traits types and aliases. Reviewed By: aary Differential Revision: D6195087 fbshipit-source-id: 07c2bbab6cccb04dc8ff12e20923351e8f38abfd --- diff --git a/folly/functional/Invoke.h b/folly/functional/Invoke.h index 0924af7a..2eab3ff6 100644 --- a/folly/functional/Invoke.h +++ b/folly/functional/Invoke.h @@ -160,3 +160,129 @@ struct is_nothrow_invocable_r } // namespace folly #endif + +/*** + * FOLLY_CREATE_MEMBER_INVOKE_TRAITS + * + * Used to create traits container, bound to a specific member-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_MEMBER_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: + * + * struct CanFoo { + * int foo(Bar const&) { return 1; } + * int foo(Car&&) noexcept { return 2; } + * }; + * + * using traits = foo_invoke_traits; + * + * traits::invoke(CanFoo{}, Bar{}) // 1 + * + * traits::invoke_result // has member + * traits::invoke_result_t // int + * traits::invoke_result // empty + * traits::invoke_result_t // error + * + * traits::is_invocable::value // true + * traits::is_invocable::value // false + * + * traits::is_invocable_r::value // true + * traits::is_invocable_r::value // false + * + * traits::is_nothrow_invocable::value // false + * traits::is_nothrow_invocable::value // true + * + * traits::is_nothrow_invocable::value // false + * traits::is_nothrow_invocable::value // false + * traits::is_nothrow_invocable::value // true + * traits::is_nothrow_invocable::value // false + */ +#define FOLLY_CREATE_MEMBER_INVOKE_TRAITS(classname, membername) \ + struct classname { \ + private: \ + template \ + using v_ = ::folly::void_t; \ + template \ + using result_ = \ + decltype(::std::declval().membername(::std::declval()...)); \ + template \ + using nothrow_ = ::std::integral_constant< \ + bool, \ + noexcept(::std::declval().membername(::std::declval()...))>; \ + \ + template \ + struct invoke_result_ {}; \ + template \ + struct invoke_result_>, F, Args...> { \ + using type = result_; \ + }; \ + \ + template \ + struct is_invocable_ : ::std::false_type {}; \ + template \ + struct is_invocable_>, F, Args...> \ + : ::std::true_type {}; \ + \ + template \ + struct is_invocable_r_ : ::std::false_type {}; \ + template \ + struct is_invocable_r_>, R, F, Args...> \ + : ::std::is_convertible, R> {}; \ + \ + template \ + struct is_nothrow_invocable_ : ::std::false_type {}; \ + template \ + struct is_nothrow_invocable_>, F, Args...> \ + : nothrow_ {}; \ + \ + template \ + struct is_nothrow_invocable_r_ : ::std::false_type {}; \ + template \ + struct is_nothrow_invocable_r_>, R, F, Args...> \ + : ::folly::StrictConjunction< \ + ::std::is_convertible, R>, \ + nothrow_> {}; \ + \ + public: \ + template \ + struct invoke_result : invoke_result_ {}; \ + template \ + using invoke_result_t = typename invoke_result::type; \ + template \ + struct is_invocable : is_invocable_ {}; \ + template \ + struct is_invocable_r : is_invocable_r_ {}; \ + template \ + struct is_nothrow_invocable : is_nothrow_invocable_ {}; \ + template \ + struct is_nothrow_invocable_r \ + : is_nothrow_invocable_r_ {}; \ + \ + template \ + static constexpr result_ invoke( \ + F&& f, \ + Args&&... args) noexcept(nothrow_::value) { \ + return std::forward(f).membername(std::forward(args)...); \ + } \ + } diff --git a/folly/functional/test/InvokeTest.cpp b/folly/functional/test/InvokeTest.cpp index 721073d5..d941b925 100644 --- a/folly/functional/test/InvokeTest.cpp +++ b/folly/functional/test/InvokeTest.cpp @@ -33,6 +33,22 @@ struct Fn { } int volatile x_ = 17; }; + +FOLLY_CREATE_MEMBER_INVOKE_TRAITS(test_invoke_traits, test); + +struct Obj { + char test(int, int) noexcept { + return 'a'; + } + int volatile&& test(int, char const*) { + return std::move(x_); + } + float test(float, float) { + return 3.14; + } + int volatile x_ = 17; +}; + } // namespace TEST_F(InvokeTest, invoke) { @@ -81,3 +97,57 @@ TEST_F(InvokeTest, is_nothrow_invocable_r) { EXPECT_FALSE((folly::is_nothrow_invocable_r::value)); EXPECT_FALSE((folly::is_nothrow_invocable_r::value)); } + +TEST_F(InvokeTest, member_invoke) { + using traits = test_invoke_traits; + + Obj fn; + + EXPECT_TRUE(noexcept(traits::invoke(fn, 1, 2))); + EXPECT_FALSE(noexcept(traits::invoke(fn, 1, "2"))); + + EXPECT_EQ('a', traits::invoke(fn, 1, 2)); + EXPECT_EQ(17, traits::invoke(fn, 1, "2")); +} + +TEST_F(InvokeTest, member_invoke_result) { + using traits = test_invoke_traits; + + EXPECT_TRUE( + (std::is_same>::value)); + EXPECT_TRUE( + (std::is_same>:: + value)); +} + +TEST_F(InvokeTest, member_is_invocable) { + using traits = test_invoke_traits; + + EXPECT_TRUE((traits::is_invocable::value)); + EXPECT_TRUE((traits::is_invocable::value)); + EXPECT_FALSE((traits::is_invocable::value)); +} + +TEST_F(InvokeTest, member_is_invocable_r) { + using traits = test_invoke_traits; + + EXPECT_TRUE((traits::is_invocable_r::value)); + EXPECT_TRUE((traits::is_invocable_r::value)); + EXPECT_FALSE((traits::is_invocable_r::value)); +} + +TEST_F(InvokeTest, member_is_nothrow_invocable) { + using traits = test_invoke_traits; + + EXPECT_TRUE((traits::is_nothrow_invocable::value)); + EXPECT_FALSE((traits::is_nothrow_invocable::value)); + EXPECT_FALSE((traits::is_nothrow_invocable::value)); +} + +TEST_F(InvokeTest, member_is_nothrow_invocable_r) { + using traits = test_invoke_traits; + + EXPECT_TRUE((traits::is_nothrow_invocable_r::value)); + EXPECT_FALSE((traits::is_nothrow_invocable_r::value)); + EXPECT_FALSE((traits::is_nothrow_invocable_r::value)); +}