Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Common/include/Luau/Variant.h
2727 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#pragma once
3
4
#include <initializer_list>
5
#include <new>
6
#include <type_traits>
7
#include <utility>
8
9
#include <stddef.h>
10
11
namespace Luau
12
{
13
14
template<typename... Ts>
15
class Variant
16
{
17
static_assert(sizeof...(Ts) > 0, "variant must have at least 1 type (empty variants are ill-formed)");
18
static_assert(std::disjunction_v<std::is_void<Ts>...> == false, "variant does not allow void as an alternative type");
19
static_assert(std::disjunction_v<std::is_reference<Ts>...> == false, "variant does not allow references as an alternative type");
20
static_assert(std::disjunction_v<std::is_array<Ts>...> == false, "variant does not allow arrays as an alternative type");
21
22
public:
23
template<typename T>
24
static constexpr int getTypeId()
25
{
26
using TT = std::decay_t<T>;
27
28
constexpr int N = sizeof...(Ts);
29
constexpr bool is[N] = {std::is_same_v<TT, Ts>...};
30
31
for (int i = 0; i < N; ++i)
32
if (is[i])
33
return i;
34
35
return -1;
36
}
37
38
private:
39
template<typename T, typename... Tail>
40
struct First
41
{
42
using type = T;
43
};
44
45
public:
46
using first_alternative = typename First<Ts...>::type;
47
48
template<typename T>
49
static constexpr bool is_part_of_v = std::disjunction_v<typename std::is_same<std::decay_t<Ts>, T>...>;
50
51
Variant()
52
{
53
static_assert(std::is_default_constructible_v<first_alternative>, "first alternative type must be default constructible");
54
typeId = 0;
55
new (&storage) first_alternative();
56
}
57
58
template<typename T>
59
Variant(T&& value, std::enable_if_t<getTypeId<T>() >= 0>* = 0)
60
{
61
using TT = std::decay_t<T>;
62
63
constexpr int tid = getTypeId<T>();
64
typeId = tid;
65
new (&storage) TT(std::forward<T>(value));
66
}
67
68
Variant(const Variant& other)
69
{
70
static constexpr FnCopy table[sizeof...(Ts)] = {&fnCopy<Ts>...};
71
72
typeId = other.typeId;
73
table[typeId](&storage, &other.storage);
74
}
75
76
Variant(Variant&& other) noexcept
77
{
78
typeId = other.typeId;
79
tableMove[typeId](&storage, &other.storage);
80
}
81
82
~Variant()
83
{
84
tableDtor[typeId](&storage);
85
}
86
87
Variant& operator=(const Variant& other)
88
{
89
Variant copy(other);
90
// static_cast<T&&> is equivalent to std::move() but faster in Debug
91
return *this = static_cast<Variant&&>(copy);
92
}
93
94
Variant& operator=(Variant&& other) noexcept
95
{
96
if (this != &other)
97
{
98
tableDtor[typeId](&storage);
99
typeId = other.typeId;
100
tableMove[typeId](&storage, &other.storage); // nothrow
101
}
102
return *this;
103
}
104
105
template<typename T, typename... Args>
106
T& emplace(Args&&... args)
107
{
108
using TT = std::decay_t<T>;
109
constexpr int tid = getTypeId<T>();
110
static_assert(tid >= 0, "unsupported T");
111
112
tableDtor[typeId](&storage);
113
typeId = tid;
114
new (&storage) TT{std::forward<Args>(args)...};
115
116
return *reinterpret_cast<T*>(&storage);
117
}
118
119
template<typename T>
120
const T* get_if() const
121
{
122
constexpr int tid = getTypeId<T>();
123
static_assert(tid >= 0, "unsupported T");
124
125
return tid == typeId ? reinterpret_cast<const T*>(&storage) : nullptr;
126
}
127
128
template<typename T>
129
T* get_if()
130
{
131
constexpr int tid = getTypeId<T>();
132
static_assert(tid >= 0, "unsupported T");
133
134
return tid == typeId ? reinterpret_cast<T*>(&storage) : nullptr;
135
}
136
137
bool valueless_by_exception() const
138
{
139
return false;
140
}
141
142
int index() const
143
{
144
return typeId;
145
}
146
147
bool operator==(const Variant& other) const
148
{
149
static constexpr FnPred table[sizeof...(Ts)] = {&fnPredEq<Ts>...};
150
151
return typeId == other.typeId && table[typeId](&storage, &other.storage);
152
}
153
154
bool operator!=(const Variant& other) const
155
{
156
return !(*this == other);
157
}
158
159
private:
160
static constexpr size_t cmax(std::initializer_list<size_t> l)
161
{
162
size_t res = 0;
163
for (size_t i : l)
164
res = (res < i) ? i : res;
165
return res;
166
}
167
168
static constexpr size_t storageSize = cmax({sizeof(Ts)...});
169
static constexpr size_t storageAlign = cmax({alignof(Ts)...});
170
171
using FnCopy = void (*)(void*, const void*);
172
using FnMove = void (*)(void*, void*) noexcept;
173
using FnDtor = void (*)(void*);
174
using FnPred = bool (*)(const void*, const void*);
175
176
template<typename T>
177
static void fnCopy(void* dst, const void* src)
178
{
179
new (dst) T(*static_cast<const T*>(src));
180
}
181
182
template<typename T>
183
static void fnMove(void* dst, void* src) noexcept
184
{
185
// static_cast<T&&> is equivalent to std::move() but faster in Debug
186
new (dst) T(static_cast<T&&>(*static_cast<T*>(src)));
187
}
188
189
template<typename T>
190
static void fnDtor(void* dst)
191
{
192
static_cast<T*>(dst)->~T();
193
}
194
195
template<typename T>
196
static bool fnPredEq(const void* lhs, const void* rhs)
197
{
198
return *static_cast<const T*>(lhs) == *static_cast<const T*>(rhs);
199
}
200
201
static constexpr FnMove tableMove[sizeof...(Ts)] = {&fnMove<Ts>...};
202
static constexpr FnDtor tableDtor[sizeof...(Ts)] = {&fnDtor<Ts>...};
203
204
int typeId;
205
alignas(storageAlign) char storage[storageSize];
206
207
template<class Visitor, typename... _Ts>
208
friend auto visit(Visitor&& vis, const Variant<_Ts...>& var);
209
template<class Visitor, typename... _Ts>
210
friend auto visit(Visitor&& vis, Variant<_Ts...>& var);
211
};
212
213
template<typename T, typename... Ts>
214
const T* get_if(const Variant<Ts...>* var)
215
{
216
return var ? var->template get_if<T>() : nullptr;
217
}
218
219
template<typename T, typename... Ts>
220
T* get_if(Variant<Ts...>* var)
221
{
222
return var ? var->template get_if<T>() : nullptr;
223
}
224
225
template<typename Visitor, typename Result, typename T>
226
static void fnVisitR(Visitor& vis, Result& dst, std::conditional_t<std::is_const_v<T>, const void, void>* src)
227
{
228
dst = vis(*static_cast<T*>(src));
229
}
230
231
template<typename Visitor, typename T>
232
static void fnVisitV(Visitor& vis, std::conditional_t<std::is_const_v<T>, const void, void>* src)
233
{
234
vis(*static_cast<T*>(src));
235
}
236
237
template<class Visitor, typename... Ts>
238
auto visit(Visitor&& vis, const Variant<Ts...>& var)
239
{
240
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts>...>, "visitor must accept every alternative as an argument");
241
242
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative>;
243
static_assert(
244
std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts>>...>, "visitor result type must be consistent between alternatives"
245
);
246
247
if constexpr (std::is_same_v<Result, void>)
248
{
249
using FnVisitV = void (*)(Visitor&, const void*);
250
static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV<Visitor, const Ts>...};
251
252
tableVisit[var.typeId](vis, &var.storage);
253
}
254
else
255
{
256
using FnVisitR = void (*)(Visitor&, Result&, const void*);
257
static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR<Visitor, Result, const Ts>...};
258
259
Result res;
260
tableVisit[var.typeId](vis, res, &var.storage);
261
return res;
262
}
263
}
264
265
template<class Visitor, typename... Ts>
266
auto visit(Visitor&& vis, Variant<Ts...>& var)
267
{
268
static_assert(std::conjunction_v<std::is_invocable<Visitor, Ts&>...>, "visitor must accept every alternative as an argument");
269
270
using Result = std::invoke_result_t<Visitor, typename Variant<Ts...>::first_alternative&>;
271
static_assert(
272
std::conjunction_v<std::is_same<Result, std::invoke_result_t<Visitor, Ts&>>...>, "visitor result type must be consistent between alternatives"
273
);
274
275
if constexpr (std::is_same_v<Result, void>)
276
{
277
using FnVisitV = void (*)(Visitor&, void*);
278
static const FnVisitV tableVisit[sizeof...(Ts)] = {&fnVisitV<Visitor, Ts>...};
279
280
tableVisit[var.typeId](vis, &var.storage);
281
}
282
else
283
{
284
using FnVisitR = void (*)(Visitor&, Result&, void*);
285
static const FnVisitR tableVisit[sizeof...(Ts)] = {&fnVisitR<Visitor, Result, Ts>...};
286
287
Result res;
288
tableVisit[var.typeId](vis, res, &var.storage);
289
return res;
290
}
291
}
292
293
template<typename... Ts>
294
struct overloaded : Ts...
295
{
296
using Ts::operator()...;
297
};
298
template<typename... Ts>
299
overloaded(Ts...) -> overloaded<Ts...>;
300
301
template<class>
302
inline constexpr bool always_false_v = false;
303
304
} // namespace Luau
305
306