Skip to content

Commit c16be27

Browse files
gchateletZijunZhaoCCK
authored andcommitted
[libc] Add invoke / invoke_result type traits (llvm#65750)
1 parent 2fb9692 commit c16be27

File tree

6 files changed

+208
-0
lines changed

6 files changed

+208
-0
lines changed

libc/src/__support/CPP/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ add_header_library(
105105
type_traits/enable_if.h
106106
type_traits/false_type.h
107107
type_traits/integral_constant.h
108+
type_traits/invoke.h
109+
type_traits/invoke_result.h
108110
type_traits/is_arithmetic.h
109111
type_traits/is_array.h
110112
type_traits/is_base_of.h

libc/src/__support/CPP/type_traits.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include "src/__support/CPP/type_traits/enable_if.h"
1919
#include "src/__support/CPP/type_traits/false_type.h"
2020
#include "src/__support/CPP/type_traits/integral_constant.h"
21+
#include "src/__support/CPP/type_traits/invoke.h"
22+
#include "src/__support/CPP/type_traits/invoke_result.h"
2123
#include "src/__support/CPP/type_traits/is_arithmetic.h"
2224
#include "src/__support/CPP/type_traits/is_array.h"
2325
#include "src/__support/CPP/type_traits/is_base_of.h"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===-- invoke type_traits --------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H
10+
#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H
11+
12+
#include "src/__support/CPP/type_traits/always_false.h"
13+
#include "src/__support/CPP/type_traits/decay.h"
14+
#include "src/__support/CPP/type_traits/enable_if.h"
15+
#include "src/__support/CPP/type_traits/is_base_of.h"
16+
#include "src/__support/CPP/type_traits/is_pointer.h"
17+
#include "src/__support/CPP/type_traits/is_same.h"
18+
#include "src/__support/CPP/utility/forward.h"
19+
20+
namespace __llvm_libc::cpp {
21+
22+
namespace detail {
23+
24+
// Catch all function and functor types.
25+
template <class FunctionPtrType> struct invoke_dispatcher {
26+
template <class T, class... Args,
27+
typename = cpp::enable_if_t<
28+
cpp::is_same_v<cpp::decay_t<T>, FunctionPtrType>>>
29+
static decltype(auto) call(T &&fun, Args &&...args) {
30+
return cpp::forward<T>(fun)(cpp::forward<Args>(args)...);
31+
}
32+
};
33+
34+
// Catch pointer to member function types.
35+
template <class Class, class FunctionReturnType>
36+
struct invoke_dispatcher<FunctionReturnType Class::*> {
37+
using FunctionPtrType = FunctionReturnType Class::*;
38+
39+
template <class T, class... Args, class DecayT = cpp::decay_t<T>>
40+
static decltype(auto) call(FunctionPtrType fun, T &&t1, Args &&...args) {
41+
if constexpr (cpp::is_base_of_v<Class, DecayT>) {
42+
// T is a (possibly cv ref) type.
43+
return (cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
44+
} else if constexpr (cpp::is_pointer_v<T>) {
45+
// T is a pointer type.
46+
return (*cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
47+
} else {
48+
static_assert(cpp::always_false<T>);
49+
}
50+
}
51+
};
52+
53+
} // namespace detail
54+
template <class Function, class... Args>
55+
decltype(auto) invoke(Function &&fun, Args &&...args) {
56+
return detail::invoke_dispatcher<cpp::decay_t<Function>>::call(
57+
cpp::forward<Function>(fun), cpp::forward<Args>(args)...);
58+
}
59+
60+
} // namespace __llvm_libc::cpp
61+
62+
#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_H
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//===-- invoke_result type_traits -------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H
9+
#define LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H
10+
11+
#include "src/__support/CPP/type_traits/invoke.h"
12+
#include "src/__support/CPP/type_traits/type_identity.h"
13+
#include "src/__support/CPP/utility/declval.h"
14+
15+
namespace __llvm_libc::cpp {
16+
17+
template <class F, class... Args>
18+
struct invoke_result : cpp::type_identity<decltype(cpp::invoke(
19+
cpp::declval<F>(), cpp::declval<Args>()...))> {};
20+
21+
template <class F, class... Args>
22+
using invoke_result_t = typename invoke_result<F, Args...>::type;
23+
24+
} // namespace __llvm_libc::cpp
25+
26+
#endif // LLVM_LIBC_SRC_SUPPORT_CPP_TYPE_TRAITS_INVOKE_RESULT_H

libc/test/src/__support/CPP/type_traits_test.cpp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,119 @@ TEST(LlvmLibcTypeTraitsTest, integral_constant) {
145145
EXPECT_EQ((integral_constant<int, 4>::value), 4);
146146
}
147147

148+
namespace invoke_detail {
149+
150+
enum State { INIT = 0, A_APPLY_CALLED, B_APPLY_CALLED };
151+
152+
struct A {
153+
State state = INIT;
154+
virtual ~A() {}
155+
virtual void apply() { state = A_APPLY_CALLED; }
156+
};
157+
158+
struct B : public A {
159+
virtual ~B() {}
160+
virtual void apply() { state = B_APPLY_CALLED; }
161+
};
162+
163+
void free_function() {}
164+
int free_function_return_5() { return 5; }
165+
int free_function_passtrough(int value) { return value; }
166+
167+
struct Delegate {
168+
int (*ptr)(int) = &free_function_passtrough;
169+
};
170+
171+
template <int tag> struct Tag {
172+
static constexpr int value = tag;
173+
};
174+
175+
struct Functor {
176+
auto operator()() & { return Tag<0>(); }
177+
auto operator()() const & { return Tag<1>(); }
178+
auto operator()() && { return Tag<2>(); }
179+
auto operator()() const && { return Tag<3>(); }
180+
181+
const Tag<0> &operator()(const Tag<0> &a) { return a; }
182+
const Tag<0> &&operator()(const Tag<0> &&a) { return cpp::move(a); }
183+
Tag<1> operator()(Tag<1> a) { return a; }
184+
};
185+
186+
} // namespace invoke_detail
187+
188+
TEST(LlvmLibcTypeTraitsTest, invoke) {
189+
using namespace invoke_detail;
190+
{ // member function call
191+
A a;
192+
EXPECT_EQ(a.state, INIT);
193+
invoke(&A::apply, a);
194+
EXPECT_EQ(a.state, A_APPLY_CALLED);
195+
}
196+
{ // overriden member function call
197+
B b;
198+
EXPECT_EQ(b.state, INIT);
199+
invoke(&A::apply, b);
200+
EXPECT_EQ(b.state, B_APPLY_CALLED);
201+
}
202+
{ // free function
203+
invoke(&free_function);
204+
EXPECT_EQ(invoke(&free_function_return_5), 5);
205+
EXPECT_EQ(invoke(&free_function_passtrough, 1), 1);
206+
}
207+
{ // pointer member function call
208+
Delegate d;
209+
EXPECT_EQ(invoke(&Delegate::ptr, d, 2), 2);
210+
}
211+
{ // Functor with several ref qualifier
212+
Functor f;
213+
const Functor cf;
214+
EXPECT_EQ(invoke(f).value, 0);
215+
EXPECT_EQ(invoke(cf).value, 1);
216+
EXPECT_EQ(invoke(move(f)).value, 2);
217+
EXPECT_EQ(invoke(move(cf)).value, 3);
218+
}
219+
{ // lambda
220+
EXPECT_EQ(invoke([]() -> int { return 2; }), 2);
221+
EXPECT_EQ(invoke([](int value) -> int { return value; }, 1), 1);
222+
223+
const auto lambda = [](int) { return 0; };
224+
EXPECT_EQ(invoke(lambda, 1), 0);
225+
}
226+
}
227+
228+
TEST(LlvmLibcTypeTraitsTest, invoke_result) {
229+
using namespace invoke_detail;
230+
EXPECT_TRUE((is_same_v<invoke_result_t<void (A::*)(), A>, void>));
231+
EXPECT_TRUE((is_same_v<invoke_result_t<void (A::*)(), B>, void>));
232+
EXPECT_TRUE((is_same_v<invoke_result_t<void (*)()>, void>));
233+
EXPECT_TRUE((is_same_v<invoke_result_t<int (*)()>, int>));
234+
EXPECT_TRUE((is_same_v<invoke_result_t<int (*)(int), int>, int>));
235+
EXPECT_TRUE((
236+
is_same_v<invoke_result_t<int (*Delegate::*)(int), Delegate, int>, int>));
237+
// Functor with several ref qualifiers
238+
EXPECT_TRUE((is_same_v<invoke_result_t<Functor &>, Tag<0>>));
239+
EXPECT_TRUE((is_same_v<invoke_result_t<Functor const &>, Tag<1>>));
240+
EXPECT_TRUE((is_same_v<invoke_result_t<Functor &&>, Tag<2>>));
241+
EXPECT_TRUE((is_same_v<invoke_result_t<Functor const &&>, Tag<3>>));
242+
// Functor with several arg qualifiers
243+
EXPECT_TRUE(
244+
(is_same_v<invoke_result_t<Functor &&, Tag<0> &>, const Tag<0> &>));
245+
EXPECT_TRUE((is_same_v<invoke_result_t<Functor, Tag<0>>, const Tag<0> &&>));
246+
EXPECT_TRUE((is_same_v<invoke_result_t<Functor, Tag<1>>, Tag<1>>));
247+
{
248+
auto lambda = []() {};
249+
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda)>, void>));
250+
}
251+
{
252+
auto lambda = []() { return 0; };
253+
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda)>, int>));
254+
}
255+
{
256+
auto lambda = [](int) -> double { return 0; };
257+
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda), int>, double>));
258+
}
259+
}
260+
148261
using IntegralAndFloatingTypes =
149262
testing::TypeList<bool, char, short, int, long, long long, unsigned char,
150263
unsigned short, unsigned int, unsigned long,

utils/bazel/llvm-project-overlay/libc/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ libc_support_library(
296296
"src/__support/CPP/type_traits/enable_if.h",
297297
"src/__support/CPP/type_traits/false_type.h",
298298
"src/__support/CPP/type_traits/integral_constant.h",
299+
"src/__support/CPP/type_traits/invoke.h",
300+
"src/__support/CPP/type_traits/invoke_result.h",
299301
"src/__support/CPP/type_traits/is_arithmetic.h",
300302
"src/__support/CPP/type_traits/is_array.h",
301303
"src/__support/CPP/type_traits/is_base_of.h",
@@ -334,6 +336,7 @@ libc_support_library(
334336
"src/__support/CPP/type_traits/type_identity.h",
335337
"src/__support/CPP/type_traits/void_t.h",
336338
"src/__support/CPP/utility/declval.h",
339+
"src/__support/CPP/utility/forward.h",
337340
],
338341
deps = [
339342
":__support_macros_attributes",

0 commit comments

Comments
 (0)