1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#pragma once
#include <util/generic/function.h>
#include <util/system/yassert.h>
#include <functional>
template <typename Signature>
class TFunctionRef;
template <typename Ret, typename... Args, bool IsNoexcept>
class TFunctionRef<Ret(Args...) noexcept(IsNoexcept)> {
public:
using TSignature = Ret(Args...) noexcept(IsNoexcept);
private:
union TErasedCallable {
const void* Functor;
void (*Function)();
};
using TProxy = Ret (*)(TErasedCallable callable, Args...);
// Making this a lambda inside TFunctionRef ctor caused:
// "error: cannot compile this forwarded non-trivially copyable parameter yet"
// on clang-win-i686-release.
//
// Using correct noexcept specifiers here (noexcept(IsNoexcept)) caused miscompilation on clang:
// https://github.com/llvm/llvm-project/issues/55280.
template <typename Functor>
static Ret InvokeErasedFunctor(TErasedCallable callable, Args... args) {
auto& ref = *static_cast<const std::remove_reference_t<Functor>*>(callable.Functor);
return static_cast<Ret>(std::invoke(ref, std::forward<Args>(args)...));
}
template <typename Function>
static Ret InvokeErasedFunction(TErasedCallable callable, Args... args) {
auto* function = reinterpret_cast<Function*>(callable.Function);
return static_cast<Ret>(std::invoke(function, std::forward<Args>(args)...));
}
template <class F>
static constexpr bool IsInvocableUsing = std::conditional_t<
IsNoexcept,
std::is_nothrow_invocable_r<Ret, F, Args...>,
std::is_invocable_r<Ret, F, Args...>>::value;
// clang-format off
template <class Callable>
static constexpr bool IsSuitableFunctor =
IsInvocableUsing<Callable>
&& !std::is_function_v<Callable>
&& !std::is_same_v<std::remove_cvref_t<Callable>, TFunctionRef>;
template <class Callable>
static constexpr bool IsSuitableFunction =
IsInvocableUsing<Callable>
&& std::is_function_v<Callable>;
// clang-format on
public:
// Function ref should not be default constructible.
// While the function ref can have empty state (for example, Proxy_ == nullptr),
// It does not make sense in common usage cases.
TFunctionRef() = delete;
// Construct function ref from a functor.
template <typename Functor, typename = std::enable_if_t<IsSuitableFunctor<Functor>>>
TFunctionRef(Functor&& functor) noexcept
: Callable_{
.Functor = std::addressof(functor),
}
, Proxy_{InvokeErasedFunctor<Functor>}
{
}
// Construct function ref from a function pointer.
template <typename Function, typename = std::enable_if_t<IsSuitableFunction<Function>>>
TFunctionRef(Function* function) noexcept
: Callable_{
.Function = reinterpret_cast<void (*)()>(function),
}
, Proxy_{InvokeErasedFunction<Function>}
{
}
// Copy ctors & assignment.
// Just copy pointers.
TFunctionRef(const TFunctionRef& rhs) noexcept = default;
TFunctionRef& operator=(const TFunctionRef& rhs) noexcept = default;
Ret operator()(Args... args) const noexcept(IsNoexcept) {
return Proxy_(Callable_, std::forward<Args>(args)...);
}
private:
TErasedCallable Callable_;
TProxy Proxy_ = nullptr;
};
namespace NPrivate {
template <typename Callable, typename Signature = typename TCallableTraits<Callable>::TSignature>
struct TIsNothrowInvocable;
template <typename Callable, typename Ret, typename... Args>
struct TIsNothrowInvocable<Callable, Ret(Args...)> {
static constexpr bool IsNoexcept = std::is_nothrow_invocable_r_v<Ret, Callable, Args...>;
using TSignature = Ret(Args...) noexcept(IsNoexcept);
};
template <typename Callable>
struct TCallableTraitsWithNoexcept {
using TSignature = typename TIsNothrowInvocable<Callable>::TSignature;
};
} // namespace NPrivate
template <typename Callable>
TFunctionRef(Callable&&) -> TFunctionRef<typename NPrivate::TCallableTraitsWithNoexcept<Callable>::TSignature>;
template <typename Function>
TFunctionRef(Function*) -> TFunctionRef<Function>;
|