diff options
Diffstat (limited to 'absl')
-rw-r--r-- | absl/base/internal/invoke.h | 21 | ||||
-rw-r--r-- | absl/base/invoke_test.cc | 102 |
2 files changed, 123 insertions, 0 deletions
diff --git a/absl/base/internal/invoke.h b/absl/base/internal/invoke.h index e9efb2fc..4a644c68 100644 --- a/absl/base/internal/invoke.h +++ b/absl/base/internal/invoke.h @@ -49,6 +49,7 @@ namespace base_internal { using std::invoke; using std::invoke_result_t; +using std::is_invocable_r; } // namespace base_internal ABSL_NAMESPACE_END @@ -201,6 +202,26 @@ invoke_result_t<F, Args...> invoke(F&& f, Args&&... args) { return Invoker<F, Args...>::type::Invoke(std::forward<F>(f), std::forward<Args>(args)...); } + +template <typename AlwaysVoid, typename, typename, typename...> +struct IsInvocableRImpl : std::false_type {}; + +template <typename R, typename F, typename... Args> +struct IsInvocableRImpl< + absl::void_t<absl::base_internal::invoke_result_t<F, Args...> >, R, F, + Args...> + : std::integral_constant< + bool, + std::is_convertible<absl::base_internal::invoke_result_t<F, Args...>, + R>::value || + std::is_void<R>::value> {}; + +// Type trait whose member `value` is true if invoking `F` with `Args` is valid, +// and either the return type is convertible to `R`, or `R` is void. +// C++11-compatible version of `std::is_invocable_r`. +template <typename R, typename F, typename... Args> +using is_invocable_r = IsInvocableRImpl<void, R, F, Args...>; + } // namespace base_internal ABSL_NAMESPACE_END } // namespace absl diff --git a/absl/base/invoke_test.cc b/absl/base/invoke_test.cc index bcdef36c..7be26f64 100644 --- a/absl/base/invoke_test.cc +++ b/absl/base/invoke_test.cc @@ -31,6 +31,14 @@ namespace { int Function(int a, int b) { return a - b; } +void VoidFunction(int& a, int& b) { + a += b; + b = a - b; + a -= b; +} + +int ZeroArgFunction() { return -1937; } + int Sink(std::unique_ptr<int> p) { return *p; } @@ -223,6 +231,100 @@ TEST(InvokeTest, SfinaeFriendly) { EXPECT_THAT(CallMaybeWithArg(Factory), ::testing::Pointee(42)); } +TEST(IsInvocableRTest, CallableExactMatch) { + static_assert( + base_internal::is_invocable_r<int, decltype(Function), int, int>::value, + "Should be true for exact match of types on a free function"); +} + +TEST(IsInvocableRTest, CallableArgumentConversionMatch) { + static_assert( + base_internal::is_invocable_r<int, decltype(Function), char, int>::value, + "Should be true for convertible argument type"); +} + +TEST(IsInvocableRTest, CallableReturnConversionMatch) { + static_assert(base_internal::is_invocable_r<double, decltype(Function), int, + int>::value, + "Should be true for convertible return type"); +} + +TEST(IsInvocableRTest, CallableReturnVoid) { + static_assert(base_internal::is_invocable_r<void, decltype(VoidFunction), + int&, int&>::value, + "Should be true for void expected and actual return types"); + static_assert( + base_internal::is_invocable_r<void, decltype(Function), int, int>::value, + "Should be true for void expected and non-void actual return types"); +} + +TEST(IsInvocableRTest, CallableRefQualifierMismatch) { + static_assert(!base_internal::is_invocable_r<void, decltype(VoidFunction), + int&, const int&>::value, + "Should be false for reference constness mismatch"); + static_assert(!base_internal::is_invocable_r<void, decltype(VoidFunction), + int&&, int&>::value, + "Should be false for reference value category mismatch"); +} + +TEST(IsInvocableRTest, CallableArgumentTypeMismatch) { + static_assert(!base_internal::is_invocable_r<int, decltype(Function), + std::string, int>::value, + "Should be false for argument type mismatch"); +} + +TEST(IsInvocableRTest, CallableReturnTypeMismatch) { + static_assert(!base_internal::is_invocable_r<std::string, decltype(Function), + int, int>::value, + "Should be false for return type mismatch"); +} + +TEST(IsInvocableRTest, CallableTooFewArgs) { + static_assert( + !base_internal::is_invocable_r<int, decltype(Function), int>::value, + "Should be false for too few arguments"); +} + +TEST(IsInvocableRTest, CallableTooManyArgs) { + static_assert(!base_internal::is_invocable_r<int, decltype(Function), int, + int, int>::value, + "Should be false for too many arguments"); +} + +TEST(IsInvocableRTest, MemberFunctionAndReference) { + static_assert(base_internal::is_invocable_r<int, decltype(&Class::Method), + Class&, int, int>::value, + "Should be true for exact match of types on a member function " + "and class reference"); +} + +TEST(IsInvocableRTest, MemberFunctionAndPointer) { + static_assert(base_internal::is_invocable_r<int, decltype(&Class::Method), + Class*, int, int>::value, + "Should be true for exact match of types on a member function " + "and class pointer"); +} + +TEST(IsInvocableRTest, DataMemberAndReference) { + static_assert(base_internal::is_invocable_r<int, decltype(&Class::member), + Class&>::value, + "Should be true for exact match of types on a data member and " + "class reference"); +} + +TEST(IsInvocableRTest, DataMemberAndPointer) { + static_assert(base_internal::is_invocable_r<int, decltype(&Class::member), + Class*>::value, + "Should be true for exact match of types on a data member and " + "class pointer"); +} + +TEST(IsInvocableRTest, CallableZeroArgs) { + static_assert( + base_internal::is_invocable_r<int, decltype(ZeroArgFunction)>::value, + "Should be true for exact match for a zero-arg free function"); +} + } // namespace } // namespace base_internal ABSL_NAMESPACE_END |