-
Notifications
You must be signed in to change notification settings - Fork 3
/
unique_function.hpp
322 lines (264 loc) · 9.24 KB
/
unique_function.hpp
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#pragma once
#ifndef BEYOND_UNIQUE_FUNCTION_HPP
#define BEYOND_UNIQUE_FUNCTION_HPP
#include <functional>
#include <memory>
#include <type_traits>
#include <cassert>
#ifndef BEYOND_FUNCTIONS_NAMESPACE
#define BEYOND_FUNCTIONS_NAMESPACE beyond
#endif
namespace BEYOND_FUNCTIONS_NAMESPACE {
template <typename R, typename... Args> class unique_function;
namespace detail {
template <typename R, typename... Args> struct unique_function_base;
enum class unique_function_behaviors { move_to, destory };
union unique_function_storage {
alignas(void*) std::byte small_[32];
void* large_;
template <class T>
static constexpr bool fit_small = sizeof(T) <= sizeof(small_) &&
alignof(T) <= alignof(decltype(small_)) &&
std::is_nothrow_move_constructible_v<T>;
unique_function_storage() noexcept = default;
template <typename Func, typename... Data>
auto emplace(Data&&... args) -> void
{
if constexpr (fit_small<Func>) {
::new (static_cast<void*>(&small_)) Func(std::forward<Data>(args)...);
} else {
large_ = new Func(std::forward<Data>(args)...);
}
}
template <typename R, typename... Args> struct behaviors {
template <typename Func>
static R invoke(const unique_function_base<R, Args...>& who, Args&&... args)
{
constexpr static bool fit_sm = fit_small<Func>;
void* data = const_cast<void*>(fit_sm ? &who.storage_.small_
: who.storage_.large_);
return (*static_cast<Func*>(data))(std::forward<Args>(args)...);
}
template <typename Func>
static auto dispatch(unique_function_behaviors behavior,
unique_function_base<R, Args...>& who, void* ret)
-> void
{
constexpr static bool fit_sm = fit_small<Func>;
void* data = fit_sm ? &who.storage_.small_ : who.storage_.large_;
switch (behavior) {
case detail::unique_function_behaviors::destory:
if constexpr (fit_sm) {
static_cast<Func*>(data)->~Func();
} else {
delete static_cast<Func*>(who.storage_.large_);
}
break;
case detail::unique_function_behaviors::move_to: {
auto* func_ptr = static_cast<unique_function_base<R, Args...>*>(ret);
func_ptr->reset();
func_ptr->storage_.template emplace<Func>(
std::move(*static_cast<Func*>(data)));
func_ptr->behaviors_ = behaviors<R, Args...>::template dispatch<Func>;
func_ptr->function_ptr_ = behaviors<R, Args...>::template invoke<Func>;
who.reset();
} break;
}
}
};
};
template <typename R, typename... Args> struct unique_function_base {
public:
using result_type = R;
unique_function_base() = default;
~unique_function_base()
{
this->reset();
}
template <
typename Func, class DFunc = std::decay_t<Func>,
class = std::enable_if_t<!std::is_same_v<DFunc, unique_function_base> &&
std::is_move_constructible_v<DFunc>>>
explicit unique_function_base(Func&& func)
{
static_assert(std::is_invocable_r_v<R, DFunc, Args...>);
storage_.emplace<DFunc>(std::forward<DFunc>(func));
behaviors_ = detail::unique_function_storage::behaviors<
R, Args...>::template dispatch<DFunc>;
function_ptr_ = detail::unique_function_storage::behaviors<
R, Args...>::template invoke<DFunc>;
}
unique_function_base(const unique_function_base&) = delete;
auto
operator=(const unique_function_base&) & -> unique_function_base& = delete;
unique_function_base(unique_function_base&& other) noexcept
{
if (other) {
other.behaviors_(detail::unique_function_behaviors::move_to, other, this);
}
}
auto operator=(unique_function_base&& other) & noexcept
-> unique_function_base&
{
if (other) {
other.behaviors_(detail::unique_function_behaviors::move_to, other, this);
} else {
this->reset();
}
return *this;
}
[[nodiscard]] operator bool() const noexcept // NOLINT
{
return behaviors_ != nullptr;
}
auto swap(unique_function_base& other) noexcept -> void
{
unique_function_base temp = std::move(other);
other = std::move(*this);
*this = std::move(temp);
}
protected:
auto invoke(Args... args) const -> R
{
#ifndef BEYOND_FUNCTIONS_NO_EXCEPTION
if (*this) {
return this->function_ptr_(*this, std::forward<Args>(args)...);
} else {
throw std::bad_function_call{};
}
#else
return this->function_ptr_(*this, std::forward<Args>(args)...);
#endif
}
private:
friend union detail::unique_function_storage;
void (*behaviors_)(detail::unique_function_behaviors, unique_function_base&,
void*) = nullptr;
R (*function_ptr_)(const unique_function_base&, Args&&...) = nullptr;
detail::unique_function_storage storage_;
void reset()
{
if (behaviors_) {
behaviors_(detail::unique_function_behaviors::destory, *this, nullptr);
}
function_ptr_ = nullptr;
behaviors_ = nullptr;
}
};
} // namespace detail
template <typename R, typename... Args>
class unique_function<R(Args...)>
: public detail::unique_function_base<R, Args...> {
using base_type = detail::unique_function_base<R, Args...>;
public:
unique_function() = default;
template <typename Func, class DFunc = std::decay_t<Func>,
class = std::enable_if_t<!std::is_same_v<DFunc, unique_function> &&
std::is_move_constructible_v<DFunc>>>
explicit unique_function(Func&& func) : base_type{std::forward<Func>(func)}
{
}
unique_function(unique_function<R(Args...) const>&& other)
: base_type{static_cast<base_type&&>(other)}
{
}
auto operator()(Args... args) -> R
{
return this->invoke(std::forward<Args>(args)...);
}
};
template <typename R, typename... Args>
class unique_function<R(Args...) const>
: public detail::unique_function_base<R, Args...> {
public:
unique_function() = default;
template <typename Func, class DFunc = std::decay_t<Func>,
class = std::enable_if_t<!std::is_same_v<DFunc, unique_function> &&
std::is_move_constructible_v<DFunc>>,
class = std::void_t<
decltype(std::declval<const Func&>()(std::declval<Args>()...))>>
explicit unique_function(Func&& func)
: detail::unique_function_base<R, Args...>{std::forward<Func>(func)}
{
}
auto operator()(Args... args) const -> R
{
return this->invoke(std::forward<Args>(args)...);
}
};
template <class Func>
auto swap(unique_function<Func>& lhs, unique_function<Func>& rhs) noexcept
-> void
{
lhs.swap(rhs);
}
template <class Func>
auto operator==(const unique_function<Func>& lhs, std::nullptr_t) noexcept
-> bool
{
return !lhs;
}
template <class Func>
auto operator==(std::nullptr_t, const unique_function<Func>& lhs) noexcept
-> bool
{
return !lhs;
}
template <class Func>
auto operator!=(const unique_function<Func>& lhs, std::nullptr_t) noexcept
-> bool
{
return lhs;
}
template <class Func>
auto operator!=(std::nullptr_t, const unique_function<Func>& lhs) noexcept
-> bool
{
return lhs;
}
// deduction guides
template <class R, typename... Args>
unique_function(R (*)(Args...))->unique_function<R(Args...) const>;
namespace detail {
// TODO: Support member functions that take this by reference
template <typename T> struct member_function_pointer_trait {
};
#define BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(CV_OPT, NOEXCEPT_OPT) \
template <typename R, typename U, typename... Args> \
struct member_function_pointer_trait<R (U::*)(Args...) \
CV_OPT NOEXCEPT_OPT> { \
using return_type = R; \
using guide_type = R(Args...); \
};
#define BEYOND_NOARG
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(BEYOND_NOARG, BEYOND_NOARG)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(const, BEYOND_NOARG)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(const volatile, BEYOND_NOARG)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(volatile, BEYOND_NOARG)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(BEYOND_NOARG, noexcept)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(const, noexcept)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(const volatile, noexcept)
BEYOND_MEMBER_FUNCTION_POINTER_TRAIT(volatile, noexcept)
#undef BEYOND_NOARG
#undef BEYOND_MEMBER_FUNCTION_POINTER_TRAIT
// Main template: cannot find &Func::operator()
template <typename Func, typename = void>
struct function_deduce_signature_impl {
};
template <typename Func>
struct function_deduce_signature_impl<
Func, std::void_t<decltype(&Func::operator())>> {
using type = member_function_pointer_trait<decltype(&Func::operator())>;
};
template <typename Func>
struct function_deduce_signature
: function_deduce_signature_impl<std::remove_cv_t<Func>> {
};
} // namespace detail
template <class Func, class = std::enable_if_t<!std::is_pointer_v<Func>>>
unique_function(Func)
->unique_function<
typename detail::function_deduce_signature<Func>::type::guide_type>;
} // namespace BEYOND_FUNCTIONS_NAMESPACE
#undef BEYOND_FUNCTIONS_NAMESPACE
#endif // BEYOND_UNIQUE_FUNCTION_HPP