-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathstatic_lambda
283 lines (242 loc) · 12.1 KB
/
static_lambda
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
/*
Copyright (C) 2018-2024 Geoffrey Daniels. https://gpdaniels.com/
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef GTL_CONTAINER_STATIC_LAMBDA_HPP
#define GTL_CONTAINER_STATIC_LAMBDA_HPP
// Summary: Lambda function class that uses the stack for storage.
#ifndef NDEBUG
# if defined(_MSC_VER)
# define __builtin_trap() __debugbreak()
# endif
/// @brief A simple assert macro to break the program if the static_lambda is misused.
# define GTL_STATIC_LAMBDA_ASSERT(ASSERTION, MESSAGE) static_cast<void>((ASSERTION) || (__builtin_trap(), 0))
#else
/// @brief At release time the assert macro is implemented as a nop.
# define GTL_STATIC_LAMBDA_ASSERT(ASSERTION, MESSAGE) static_cast<void>(0)
#endif
namespace gtl {
// Forward declare class to allow a unique definition of operator new using it as a parameter.
template <typename function_type, unsigned long long int size>
class static_lambda;
}
namespace {
// Operator new requires the size_t type for its size argument.
using size_t = decltype(sizeof(0));
}
/// @brief Custom placement operator new to avoid including the (massive) <new> header.
/// @tparam function_type Type required by the unused static_lambda parameter.
/// @tparam static_lambda_size Value required by the unused static_lambda parameter.
/// @param size The size of the data to placement new on.
/// @param pointer The pointer of the data to placement new on.
/// @param unused_type_tag An unused type tag used to make this placement new operator function unique.
template <typename function_type, unsigned long long int static_lambda_size>
inline void* operator new(size_t size, void* pointer, gtl::static_lambda<function_type, static_lambda_size>* unused_type_tag) {
static_cast<void>(size);
static_cast<void>(unused_type_tag);
return pointer;
}
/// @brief Custom placement operator delete to avoid compilers complaing about potential memory leaks.
/// @tparam function_type Type required by the unused static_lambda parameter.
/// @tparam static_lambda_size Value required by the unused static_lambda parameter.
/// @param data The pointer of the data to placement delete on.
/// @param pointer The pointer of the data to placement delete on.
/// @param unused_type_tag An unused type tag used to make this placement delete operator function unique.
template <typename function_type, unsigned long long int static_lambda_size>
inline void operator delete(void* data, void* pointer, gtl::static_lambda<function_type, static_lambda_size>* unused_type_tag) {
static_cast<void>(data);
static_cast<void>(pointer);
static_cast<void>(unused_type_tag);
}
namespace gtl {
/// @brief An optionally capturing lambda that is stored on the stack.
/// @tparam return_type The return type of the lambda to store.
/// @tparam argument_types The argument types of the lambda to store.
/// @tparam size The reserved stack buffer size.
template <typename return_type, typename... argument_types, unsigned long long int size>
class static_lambda<return_type(argument_types...), size> final {
private:
static_assert(size > 0, "Size of static_lambda storage must be greater than zero.");
private:
/// @brief A template to remove references from types.
template<typename type>
struct remove_reference final {
using type_unreferenced = type;
};
/// @brief A template to remove references from types.
template<typename type>
struct remove_reference<type&> final {
using type_unreferenced = type;
};
/// @brief A template to remove references from types.
template<typename type>
struct remove_reference<type&&> final {
using type_unreferenced = type;
};
/// @brief A template to check if two types are the same.
template<typename lhs_type, typename rhs_type>
struct is_same_type final {
constexpr static const bool value = false;
};
/// @brief A template to check if two types are the same.
template<typename type>
struct is_same_type<type, type> final {
constexpr static const bool value = true;
};
private:
/// @brief The type used to store and modify the passed in lambda function.
using erased_type = void*;
/// @brief The type used to store and access the passed in lambda function.
using erased_type_const = const void*;
/// @brief The type of the copier function created on construction of the internal lambda.
using copier_type = void (*)(erased_type_const, erased_type);
/// @brief The type of the executer function created on construction of the internal lambda.
using executor_type = return_type (*)(erased_type_const, argument_types...);
/// @brief The type of the destructor function created on construction of the internal lambda.
using destructor_type = void (*)(erased_type);
private:
/// @brief A stack allocated buffer to store the lambda function.
alignas(sizeof(executor_type)) unsigned char function[size];
/// @brief A function pointer to a lambda to copy the function.
copier_type copier;
/// @brief A function pointer to a lambda to execute the function.
executor_type executor;
/// @brief A function pointer to a lambda to destruct the function.
destructor_type destructor;
private:
/// @brief Swap function to simplify constructors by following the copy-and-swap idiom.
/// @param lhs The left hand side static_lambda to swap.
/// @param rhs The right hand side static_lambda to swap.
constexpr static void swap(static_lambda& lhs, static_lambda& rhs) {
for (unsigned long long int index = 0; index < size; ++index) {
unsigned char byte = lhs.function[index];
lhs.function[index] = rhs.function[index];
rhs.function[index] = byte;
}
copier_type copier = lhs.copier;
lhs.copier = rhs.copier;
rhs.copier = copier;
executor_type executor = lhs.executor;
lhs.executor = rhs.executor;
rhs.executor = executor;
destructor_type destructor = lhs.destructor;
lhs.destructor = rhs.destructor;
rhs.destructor = destructor;
}
public:
/// @brief Destructor function to cleanup the internal type erased lambda.
~static_lambda() {
if (this->destructor) {
this->destructor(this->function);
}
}
/// @brief Empty constructor.
constexpr static_lambda()
: function{}
, copier(nullptr)
, executor(nullptr)
, destructor(nullptr) {
}
/// @brief Copy constructor for const static_lambda types.
/// @param other The static_lambda to copy.
constexpr static_lambda(const static_lambda& other)
: function{}
, copier(other.copier)
, executor(other.executor)
, destructor(other.destructor) {
if (this->copier) {
this->copier(other.function, this->function);
}
}
/// @brief Move constructor.
/// @param other The static_lambda to move.
constexpr static_lambda(static_lambda&& other)
: static_lambda() {
static_lambda::swap(*this, other);
}
/// @brief Copy assignment operator for const static_lambda types.
/// @param other The static_lambda to copy.
constexpr static_lambda& operator=(const static_lambda& other) {
static_lambda::swap(*this, static_lambda(other));
return *this;
}
/// @brief Move assignment operator.
/// @param other The static_lambda to move.
constexpr static_lambda& operator=(static_lambda&& other) {
static_lambda::swap(*this, other);
return *this;
}
public:
/// @brief Universal constructor from a template type.
/// @tparam function_type The lambda type to store.
/// @param raw_function The lambda to store.
template <typename function_type>
constexpr static_lambda(function_type&& raw_function)
: static_lambda() {
// First get real function type.
using real_function_type = decltype(raw_function);
using pure_function_type = typename remove_reference<real_function_type>::type_unreferenced;
// If input is already a static_lambda type, cast can call the appropriate constructor.
constexpr bool is_lambda = is_same_type<static_lambda, pure_function_type>::value;
if constexpr (is_lambda) {
constexpr bool is_move = is_same_type<real_function_type, pure_function_type&&>::value;
if constexpr (is_move) {
static_lambda other(static_cast<static_lambda&&>(raw_function));
static_lambda::swap(*this, other);
}
else {
static_lambda other(static_cast<const static_lambda&>(raw_function));
static_lambda::swap(*this, other);
}
}
else {
static_assert(sizeof(pure_function_type) <= size, "Functional object is larger than static_lambda size.");
this->copier = [](erased_type_const source, erased_type destination) -> void {
constexpr bool is_move = is_same_type<real_function_type, pure_function_type&&>::value;
if constexpr ((is_move)) {
new (destination, static_cast<static_lambda*>(nullptr)) pure_function_type(static_cast<function_type&&>(*static_cast<pure_function_type*>(const_cast<erased_type>(source))));
}
else {
new (destination, static_cast<static_lambda*>(nullptr)) pure_function_type(static_cast<const function_type&>(*static_cast<pure_function_type*>(const_cast<erased_type>(source))));
}
};
this->executor = [](erased_type_const function_pointer, argument_types... arguments) -> return_type {
return static_cast<const pure_function_type*>(function_pointer)->operator()(arguments...);
};
this->destructor = [](erased_type function_pointer) -> void {
static_cast<pure_function_type*>(function_pointer)->~pure_function_type();
};
this->copier(&raw_function, this->function);
}
}
/// @brief Asignment operator from a null pointer.
constexpr static_lambda& operator=(decltype(nullptr)) {
static_lambda other;
static_lambda::swap(*this, other);
return *this;
}
public:
/// @brief Boolean operator to check if the internal lambda is valid.
constexpr operator bool() const {
return (this->executor != nullptr);
}
public:
/// @brief The function call operator is overloaded to call the internal lambda function.
/// @param arguments Any required internal lambda function arguments.
constexpr return_type operator()(argument_types... arguments) const {
GTL_STATIC_LAMBDA_ASSERT(this->operator bool() == true, "Executing a null lambda.");
return this->executor(this->function, arguments...);
}
};
}
#undef GTL_STATIC_LAMBDA_ASSERT
#endif // GTL_CONTAINER_STATIC_LAMBDA_HPP