Skip to content

Commit

Permalink
[libc] Adjust the cpp:function type to support lambdas
Browse files Browse the repository at this point in the history
The current function type does not support generic lambdas because it
relies on the lambda being implicitly convertible to a function pointer.
This patch adds support for this by copying the existing lightweight
`function_ref` type.

Reviewed By: sivachandra

Differential Revision: https://reviews.llvm.org/D148971
  • Loading branch information
jhuber6 committed Apr 24, 2023
1 parent 50445df commit 7090c10
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 7 deletions.
47 changes: 41 additions & 6 deletions libc/src/__support/CPP/functional.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,54 @@
#ifndef LLVM_LIBC_SRC_SUPPORT_CPP_FUNCTIONAL_H
#define LLVM_LIBC_SRC_SUPPORT_CPP_FUNCTIONAL_H

#include "src/__support/CPP/type_traits.h"
#include "src/__support/CPP/utility.h"
#include "src/__support/macros/attributes.h"

#include <stdint.h>

namespace __llvm_libc {
namespace cpp {

template <typename F> class function;
/// A function type adapted from LLVM's function_ref.
/// This class does not own the callable, so it is not in general safe to
/// store a function.
template <typename Fn> class function;

template <typename Ret, typename... Params> class function<Ret(Params...)> {
Ret (*callback)(intptr_t callable, Params... params) = nullptr;
intptr_t callable;

template <typename R, typename... Args> class function<R(Args...)> {
R (*func)(Args...) = nullptr;
template <typename Callable>
LIBC_INLINE static Ret callback_fn(intptr_t callable, Params... params) {
return (*reinterpret_cast<Callable *>(callable))(
forward<Params>(params)...);
}

public:
constexpr function() = default;
template <typename F> constexpr function(F &&f) : func(f) {}
LIBC_INLINE function() = default;
LIBC_INLINE function(decltype(nullptr)) {}
LIBC_INLINE ~function() = default;

template <typename Callable>
LIBC_INLINE function(
Callable &&callable,
// This is not the copy-constructor.
enable_if_t<!is_same<remove_cvref_t<Callable>, function>::value> * =
nullptr,
// Functor must be callable and return a suitable type.
enable_if_t<is_void_v<Ret> ||
is_convertible_v<
decltype(declval<Callable>()(declval<Params>()...)), Ret>>
* = nullptr)
: callback(callback_fn<remove_reference_t<Callable>>),
callable(reinterpret_cast<intptr_t>(&callable)) {}

LIBC_INLINE Ret operator()(Params... params) const {
return callback(callable, forward<Params>(params)...);
}

constexpr R operator()(Args... params) { return func(params...); }
LIBC_INLINE explicit operator bool() const { return callback; }
};

} // namespace cpp
Expand Down
37 changes: 36 additions & 1 deletion libc/src/__support/CPP/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
namespace __llvm_libc {
namespace cpp {

template <typename T> struct type_identity { using type = T; };
template <typename T> struct type_identity {
using type = T;
};

template <bool B, typename T> struct enable_if;
template <typename T> struct enable_if<true, T> : type_identity<T> {};
Expand Down Expand Up @@ -49,6 +51,19 @@ template <typename T> struct remove_cv<volatile T> : type_identity<T> {};
template <typename T> struct remove_cv<const volatile T> : type_identity<T> {};
template <typename T> using remove_cv_t = typename remove_cv<T>::type;

template <typename T> struct remove_reference : type_identity<T> {};
template <typename T> struct remove_reference<T &> : type_identity<T> {};
template <typename T> struct remove_reference<T &&> : type_identity<T> {};
template <typename T>
using remove_reference_t = typename remove_reference<T>::type;

template <typename T> struct add_rvalue_reference : type_identity<T &&> {};

template <typename T> struct remove_cvref {
using type = remove_cv_t<remove_reference_t<T>>;
};
template <typename T> using remove_cvref_t = typename remove_cvref<T>::type;

namespace details {
template <typename T, typename... Args> constexpr bool is_unqualified_any_of() {
return (... || is_same_v<remove_cv_t<T>, Args>);
Expand Down Expand Up @@ -148,6 +163,26 @@ struct conditional<false, T, F> : type_identity<F> {};
template <bool B, typename T, typename F>
using conditional_t = typename conditional<B, T, F>::type;

template <typename T>
struct is_void : is_same<void, typename remove_cv<T>::type> {};
template <typename T> inline constexpr bool is_void_v = is_void<T>::value;
template <class T> T declval();

// Compile time checks on implicit conversions.
namespace details {
template <typename...> using void_t = void;
template <typename T> void convertible_to_helper(T);
} // namespace details

template <typename F, typename T, typename = void>
inline constexpr bool is_convertible_v = false;

template <typename F, typename T>
inline constexpr bool
is_convertible_v<F, T,
details::void_t<decltype(details::convertible_to_helper<T>(
declval<F>()))>> = true;

} // namespace cpp
} // namespace __llvm_libc

Expand Down
16 changes: 16 additions & 0 deletions libc/src/__support/CPP/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_LIBC_SRC_SUPPORT_CPP_UTILITY_H

#include "src/__support/CPP/type_traits.h"
#include "src/__support/macros/attributes.h"

namespace __llvm_libc::cpp {

Expand All @@ -35,6 +36,21 @@ template <typename T, int N>
using make_integer_sequence =
typename internal::make_integer_sequence<T, N - 1>::type;

template <typename T>
LIBC_INLINE constexpr T &&forward(typename remove_reference<T>::type &value) {
return static_cast<T &&>(value);
}

template <typename T>
LIBC_INLINE constexpr T &&forward(typename remove_reference<T>::type &&value) {
return static_cast<T &&>(value);
}

template <typename T>
LIBC_INLINE constexpr typename remove_reference<T>::type &&move(T &&value) {
return static_cast<typename remove_reference<T>::type &&>(value);
}

} // namespace __llvm_libc::cpp

#endif // LLVM_LIBC_SRC_SUPPORT_CPP_UTILITY_H

0 comments on commit 7090c10

Please sign in to comment.