one-of.h (18467B)
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 #include "common.h" 25 26 KJ_BEGIN_HEADER 27 28 namespace kj { 29 30 namespace _ { // private 31 32 template <uint i, template<uint> class Fail, typename Key, typename... Variants> 33 struct TypeIndex_; 34 template <uint i, template<uint> class Fail, typename Key, typename First, typename... Rest> 35 struct TypeIndex_<i, Fail, Key, First, Rest...> { 36 static constexpr uint value = TypeIndex_<i + 1, Fail, Key, Rest...>::value; 37 }; 38 template <uint i, template<uint> class Fail, typename Key, typename... Rest> 39 struct TypeIndex_<i, Fail, Key, Key, Rest...> { static constexpr uint value = i; }; 40 template <uint i, template<uint> class Fail, typename Key> 41 struct TypeIndex_<i, Fail, Key>: public Fail<i> {}; 42 43 template <uint i> 44 struct OneOfFailError_ { 45 static_assert(i == -1, "type does not match any in OneOf"); 46 }; 47 template <uint i> 48 struct OneOfFailZero_ { 49 static constexpr int value = 0; 50 }; 51 52 template <uint i> 53 struct SuccessIfNotZero { 54 typedef int Success; 55 }; 56 template <> 57 struct SuccessIfNotZero<0> {}; 58 59 enum class Variants0 {}; 60 enum class Variants1 { _variant0 }; 61 enum class Variants2 { _variant0, _variant1 }; 62 enum class Variants3 { _variant0, _variant1, _variant2 }; 63 enum class Variants4 { _variant0, _variant1, _variant2, _variant3 }; 64 enum class Variants5 { _variant0, _variant1, _variant2, _variant3, _variant4 }; 65 enum class Variants6 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5 }; 66 enum class Variants7 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6 }; 67 enum class Variants8 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 68 _variant7 }; 69 enum class Variants9 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 70 _variant7, _variant8 }; 71 enum class Variants10 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 72 _variant7, _variant8, _variant9 }; 73 enum class Variants11 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 74 _variant7, _variant8, _variant9, _variant10 }; 75 enum class Variants12 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 76 _variant7, _variant8, _variant9, _variant10, _variant11 }; 77 enum class Variants13 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 78 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12 }; 79 enum class Variants14 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 80 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 81 _variant13 }; 82 enum class Variants15 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 83 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 84 _variant13, _variant14 }; 85 enum class Variants16 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 86 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 87 _variant13, _variant14, _variant15 }; 88 enum class Variants17 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 89 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 90 _variant13, _variant14, _variant15, _variant16 }; 91 enum class Variants18 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 92 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 93 _variant13, _variant14, _variant15, _variant16, _variant17 }; 94 enum class Variants19 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 95 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 96 _variant13, _variant14, _variant15, _variant16, _variant17, _variant18 }; 97 enum class Variants20 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6, 98 _variant7, _variant8, _variant9, _variant10, _variant11, _variant12, 99 _variant13, _variant14, _variant15, _variant16, _variant17, _variant18, 100 _variant19 }; 101 102 template <uint i> struct Variants_; 103 template <> struct Variants_<0> { typedef Variants0 Type; }; 104 template <> struct Variants_<1> { typedef Variants1 Type; }; 105 template <> struct Variants_<2> { typedef Variants2 Type; }; 106 template <> struct Variants_<3> { typedef Variants3 Type; }; 107 template <> struct Variants_<4> { typedef Variants4 Type; }; 108 template <> struct Variants_<5> { typedef Variants5 Type; }; 109 template <> struct Variants_<6> { typedef Variants6 Type; }; 110 template <> struct Variants_<7> { typedef Variants7 Type; }; 111 template <> struct Variants_<8> { typedef Variants8 Type; }; 112 template <> struct Variants_<9> { typedef Variants9 Type; }; 113 template <> struct Variants_<10> { typedef Variants10 Type; }; 114 template <> struct Variants_<11> { typedef Variants11 Type; }; 115 template <> struct Variants_<12> { typedef Variants12 Type; }; 116 template <> struct Variants_<13> { typedef Variants13 Type; }; 117 template <> struct Variants_<14> { typedef Variants14 Type; }; 118 template <> struct Variants_<15> { typedef Variants15 Type; }; 119 template <> struct Variants_<16> { typedef Variants16 Type; }; 120 template <> struct Variants_<17> { typedef Variants17 Type; }; 121 template <> struct Variants_<18> { typedef Variants18 Type; }; 122 template <> struct Variants_<19> { typedef Variants19 Type; }; 123 template <> struct Variants_<20> { typedef Variants20 Type; }; 124 125 template <uint i> 126 using Variants = typename Variants_<i>::Type; 127 128 } // namespace _ (private) 129 130 template <typename... Variants> 131 class OneOf { 132 template <typename Key> 133 static inline constexpr uint typeIndex() { 134 return _::TypeIndex_<1, _::OneOfFailError_, Key, Variants...>::value; 135 } 136 // Get the 1-based index of Key within the type list Types, or static_assert with a nice error. 137 138 template <typename Key> 139 static inline constexpr uint typeIndexOrZero() { 140 return _::TypeIndex_<1, _::OneOfFailZero_, Key, Variants...>::value; 141 } 142 143 template <uint i, typename... OtherVariants> 144 struct HasAll; 145 // Has a member type called "Success" if and only if all of `OtherVariants` are types that 146 // appear in `Variants`. Used with SFINAE to enable subset constructors. 147 148 public: 149 inline OneOf(): tag(0) {} 150 151 OneOf(const OneOf& other) { copyFrom(other); } 152 OneOf(OneOf& other) { copyFrom(other); } 153 OneOf(OneOf&& other) { moveFrom(other); } 154 // Copy/move from same OneOf type. 155 156 template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success> 157 OneOf(const OneOf<OtherVariants...>& other) { copyFromSubset(other); } 158 template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success> 159 OneOf(OneOf<OtherVariants...>& other) { copyFromSubset(other); } 160 template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success> 161 OneOf(OneOf<OtherVariants...>&& other) { moveFromSubset(other); } 162 // Copy/move from OneOf that contains a subset of the types we do. 163 164 template <typename T, typename = typename HasAll<0, Decay<T>>::Success> 165 OneOf(T&& other): tag(typeIndex<Decay<T>>()) { 166 ctor(*reinterpret_cast<Decay<T>*>(space), kj::fwd<T>(other)); 167 } 168 // Copy/move from a value that matches one of the individual types in the OneOf. 169 170 ~OneOf() { destroy(); } 171 172 OneOf& operator=(const OneOf& other) { if (tag != 0) destroy(); copyFrom(other); return *this; } 173 OneOf& operator=(OneOf&& other) { if (tag != 0) destroy(); moveFrom(other); return *this; } 174 175 inline bool operator==(decltype(nullptr)) const { return tag == 0; } 176 inline bool operator!=(decltype(nullptr)) const { return tag != 0; } 177 178 template <typename T> 179 bool is() const { 180 return tag == typeIndex<T>(); 181 } 182 183 template <typename T> 184 T& get() & { 185 KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>()."); 186 return *reinterpret_cast<T*>(space); 187 } 188 template <typename T> 189 T&& get() && { 190 KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>()."); 191 return kj::mv(*reinterpret_cast<T*>(space)); 192 } 193 template <typename T> 194 const T& get() const& { 195 KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>()."); 196 return *reinterpret_cast<const T*>(space); 197 } 198 template <typename T> 199 const T&& get() const&& { 200 KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>()."); 201 return kj::mv(*reinterpret_cast<const T*>(space)); 202 } 203 204 template <typename T, typename... Params> 205 T& init(Params&&... params) { 206 if (tag != 0) destroy(); 207 ctor(*reinterpret_cast<T*>(space), kj::fwd<Params>(params)...); 208 tag = typeIndex<T>(); 209 return *reinterpret_cast<T*>(space); 210 } 211 212 template <typename T> 213 Maybe<T&> tryGet() { 214 if (is<T>()) { 215 return *reinterpret_cast<T*>(space); 216 } else { 217 return nullptr; 218 } 219 } 220 template <typename T> 221 Maybe<const T&> tryGet() const { 222 if (is<T>()) { 223 return *reinterpret_cast<const T*>(space); 224 } else { 225 return nullptr; 226 } 227 } 228 229 template <uint i> 230 KJ_NORETURN(void allHandled()); 231 // After a series of if/else blocks handling each variant of the OneOf, have the final else 232 // block call allHandled<n>() where n is the number of variants. This will fail to compile 233 // if new variants are added in the future. 234 235 typedef _::Variants<sizeof...(Variants)> Tag; 236 237 Tag which() const { 238 KJ_IREQUIRE(tag != 0, "Can't KJ_SWITCH_ONEOF() on uninitialized value."); 239 return static_cast<Tag>(tag - 1); 240 } 241 242 template <typename T> 243 static constexpr Tag tagFor() { 244 return static_cast<Tag>(typeIndex<T>() - 1); 245 } 246 247 OneOf* _switchSubject() & { return this; } 248 const OneOf* _switchSubject() const& { return this; } 249 _::NullableValue<OneOf> _switchSubject() && { return kj::mv(*this); } 250 251 private: 252 uint tag; 253 254 static inline constexpr size_t maxSize(size_t a) { 255 return a; 256 } 257 template <typename... Rest> 258 static inline constexpr size_t maxSize(size_t a, size_t b, Rest... rest) { 259 return maxSize(kj::max(a, b), rest...); 260 } 261 // Returns the maximum of all the parameters. 262 // TODO(someday): Generalize the above template and make it common. I tried, but C++ decided to 263 // be difficult so I cut my losses. 264 265 static constexpr auto spaceSize = maxSize(sizeof(Variants)...); 266 // TODO(msvc): This constant could just as well go directly inside space's bracket's, where it's 267 // used, but MSVC suffers a parse error on `...`. 268 269 union { 270 byte space[spaceSize]; 271 272 void* forceAligned; 273 // TODO(someday): Use C++11 alignas() once we require GCC 4.8 / Clang 3.3. 274 }; 275 276 template <typename... T> 277 inline void doAll(T... t) {} 278 279 template <typename T> 280 inline bool destroyVariant() { 281 if (tag == typeIndex<T>()) { 282 tag = 0; 283 dtor(*reinterpret_cast<T*>(space)); 284 } 285 return false; 286 } 287 void destroy() { 288 doAll(destroyVariant<Variants>()...); 289 } 290 291 template <typename T> 292 inline bool copyVariantFrom(const OneOf& other) { 293 if (other.is<T>()) { 294 ctor(*reinterpret_cast<T*>(space), other.get<T>()); 295 } 296 return false; 297 } 298 void copyFrom(const OneOf& other) { 299 // Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag 300 // is invalid. 301 tag = other.tag; 302 doAll(copyVariantFrom<Variants>(other)...); 303 } 304 305 template <typename T> 306 inline bool copyVariantFrom(OneOf& other) { 307 if (other.is<T>()) { 308 ctor(*reinterpret_cast<T*>(space), other.get<T>()); 309 } 310 return false; 311 } 312 void copyFrom(OneOf& other) { 313 // Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag 314 // is invalid. 315 tag = other.tag; 316 doAll(copyVariantFrom<Variants>(other)...); 317 } 318 319 template <typename T> 320 inline bool moveVariantFrom(OneOf& other) { 321 if (other.is<T>()) { 322 ctor(*reinterpret_cast<T*>(space), kj::mv(other.get<T>())); 323 } 324 return false; 325 } 326 void moveFrom(OneOf& other) { 327 // Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag 328 // is invalid. 329 tag = other.tag; 330 doAll(moveVariantFrom<Variants>(other)...); 331 } 332 333 template <typename T, typename... OtherVariants> 334 inline bool copySubsetVariantFrom(const OneOf<OtherVariants...>& other) { 335 if (other.template is<T>()) { 336 tag = typeIndex<Decay<T>>(); 337 ctor(*reinterpret_cast<T*>(space), other.template get<T>()); 338 } 339 return false; 340 } 341 template <typename... OtherVariants> 342 void copyFromSubset(const OneOf<OtherVariants...>& other) { 343 doAll(copySubsetVariantFrom<OtherVariants>(other)...); 344 } 345 346 template <typename T, typename... OtherVariants> 347 inline bool copySubsetVariantFrom(OneOf<OtherVariants...>& other) { 348 if (other.template is<T>()) { 349 tag = typeIndex<Decay<T>>(); 350 ctor(*reinterpret_cast<T*>(space), other.template get<T>()); 351 } 352 return false; 353 } 354 template <typename... OtherVariants> 355 void copyFromSubset(OneOf<OtherVariants...>& other) { 356 doAll(copySubsetVariantFrom<OtherVariants>(other)...); 357 } 358 359 template <typename T, typename... OtherVariants> 360 inline bool moveSubsetVariantFrom(OneOf<OtherVariants...>& other) { 361 if (other.template is<T>()) { 362 tag = typeIndex<Decay<T>>(); 363 ctor(*reinterpret_cast<T*>(space), kj::mv(other.template get<T>())); 364 } 365 return false; 366 } 367 template <typename... OtherVariants> 368 void moveFromSubset(OneOf<OtherVariants...>& other) { 369 doAll(moveSubsetVariantFrom<OtherVariants>(other)...); 370 } 371 }; 372 373 template <typename... Variants> 374 template <uint i, typename First, typename... Rest> 375 struct OneOf<Variants...>::HasAll<i, First, Rest...> 376 : public HasAll<typeIndexOrZero<First>(), Rest...> {}; 377 template <typename... Variants> 378 template <uint i> 379 struct OneOf<Variants...>::HasAll<i>: public _::SuccessIfNotZero<i> {}; 380 381 template <typename... Variants> 382 template <uint i> 383 void OneOf<Variants...>::allHandled() { 384 // After a series of if/else blocks handling each variant of the OneOf, have the final else 385 // block call allHandled<n>() where n is the number of variants. This will fail to compile 386 // if new variants are added in the future. 387 388 static_assert(i == sizeof...(Variants), "new OneOf variants need to be handled here"); 389 KJ_UNREACHABLE; 390 } 391 392 #if __cplusplus > 201402L 393 #define KJ_SWITCH_ONEOF(value) \ 394 switch (auto _kj_switch_subject = (value)._switchSubject(); _kj_switch_subject->which()) 395 #else 396 #define KJ_SWITCH_ONEOF(value) \ 397 /* Without C++17, we can only support one switch per containing block. Deal with it. */ \ 398 auto _kj_switch_subject = (value)._switchSubject(); \ 399 switch (_kj_switch_subject->which()) 400 #endif 401 #if !_MSC_VER || defined(__clang__) 402 #define KJ_CASE_ONEOF(name, ...) \ 403 break; \ 404 case ::kj::Decay<decltype(*_kj_switch_subject)>::template tagFor<__VA_ARGS__>(): \ 405 for (auto& name = _kj_switch_subject->template get<__VA_ARGS__>(), *_kj_switch_done = &name; \ 406 _kj_switch_done; _kj_switch_done = nullptr) 407 #else 408 // TODO(msvc): The latest MSVC which ships with VS2019 now ICEs on the implementation above. It 409 // appears we can hack around the problem by moving the `->template get<>()` syntax to an outer 410 // `if`. (This unfortunately allows wonky syntax like `KJ_CASE_ONEOF(a, B) { } else { }`.) 411 // https://developercommunity.visualstudio.com/content/problem/1143733/internal-compiler-error-on-v1670.html 412 #define KJ_CASE_ONEOF(name, ...) \ 413 break; \ 414 case ::kj::Decay<decltype(*_kj_switch_subject)>::template tagFor<__VA_ARGS__>(): \ 415 if (auto* _kj_switch_done = &_kj_switch_subject->template get<__VA_ARGS__>()) \ 416 for (auto& name = *_kj_switch_done; _kj_switch_done; _kj_switch_done = nullptr) 417 #endif 418 #define KJ_CASE_ONEOF_DEFAULT break; default: 419 // Allows switching over a OneOf. 420 // 421 // Example: 422 // 423 // kj::OneOf<int, float, const char*> variant; 424 // KJ_SWITCH_ONEOF(variant) { 425 // KJ_CASE_ONEOF(i, int) { 426 // doSomethingWithInt(i); 427 // } 428 // KJ_CASE_ONEOF(s, const char*) { 429 // doSomethingWithString(s); 430 // } 431 // KJ_CASE_ONEOF_DEFAULT { 432 // doSomethingElse(); 433 // } 434 // } 435 // 436 // Notes: 437 // - If you don't handle all possible types and don't include a default branch, you'll get a 438 // compiler warning, just like a regular switch() over an enum where one of the enum values is 439 // missing. 440 // - There's no need for a `break` statement in a KJ_CASE_ONEOF; it is implied. 441 // - Under C++11 and C++14, only one KJ_SWITCH_ONEOF() can appear in a block. Wrap the switch in 442 // a pair of braces if you need a second switch in the same block. If C++17 is enabled, this is 443 // not an issue. 444 // 445 // Implementation notes: 446 // - The use of __VA_ARGS__ is to account for template types that have commas separating type 447 // parameters, since macros don't recognize <> as grouping. 448 // - _kj_switch_done is really used as a boolean flag to prevent the for() loop from actually 449 // looping, but it's defined as a pointer since that's all we can define in this context. 450 451 } // namespace kj 452 453 KJ_END_HEADER