1// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
2// Licensed under the MIT License:
3//
4// Permission is hereby granted, free of charge, to any person obtaining a copy
5// of this software and associated documentation files (the "Software"), to deal
6// in the Software without restriction, including without limitation the rights
7// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8// copies of the Software, and to permit persons to whom the Software is
9// furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20// THE SOFTWARE.
21
22#pragma once
23
24#if defined(__GNUC__) && !KJ_HEADER_WARNINGS
25#pragma GCC system_header
26#endif
27
28#include "common.h"
29
30namespace kj {
31
32namespace _ { // private
33
34template <uint i, typename Key, typename First, typename... Rest>
35struct TypeIndex_ { static constexpr uint value = TypeIndex_<i + 1, Key, Rest...>::value; };
36template <uint i, typename Key, typename... Rest>
37struct TypeIndex_<i, Key, Key, Rest...> { static constexpr uint value = i; };
38
39enum class Variants0 {};
40enum class Variants1 { _variant0 };
41enum class Variants2 { _variant0, _variant1 };
42enum class Variants3 { _variant0, _variant1, _variant2 };
43enum class Variants4 { _variant0, _variant1, _variant2, _variant3 };
44enum class Variants5 { _variant0, _variant1, _variant2, _variant3, _variant4 };
45enum class Variants6 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5 };
46enum class Variants7 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6 };
47enum class Variants8 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
48 _variant7 };
49
50template <uint i> struct Variants_;
51template <> struct Variants_<0> { typedef Variants0 Type; };
52template <> struct Variants_<1> { typedef Variants1 Type; };
53template <> struct Variants_<2> { typedef Variants2 Type; };
54template <> struct Variants_<3> { typedef Variants3 Type; };
55template <> struct Variants_<4> { typedef Variants4 Type; };
56template <> struct Variants_<5> { typedef Variants5 Type; };
57template <> struct Variants_<6> { typedef Variants6 Type; };
58template <> struct Variants_<7> { typedef Variants7 Type; };
59template <> struct Variants_<8> { typedef Variants8 Type; };
60
61template <uint i>
62using Variants = typename Variants_<i>::Type;
63
64} // namespace _ (private)
65
66template <typename... Variants>
67class OneOf {
68 template <typename Key>
69 static inline constexpr uint typeIndex() { return _::TypeIndex_<1, Key, Variants...>::value; }
70 // Get the 1-based index of Key within the type list Types.
71
72public:
73 inline OneOf(): tag(0) {}
74 OneOf(const OneOf& other) { copyFrom(other); }
75 OneOf(OneOf& other) { copyFrom(other); }
76 OneOf(OneOf&& other) { moveFrom(other); }
77 template <typename T>
78 OneOf(T&& other): tag(typeIndex<Decay<T>>()) {
79 ctor(*reinterpret_cast<Decay<T>*>(space), kj::fwd<T>(other));
80 }
81 ~OneOf() { destroy(); }
82
83 OneOf& operator=(const OneOf& other) { if (tag != 0) destroy(); copyFrom(other); return *this; }
84 OneOf& operator=(OneOf&& other) { if (tag != 0) destroy(); moveFrom(other); return *this; }
85
86 inline bool operator==(decltype(nullptr)) const { return tag == 0; }
87 inline bool operator!=(decltype(nullptr)) const { return tag != 0; }
88
89 template <typename T>
90 bool is() const {
91 return tag == typeIndex<T>();
92 }
93
94 template <typename T>
95 T& get() {
96 KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
97 return *reinterpret_cast<T*>(space);
98 }
99 template <typename T>
100 const T& get() const {
101 KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
102 return *reinterpret_cast<const T*>(space);
103 }
104
105 template <typename T, typename... Params>
106 T& init(Params&&... params) {
107 if (tag != 0) destroy();
108 ctor(*reinterpret_cast<T*>(space), kj::fwd<Params>(params)...);
109 tag = typeIndex<T>();
110 return *reinterpret_cast<T*>(space);
111 }
112
113 template <typename T>
114 Maybe<T&> tryGet() {
115 if (is<T>()) {
116 return *reinterpret_cast<T*>(space);
117 } else {
118 return nullptr;
119 }
120 }
121
122 template <uint i>
123 KJ_NORETURN(void allHandled());
124 // After a series of if/else blocks handling each variant of the OneOf, have the final else
125 // block call allHandled<n>() where n is the number of variants. This will fail to compile
126 // if new variants are added in the future.
127
128 typedef _::Variants<sizeof...(Variants)> Tag;
129
130 Tag which() const {
131 KJ_IREQUIRE(tag != 0, "Can't KJ_SWITCH_ONEOF() on uninitialized value.");
132 return static_cast<Tag>(tag - 1);
133 }
134
135 template <typename T>
136 static constexpr Tag tagFor() {
137 return static_cast<Tag>(typeIndex<T>() - 1);
138 }
139
140 OneOf* _switchSubject() & { return this; }
141 const OneOf* _switchSubject() const& { return this; }
142 _::NullableValue<OneOf> _switchSubject() && { return kj::mv(*this); }
143
144private:
145 uint tag;
146
147 static inline constexpr size_t maxSize(size_t a) {
148 return a;
149 }
150 template <typename... Rest>
151 static inline constexpr size_t maxSize(size_t a, size_t b, Rest... rest) {
152 return maxSize(kj::max(a, b), rest...);
153 }
154 // Returns the maximum of all the parameters.
155 // TODO(someday): Generalize the above template and make it common. I tried, but C++ decided to
156 // be difficult so I cut my losses.
157
158 static constexpr auto spaceSize = maxSize(sizeof(Variants)...);
159 // TODO(msvc): This constant could just as well go directly inside space's bracket's, where it's
160 // used, but MSVC suffers a parse error on `...`.
161
162 union {
163 byte space[spaceSize];
164
165 void* forceAligned;
166 // TODO(someday): Use C++11 alignas() once we require GCC 4.8 / Clang 3.3.
167 };
168
169 template <typename... T>
170 inline void doAll(T... t) {}
171
172 template <typename T>
173 inline bool destroyVariant() {
174 if (tag == typeIndex<T>()) {
175 tag = 0;
176 dtor(*reinterpret_cast<T*>(space));
177 }
178 return false;
179 }
180 void destroy() {
181 doAll(destroyVariant<Variants>()...);
182 }
183
184 template <typename T>
185 inline bool copyVariantFrom(const OneOf& other) {
186 if (other.is<T>()) {
187 ctor(*reinterpret_cast<T*>(space), other.get<T>());
188 }
189 return false;
190 }
191 void copyFrom(const OneOf& other) {
192 // Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
193 // is invalid.
194 tag = other.tag;
195 doAll(copyVariantFrom<Variants>(other)...);
196 }
197
198 template <typename T>
199 inline bool copyVariantFrom(OneOf& other) {
200 if (other.is<T>()) {
201 ctor(*reinterpret_cast<T*>(space), other.get<T>());
202 }
203 return false;
204 }
205 void copyFrom(OneOf& other) {
206 // Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
207 // is invalid.
208 tag = other.tag;
209 doAll(copyVariantFrom<Variants>(other)...);
210 }
211
212 template <typename T>
213 inline bool moveVariantFrom(OneOf& other) {
214 if (other.is<T>()) {
215 ctor(*reinterpret_cast<T*>(space), kj::mv(other.get<T>()));
216 }
217 return false;
218 }
219 void moveFrom(OneOf& other) {
220 // Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
221 // is invalid.
222 tag = other.tag;
223 doAll(moveVariantFrom<Variants>(other)...);
224 }
225};
226
227template <typename... Variants>
228template <uint i>
229void OneOf<Variants...>::allHandled() {
230 // After a series of if/else blocks handling each variant of the OneOf, have the final else
231 // block call allHandled<n>() where n is the number of variants. This will fail to compile
232 // if new variants are added in the future.
233
234 static_assert(i == sizeof...(Variants), "new OneOf variants need to be handled here");
235 KJ_UNREACHABLE;
236}
237
238#if __cplusplus > 201402L
239#define KJ_SWITCH_ONEOF(value) \
240 switch (auto _kj_switch_subject = (value)._switchSubject(); _kj_switch_subject->which())
241#else
242#define KJ_SWITCH_ONEOF(value) \
243 /* Without C++17, we can only support one switch per containing block. Deal with it. */ \
244 auto _kj_switch_subject = (value)._switchSubject(); \
245 switch (_kj_switch_subject->which())
246#endif
247#define KJ_CASE_ONEOF(name, ...) \
248 break; \
249 case ::kj::Decay<decltype(*_kj_switch_subject)>::tagFor<__VA_ARGS__>(): \
250 for (auto& name = _kj_switch_subject->get<__VA_ARGS__>(), *_kj_switch_done = &name; \
251 _kj_switch_done; _kj_switch_done = nullptr)
252#define KJ_CASE_ONEOF_DEFAULT break; default:
253// Allows switching over a OneOf.
254//
255// Example:
256//
257// kj::OneOf<int, float, const char*> variant;
258// KJ_SWITCH_ONEOF(variant) {
259// KJ_CASE_ONEOF(i, int) {
260// doSomethingWithInt(i);
261// }
262// KJ_CASE_ONEOF(s, const char*) {
263// doSomethingWithString(s);
264// }
265// KJ_CASE_ONEOF_DEFAULT {
266// doSomethingElse();
267// }
268// }
269//
270// Notes:
271// - If you don't handle all possible types and don't include a default branch, you'll get a
272// compiler warning, just like a regular switch() over an enum where one of the enum values is
273// missing.
274// - There's no need for a `break` statement in a KJ_CASE_ONEOF; it is implied.
275// - Under C++11 and C++14, only one KJ_SWITCH_ONEOF() can appear in a block. Wrap the switch in
276// a pair of braces if you need a second switch in the same block. If C++17 is enabled, this is
277// not an issue.
278//
279// Implementation notes:
280// - The use of __VA_ARGS__ is to account for template types that have commas separating type
281// parameters, since macros don't recognize <> as grouping.
282// - _kj_switch_done is really used as a boolean flag to prevent the for() loop from actually
283// looping, but it's defined as a pointer since that's all we can define in this context.
284
285} // namespace kj
286