Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libc/src/__support/CPP/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,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
Expand Down
2 changes: 2 additions & 0 deletions libc/src/__support/CPP/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
59 changes: 59 additions & 0 deletions libc/src/__support/CPP/type_traits/invoke.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//===-- 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/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_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 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 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 {
// T is assumed to be a pointer type.
return (*cpp::forward<T>(t1).*fun)(cpp::forward<Args>(args)...);
}
}
};

} // namespace detail

template <class Function, class... Args>
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
26 changes: 26 additions & 0 deletions libc/src/__support/CPP/type_traits/invoke_result.h
Original file line number Diff line number Diff line change
@@ -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/utility/declval.h"

namespace __llvm_libc::cpp {

template <class F, class... Args> struct invoke_result {
using type =
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
89 changes: 89 additions & 0 deletions libc/test/src/__support/CPP/type_traits_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,95 @@ 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;
};

} // namespace invoke_detail

TEST(LlvmLibcTypeTraitsTest, invoke) {
using namespace invoke_detail;
{ // member function call
A a;
EXPECT_EQ(a.state, INIT);
cpp::invoke(&A::apply, a);
EXPECT_EQ(a.state, A_APPLY_CALLED);
}
{ // overriden member function call
B b;
EXPECT_EQ(b.state, INIT);
cpp::invoke(&A::apply, b);
EXPECT_EQ(b.state, B_APPLY_CALLED);
}
{ // free function
cpp::invoke(&free_function);
EXPECT_EQ(cpp::invoke(&free_function_return_5), 5);
EXPECT_EQ(cpp::invoke(&free_function_passtrough, 1), 1);
}
{ // pointer member function call
Delegate d;
EXPECT_EQ(cpp::invoke(&Delegate::ptr, d, 2), 2);
}
{ // lambda
EXPECT_EQ(cpp::invoke([]() -> int { return 2; }), 2);
EXPECT_EQ(cpp::invoke([](int value) -> int { return value; }, 1), 1);

const auto lambda = [](int) { return 0; };
EXPECT_EQ(cpp::invoke(lambda, 1), 0);
}
}

TEST(LlvmLibcTypeTraitsTest, invoke_result) {
using namespace invoke_detail;
EXPECT_TRUE(
(cpp::is_same_v<cpp::invoke_result_t<decltype(&A::apply), A>, void>));
EXPECT_TRUE(
(cpp::is_same_v<cpp::invoke_result_t<decltype(&A::apply), B>, void>));
EXPECT_TRUE(
(cpp::is_same_v<cpp::invoke_result_t<decltype(&free_function)>, void>));
EXPECT_TRUE(
(cpp::is_same_v<cpp::invoke_result_t<decltype(&free_function_return_5)>,
int>));
EXPECT_TRUE((cpp::is_same_v<
cpp::invoke_result_t<decltype(&free_function_passtrough), int>,
int>));
EXPECT_TRUE(
(cpp::is_same_v<
cpp::invoke_result_t<decltype(&Delegate::ptr), Delegate, int>, int>));
{
auto lambda = []() {};
EXPECT_TRUE((cpp::is_same_v<cpp::invoke_result_t<decltype(lambda)>, void>));
}
{
auto lambda = []() { return 0; };
EXPECT_TRUE((cpp::is_same_v<cpp::invoke_result_t<decltype(lambda)>, int>));
}
{
auto lambda = [](int) -> double { return 0; };
EXPECT_TRUE(
(cpp::is_same_v<cpp::invoke_result_t<decltype(lambda), int>, double>));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're missing tests for a functor with overloaded types:

auto lambda = [](auto a) { return a; };
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda), int>, int>));
EXPECT_TRUE((is_same_v<invoke_result_t<decltype(lambda), double>, double>));

maybe also with auto& and auto&&.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx! This helped me catch a bug where the function return type should be decltype(auto) instead of auto for perfect forwarding.


using IntegralAndFloatingTypes =
testing::TypeList<bool, char, short, int, long, long long, unsigned char,
unsigned short, unsigned int, unsigned long,
Expand Down
3 changes: 3 additions & 0 deletions utils/bazel/llvm-project-overlay/libc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,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",
Expand Down Expand Up @@ -333,6 +335,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",
Expand Down