diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt index 4e3e4a3edf381..a2d3bd7df9e90 100644 --- a/libc/src/__support/CPP/CMakeLists.txt +++ b/libc/src/__support/CPP/CMakeLists.txt @@ -105,6 +105,8 @@ add_header_library( type_traits/enable_if.h type_traits/false_type.h type_traits/integral_constant.h + type_traits/invoke.h + type_traits/invoke_result.h type_traits/is_arithmetic.h type_traits/is_array.h type_traits/is_base_of.h diff --git a/libc/src/__support/CPP/type_traits.h b/libc/src/__support/CPP/type_traits.h index 9deb08b221593..3de2ca5890318 100644 --- a/libc/src/__support/CPP/type_traits.h +++ b/libc/src/__support/CPP/type_traits.h @@ -18,6 +18,8 @@ #include "src/__support/CPP/type_traits/enable_if.h" #include "src/__support/CPP/type_traits/false_type.h" #include "src/__support/CPP/type_traits/integral_constant.h" +#include "src/__support/CPP/type_traits/invoke.h" +#include "src/__support/CPP/type_traits/invoke_result.h" #include "src/__support/CPP/type_traits/is_arithmetic.h" #include "src/__support/CPP/type_traits/is_array.h" #include "src/__support/CPP/type_traits/is_base_of.h" diff --git a/libc/src/__support/CPP/type_traits/invoke.h b/libc/src/__support/CPP/type_traits/invoke.h new file mode 100644 index 0000000000000..e5294ffcba74f --- /dev/null +++ b/libc/src/__support/CPP/type_traits/invoke.h @@ -0,0 +1,62 @@ +//===-- invoke type_traits --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H + +#include "src/__support/CPP/type_traits/always_false.h" +#include "src/__support/CPP/type_traits/decay.h" +#include "src/__support/CPP/type_traits/enable_if.h" +#include "src/__support/CPP/type_traits/is_base_of.h" +#include "src/__support/CPP/type_traits/is_pointer.h" +#include "src/__support/CPP/type_traits/is_same.h" +#include "src/__support/CPP/utility/forward.h" + +namespace __llvm_libc::cpp { + +namespace detail { + +// Catch all function and functor types. +template struct invoke_dispatcher { + template , FunctionPtrType>>> + static decltype(auto) call(T &&fun, Args &&...args) { + return cpp::forward(fun)(cpp::forward(args)...); + } +}; + +// Catch pointer to member function types. +template +struct invoke_dispatcher { + using FunctionPtrType = FunctionReturnType Class::*; + + template > + static decltype(auto) call(FunctionPtrType fun, T &&t1, Args &&...args) { + if constexpr (cpp::is_base_of_v) { + // T is a (possibly cv ref) type. + return (cpp::forward(t1).*fun)(cpp::forward(args)...); + } else if constexpr (cpp::is_pointer_v) { + // T is a pointer type. + return (*cpp::forward(t1).*fun)(cpp::forward(args)...); + } else { + static_assert(cpp::always_false); + } + } +}; + +} // namespace detail +template +decltype(auto) invoke(Function &&fun, Args &&...args) { + return detail::invoke_dispatcher>::call( + cpp::forward(fun), cpp::forward(args)...); +} + +} // namespace __llvm_libc::cpp + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H diff --git a/libc/src/__support/CPP/type_traits/invoke_result.h b/libc/src/__support/CPP/type_traits/invoke_result.h new file mode 100644 index 0000000000000..20ebba4e4cf95 --- /dev/null +++ b/libc/src/__support/CPP/type_traits/invoke_result.h @@ -0,0 +1,26 @@ +//===-- invoke_result type_traits -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H +#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H + +#include "src/__support/CPP/type_traits/invoke.h" +#include "src/__support/CPP/type_traits/type_identity.h" +#include "src/__support/CPP/utility/declval.h" + +namespace __llvm_libc::cpp { + +template +struct invoke_result : cpp::type_identity(), cpp::declval()...))> {}; + +template +using invoke_result_t = typename invoke_result::type; + +} // namespace __llvm_libc::cpp + +#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H diff --git a/libc/test/src/__support/CPP/type_traits_test.cpp b/libc/test/src/__support/CPP/type_traits_test.cpp index cd025f588388d..4e1eb41b73992 100644 --- a/libc/test/src/__support/CPP/type_traits_test.cpp +++ b/libc/test/src/__support/CPP/type_traits_test.cpp @@ -145,6 +145,119 @@ TEST(LlvmLibcTypeTraitsTest, integral_constant) { EXPECT_EQ((integral_constant::value), 4); } +namespace invoke_detail { + +enum State { INIT = 0, A_APPLY_CALLED, B_APPLY_CALLED }; + +struct A { + State state = INIT; + virtual ~A() {} + virtual void apply() { state = A_APPLY_CALLED; } +}; + +struct B : public A { + virtual ~B() {} + virtual void apply() { state = B_APPLY_CALLED; } +}; + +void free_function() {} +int free_function_return_5() { return 5; } +int free_function_passtrough(int value) { return value; } + +struct Delegate { + int (*ptr)(int) = &free_function_passtrough; +}; + +template struct Tag { + static constexpr int value = tag; +}; + +struct Functor { + auto operator()() & { return Tag<0>(); } + auto operator()() const & { return Tag<1>(); } + auto operator()() && { return Tag<2>(); } + auto operator()() const && { return Tag<3>(); } + + const Tag<0> &operator()(const Tag<0> &a) { return a; } + const Tag<0> &&operator()(const Tag<0> &&a) { return cpp::move(a); } + Tag<1> operator()(Tag<1> a) { return a; } +}; + +} // namespace invoke_detail + +TEST(LlvmLibcTypeTraitsTest, invoke) { + using namespace invoke_detail; + { // member function call + A a; + EXPECT_EQ(a.state, INIT); + invoke(&A::apply, a); + EXPECT_EQ(a.state, A_APPLY_CALLED); + } + { // overriden member function call + B b; + EXPECT_EQ(b.state, INIT); + invoke(&A::apply, b); + EXPECT_EQ(b.state, B_APPLY_CALLED); + } + { // free function + invoke(&free_function); + EXPECT_EQ(invoke(&free_function_return_5), 5); + EXPECT_EQ(invoke(&free_function_passtrough, 1), 1); + } + { // pointer member function call + Delegate d; + EXPECT_EQ(invoke(&Delegate::ptr, d, 2), 2); + } + { // Functor with several ref qualifier + Functor f; + const Functor cf; + EXPECT_EQ(invoke(f).value, 0); + EXPECT_EQ(invoke(cf).value, 1); + EXPECT_EQ(invoke(move(f)).value, 2); + EXPECT_EQ(invoke(move(cf)).value, 3); + } + { // lambda + EXPECT_EQ(invoke([]() -> int { return 2; }), 2); + EXPECT_EQ(invoke([](int value) -> int { return value; }, 1), 1); + + const auto lambda = [](int) { return 0; }; + EXPECT_EQ(invoke(lambda, 1), 0); + } +} + +TEST(LlvmLibcTypeTraitsTest, invoke_result) { + using namespace invoke_detail; + EXPECT_TRUE((is_same_v, void>)); + EXPECT_TRUE((is_same_v, void>)); + EXPECT_TRUE((is_same_v, void>)); + EXPECT_TRUE((is_same_v, int>)); + EXPECT_TRUE((is_same_v, int>)); + EXPECT_TRUE(( + is_same_v, int>)); + // Functor with several ref qualifiers + EXPECT_TRUE((is_same_v, Tag<0>>)); + EXPECT_TRUE((is_same_v, Tag<1>>)); + EXPECT_TRUE((is_same_v, Tag<2>>)); + EXPECT_TRUE((is_same_v, Tag<3>>)); + // Functor with several arg qualifiers + EXPECT_TRUE( + (is_same_v &>, const Tag<0> &>)); + EXPECT_TRUE((is_same_v>, const Tag<0> &&>)); + EXPECT_TRUE((is_same_v>, Tag<1>>)); + { + auto lambda = []() {}; + EXPECT_TRUE((is_same_v, void>)); + } + { + auto lambda = []() { return 0; }; + EXPECT_TRUE((is_same_v, int>)); + } + { + auto lambda = [](int) -> double { return 0; }; + EXPECT_TRUE((is_same_v, double>)); + } +} + using IntegralAndFloatingTypes = testing::TypeList