[libc] Add invoke / invoke_result type traits (#65750)

This commit is contained in:
Guillaume Chatelet 2023-09-15 09:15:41 +00:00 committed by GitHub
parent ea42b49f10
commit 2dbdc9fc85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 208 additions and 0 deletions

View File

@ -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

View File

@ -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"

View File

@ -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 <class FunctionPtrType> struct invoke_dispatcher {
template <class T, class... Args,
typename = cpp::enable_if_t<
cpp::is_same_v<cpp::decay_t<T>, FunctionPtrType>>>
static decltype(auto) call(T &&fun, Args &&...args) {
return cpp::forward<T>(fun)(cpp::forward<Args>(args)...);
}
};
// Catch pointer to member function types.
template <class Class, class FunctionReturnType>
struct invoke_dispatcher<FunctionReturnType Class::*> {
using FunctionPtrType = FunctionReturnType Class::*;
template <class T, class... Args, class DecayT = cpp::decay_t<T>>
static decltype(auto) call(FunctionPtrType fun, T &&t1, Args &&...args) {
if constexpr (cpp::is_base_of_v<Class, DecayT>) {
// T is a (possibly cv ref) type.
return (cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
} else if constexpr (cpp::is_pointer_v<T>) {
// T is a pointer type.
return (*cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
} else {
static_assert(cpp::always_false<T>);
}
}
};
} // namespace detail
template <class Function, class... Args>
decltype(auto) invoke(Function &&fun, Args &&...args) {
return detail::invoke_dispatcher<cpp::decay_t<Function>>::call(
cpp::forward<Function>(fun), cpp::forward<Args>(args)...);
}
} // namespace __llvm_libc::cpp
#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H

View File

@ -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 <class F, class... Args>
struct invoke_result : cpp::type_identity<decltype(cpp::invoke(
cpp::declval<F>(), cpp::declval<Args>()...))> {};
template <class F, class... Args>
using invoke_result_t = typename invoke_result<F, Args...>::type;
} // namespace __llvm_libc::cpp
#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H

View File

@ -145,6 +145,119 @@ TEST(LlvmLibcTypeTraitsTest, integral_constant) {
EXPECT_EQ((integral_constant<int, 4>::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 <int tag> 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<invoke_result_t<void (A::*)(), A>, void>));
EXPECT_TRUE((is_same_v<invoke_result_t<void (A::*)(), B>, void>));
EXPECT_TRUE((is_same_v<invoke_result_t<void (*)()>, void>));
EXPECT_TRUE((is_same_v<invoke_result_t<int (*)()>, int>));
EXPECT_TRUE((is_same_v<invoke_result_t<int (*)(int), int>, int>));
EXPECT_TRUE((
is_same_v<invoke_result_t<int (*Delegate::*)(int), Delegate, int>, int>));
// Functor with several ref qualifiers
EXPECT_TRUE((is_same_v<invoke_result_t<Functor &>, Tag<0>>));
EXPECT_TRUE((is_same_v<invoke_result_t<Functor const &>, Tag<1>>));
EXPECT_TRUE((is_same_v<invoke_result_t<Functor &&>, Tag<2>>));
EXPECT_TRUE((is_same_v<invoke_result_t<Functor const &&>, Tag<3>>));
// Functor with several arg qualifiers
EXPECT_TRUE(
(is_same_v<invoke_result_t<Functor &&, Tag<0> &>, const Tag<0> &>));
EXPECT_TRUE((is_same_v<invoke_result_t<Functor, Tag<0>>, const Tag<0> &&>));
EXPECT_TRUE((is_same_v<invoke_result_t<Functor, Tag<1>>, Tag<1>>));
{
auto lambda = []() {};
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda)>, void>));
}
{
auto lambda = []() { return 0; };
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda)>, int>));
}
{
auto lambda = [](int) -> double { return 0; };
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda), int>, double>));
}
}
using IntegralAndFloatingTypes =
testing::TypeList<bool, char, short, int, long, long long, unsigned char,
unsigned short, unsigned int, unsigned long,

View File

@ -296,6 +296,8 @@ libc_support_library(
"src/__support/CPP/type_traits/enable_if.h",
"src/__support/CPP/type_traits/false_type.h",
"src/__support/CPP/type_traits/integral_constant.h",
"src/__support/CPP/type_traits/invoke.h",
"src/__support/CPP/type_traits/invoke_result.h",
"src/__support/CPP/type_traits/is_arithmetic.h",
"src/__support/CPP/type_traits/is_array.h",
"src/__support/CPP/type_traits/is_base_of.h",
@ -334,6 +336,7 @@ libc_support_library(
"src/__support/CPP/type_traits/type_identity.h",
"src/__support/CPP/type_traits/void_t.h",
"src/__support/CPP/utility/declval.h",
"src/__support/CPP/utility/forward.h",
],
deps = [
":__support_macros_attributes",