-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlambda
245 lines (208 loc) · 9.58 KB
/
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
/*
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_LAMBDA_HPP
#define GTL_CONTAINER_LAMBDA_HPP
// Summary: Lambda function class that uses the heap for storage.
#ifndef NDEBUG
# if defined(_MSC_VER)
# define __builtin_trap() __debugbreak()
# endif
/// @brief A simple assert macro to break the program if the lambda is misused.
# define GTL_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_LAMBDA_ASSERT(ASSERTION, MESSAGE) static_cast<void>(0)
#endif
namespace gtl {
/// @brief Class declaration to enable expansion of the function_type in template specialisation.
/// @tparam function_type The lambda type to store.
template <typename function_type>
class lambda;
/// @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.
template <typename return_type, typename... argument_types>
class lambda<return_type(argument_types...)> final {
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 An allocated buffer to store the lambda function.
erased_type function;
/// @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 lambda to swap.
/// @param rhs The right hand side lambda to swap.
constexpr static void swap(lambda& lhs, lambda& rhs) {
erased_type function = lhs.function;
lhs.function = rhs.function;
rhs.function = function;
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.
~lambda() {
if (this->destructor) {
this->destructor(this->function);
}
}
/// @brief Empty constructor.
constexpr lambda()
: function(nullptr)
, copier(nullptr)
, executor(nullptr)
, destructor(nullptr) {
}
/// @brief Copy constructor for const lambda types.
/// @param other The lambda to copy.
constexpr lambda(const lambda& other)
: function(nullptr)
, copier(other.copier)
, executor(other.executor)
, destructor(other.destructor) {
if (this->copier) {
this->copier(other.function, this->function);
}
}
/// @brief Move constructor.
/// @param other The lambda to move.
constexpr lambda(lambda&& other)
: lambda() {
lambda::swap(*this, other);
}
/// @brief Copy assignment operator for const lambda types.
/// @param other The lambda to copy.
constexpr lambda& operator=(const lambda& other) {
lambda::swap(*this, lambda(other));
return *this;
}
/// @brief Move assignment operator.
/// @param other The lambda to move.
constexpr lambda& operator=(lambda&& other) {
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 lambda(function_type&& raw_function)
: 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 lambda type, cast can call the appropriate constructor.
constexpr bool is_lambda = is_same_type<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) {
lambda other(static_cast<lambda&&>(raw_function));
lambda::swap(*this, other);
}
else {
lambda other(static_cast<const lambda&>(raw_function));
lambda::swap(*this, other);
}
}
else {
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) {
destination = new pure_function_type(static_cast<function_type&&>(*static_cast<pure_function_type*>(const_cast<erased_type>(source))));
}
else {
destination = new 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 {
delete static_cast<pure_function_type*>(function_pointer);
function_pointer = nullptr;
};
this->copier(&raw_function, this->function);
}
}
/// @brief Asignment operator from a null pointer.
constexpr lambda& operator=(decltype(nullptr)) {
lambda other;
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_LAMBDA_ASSERT(this->operator bool() == true, "Executing a null lambda.");
return this->executor(this->function, arguments...);
}
};
}
#undef GTL_LAMBDA_ASSERT
#endif // GTL_CONTAINER_LAMBDA_HPP