blob: cbed3916b876561164a766f69f121e21b719b511 [file] [log] [blame]
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#pragma once
#include "common.h"
KJ_BEGIN_HEADER
namespace kj {
namespace _ { // private
template <uint i, template<uint> class Fail, typename Key, typename... Variants>
struct TypeIndex_;
template <uint i, template<uint> class Fail, typename Key, typename First, typename... Rest>
struct TypeIndex_<i, Fail, Key, First, Rest...> {
static constexpr uint value = TypeIndex_<i + 1, Fail, Key, Rest...>::value;
};
template <uint i, template<uint> class Fail, typename Key, typename... Rest>
struct TypeIndex_<i, Fail, Key, Key, Rest...> { static constexpr uint value = i; };
template <uint i, template<uint> class Fail, typename Key>
struct TypeIndex_<i, Fail, Key>: public Fail<i> {};
template <uint i>
struct OneOfFailError_ {
static_assert(i == -1, "type does not match any in OneOf");
};
template <uint i>
struct OneOfFailZero_ {
static constexpr int value = 0;
};
template <uint i>
struct SuccessIfNotZero {
typedef int Success;
};
template <>
struct SuccessIfNotZero<0> {};
enum class Variants0 {};
enum class Variants1 { _variant0 };
enum class Variants2 { _variant0, _variant1 };
enum class Variants3 { _variant0, _variant1, _variant2 };
enum class Variants4 { _variant0, _variant1, _variant2, _variant3 };
enum class Variants5 { _variant0, _variant1, _variant2, _variant3, _variant4 };
enum class Variants6 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5 };
enum class Variants7 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6 };
enum class Variants8 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7 };
enum class Variants9 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8 };
enum class Variants10 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9 };
enum class Variants11 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10 };
enum class Variants12 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11 };
enum class Variants13 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12 };
enum class Variants14 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13 };
enum class Variants15 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13, _variant14 };
enum class Variants16 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13, _variant14, _variant15 };
enum class Variants17 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13, _variant14, _variant15, _variant16 };
enum class Variants18 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13, _variant14, _variant15, _variant16, _variant17 };
enum class Variants19 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18 };
enum class Variants20 { _variant0, _variant1, _variant2, _variant3, _variant4, _variant5, _variant6,
_variant7, _variant8, _variant9, _variant10, _variant11, _variant12,
_variant13, _variant14, _variant15, _variant16, _variant17, _variant18,
_variant19 };
template <uint i> struct Variants_;
template <> struct Variants_<0> { typedef Variants0 Type; };
template <> struct Variants_<1> { typedef Variants1 Type; };
template <> struct Variants_<2> { typedef Variants2 Type; };
template <> struct Variants_<3> { typedef Variants3 Type; };
template <> struct Variants_<4> { typedef Variants4 Type; };
template <> struct Variants_<5> { typedef Variants5 Type; };
template <> struct Variants_<6> { typedef Variants6 Type; };
template <> struct Variants_<7> { typedef Variants7 Type; };
template <> struct Variants_<8> { typedef Variants8 Type; };
template <> struct Variants_<9> { typedef Variants9 Type; };
template <> struct Variants_<10> { typedef Variants10 Type; };
template <> struct Variants_<11> { typedef Variants11 Type; };
template <> struct Variants_<12> { typedef Variants12 Type; };
template <> struct Variants_<13> { typedef Variants13 Type; };
template <> struct Variants_<14> { typedef Variants14 Type; };
template <> struct Variants_<15> { typedef Variants15 Type; };
template <> struct Variants_<16> { typedef Variants16 Type; };
template <> struct Variants_<17> { typedef Variants17 Type; };
template <> struct Variants_<18> { typedef Variants18 Type; };
template <> struct Variants_<19> { typedef Variants19 Type; };
template <> struct Variants_<20> { typedef Variants20 Type; };
template <uint i>
using Variants = typename Variants_<i>::Type;
} // namespace _ (private)
template <typename... Variants>
class OneOf {
template <typename Key>
static inline constexpr uint typeIndex() {
return _::TypeIndex_<1, _::OneOfFailError_, Key, Variants...>::value;
}
// Get the 1-based index of Key within the type list Types, or static_assert with a nice error.
template <typename Key>
static inline constexpr uint typeIndexOrZero() {
return _::TypeIndex_<1, _::OneOfFailZero_, Key, Variants...>::value;
}
template <uint i, typename... OtherVariants>
struct HasAll;
// Has a member type called "Success" if and only if all of `OtherVariants` are types that
// appear in `Variants`. Used with SFINAE to enable subset constructors.
public:
inline OneOf(): tag(0) {}
OneOf(const OneOf& other) { copyFrom(other); }
OneOf(OneOf& other) { copyFrom(other); }
OneOf(OneOf&& other) { moveFrom(other); }
// Copy/move from same OneOf type.
template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success>
OneOf(const OneOf<OtherVariants...>& other) { copyFromSubset(other); }
template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success>
OneOf(OneOf<OtherVariants...>& other) { copyFromSubset(other); }
template <typename... OtherVariants, typename = typename HasAll<1, OtherVariants...>::Success>
OneOf(OneOf<OtherVariants...>&& other) { moveFromSubset(other); }
// Copy/move from OneOf that contains a subset of the types we do.
template <typename T, typename = typename HasAll<0, Decay<T>>::Success>
OneOf(T&& other): tag(typeIndex<Decay<T>>()) {
ctor(*reinterpret_cast<Decay<T>*>(space), kj::fwd<T>(other));
}
// Copy/move from a value that matches one of the individual types in the OneOf.
~OneOf() { destroy(); }
OneOf& operator=(const OneOf& other) { if (tag != 0) destroy(); copyFrom(other); return *this; }
OneOf& operator=(OneOf&& other) { if (tag != 0) destroy(); moveFrom(other); return *this; }
inline bool operator==(decltype(nullptr)) const { return tag == 0; }
inline bool operator!=(decltype(nullptr)) const { return tag != 0; }
template <typename T>
bool is() const {
return tag == typeIndex<T>();
}
template <typename T>
T& get() & {
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
return *reinterpret_cast<T*>(space);
}
template <typename T>
T&& get() && {
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
return kj::mv(*reinterpret_cast<T*>(space));
}
template <typename T>
const T& get() const& {
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
return *reinterpret_cast<const T*>(space);
}
template <typename T>
const T&& get() const&& {
KJ_IREQUIRE(is<T>(), "Must check OneOf::is<T>() before calling get<T>().");
return kj::mv(*reinterpret_cast<const T*>(space));
}
template <typename T, typename... Params>
T& init(Params&&... params) {
if (tag != 0) destroy();
ctor(*reinterpret_cast<T*>(space), kj::fwd<Params>(params)...);
tag = typeIndex<T>();
return *reinterpret_cast<T*>(space);
}
template <typename T>
Maybe<T&> tryGet() {
if (is<T>()) {
return *reinterpret_cast<T*>(space);
} else {
return nullptr;
}
}
template <typename T>
Maybe<const T&> tryGet() const {
if (is<T>()) {
return *reinterpret_cast<const T*>(space);
} else {
return nullptr;
}
}
template <uint i>
KJ_NORETURN(void allHandled());
// After a series of if/else blocks handling each variant of the OneOf, have the final else
// block call allHandled<n>() where n is the number of variants. This will fail to compile
// if new variants are added in the future.
typedef _::Variants<sizeof...(Variants)> Tag;
Tag which() const {
KJ_IREQUIRE(tag != 0, "Can't KJ_SWITCH_ONEOF() on uninitialized value.");
return static_cast<Tag>(tag - 1);
}
template <typename T>
static constexpr Tag tagFor() {
return static_cast<Tag>(typeIndex<T>() - 1);
}
OneOf* _switchSubject() & { return this; }
const OneOf* _switchSubject() const& { return this; }
_::NullableValue<OneOf> _switchSubject() && { return kj::mv(*this); }
private:
uint tag;
static inline constexpr size_t maxSize(size_t a) {
return a;
}
template <typename... Rest>
static inline constexpr size_t maxSize(size_t a, size_t b, Rest... rest) {
return maxSize(kj::max(a, b), rest...);
}
// Returns the maximum of all the parameters.
// TODO(someday): Generalize the above template and make it common. I tried, but C++ decided to
// be difficult so I cut my losses.
static constexpr auto spaceSize = maxSize(sizeof(Variants)...);
// TODO(msvc): This constant could just as well go directly inside space's bracket's, where it's
// used, but MSVC suffers a parse error on `...`.
union {
byte space[spaceSize];
void* forceAligned;
// TODO(someday): Use C++11 alignas() once we require GCC 4.8 / Clang 3.3.
};
template <typename... T>
inline void doAll(T... t) {}
template <typename T>
inline bool destroyVariant() {
if (tag == typeIndex<T>()) {
tag = 0;
dtor(*reinterpret_cast<T*>(space));
}
return false;
}
void destroy() {
doAll(destroyVariant<Variants>()...);
}
template <typename T>
inline bool copyVariantFrom(const OneOf& other) {
if (other.is<T>()) {
ctor(*reinterpret_cast<T*>(space), other.get<T>());
}
return false;
}
void copyFrom(const OneOf& other) {
// Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
// is invalid.
tag = other.tag;
doAll(copyVariantFrom<Variants>(other)...);
}
template <typename T>
inline bool copyVariantFrom(OneOf& other) {
if (other.is<T>()) {
ctor(*reinterpret_cast<T*>(space), other.get<T>());
}
return false;
}
void copyFrom(OneOf& other) {
// Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
// is invalid.
tag = other.tag;
doAll(copyVariantFrom<Variants>(other)...);
}
template <typename T>
inline bool moveVariantFrom(OneOf& other) {
if (other.is<T>()) {
ctor(*reinterpret_cast<T*>(space), kj::mv(other.get<T>()));
}
return false;
}
void moveFrom(OneOf& other) {
// Initialize as a copy of `other`. Expects that `this` starts out uninitialized, so the tag
// is invalid.
tag = other.tag;
doAll(moveVariantFrom<Variants>(other)...);
}
template <typename T, typename... OtherVariants>
inline bool copySubsetVariantFrom(const OneOf<OtherVariants...>& other) {
if (other.template is<T>()) {
tag = typeIndex<Decay<T>>();
ctor(*reinterpret_cast<T*>(space), other.template get<T>());
}
return false;
}
template <typename... OtherVariants>
void copyFromSubset(const OneOf<OtherVariants...>& other) {
doAll(copySubsetVariantFrom<OtherVariants>(other)...);
}
template <typename T, typename... OtherVariants>
inline bool copySubsetVariantFrom(OneOf<OtherVariants...>& other) {
if (other.template is<T>()) {
tag = typeIndex<Decay<T>>();
ctor(*reinterpret_cast<T*>(space), other.template get<T>());
}
return false;
}
template <typename... OtherVariants>
void copyFromSubset(OneOf<OtherVariants...>& other) {
doAll(copySubsetVariantFrom<OtherVariants>(other)...);
}
template <typename T, typename... OtherVariants>
inline bool moveSubsetVariantFrom(OneOf<OtherVariants...>& other) {
if (other.template is<T>()) {
tag = typeIndex<Decay<T>>();
ctor(*reinterpret_cast<T*>(space), kj::mv(other.template get<T>()));
}
return false;
}
template <typename... OtherVariants>
void moveFromSubset(OneOf<OtherVariants...>& other) {
doAll(moveSubsetVariantFrom<OtherVariants>(other)...);
}
};
template <typename... Variants>
template <uint i, typename First, typename... Rest>
struct OneOf<Variants...>::HasAll<i, First, Rest...>
: public HasAll<typeIndexOrZero<First>(), Rest...> {};
template <typename... Variants>
template <uint i>
struct OneOf<Variants...>::HasAll<i>: public _::SuccessIfNotZero<i> {};
template <typename... Variants>
template <uint i>
void OneOf<Variants...>::allHandled() {
// After a series of if/else blocks handling each variant of the OneOf, have the final else
// block call allHandled<n>() where n is the number of variants. This will fail to compile
// if new variants are added in the future.
static_assert(i == sizeof...(Variants), "new OneOf variants need to be handled here");
KJ_UNREACHABLE;
}
#if __cplusplus > 201402L
#define KJ_SWITCH_ONEOF(value) \
switch (auto _kj_switch_subject = (value)._switchSubject(); _kj_switch_subject->which())
#else
#define KJ_SWITCH_ONEOF(value) \
/* Without C++17, we can only support one switch per containing block. Deal with it. */ \
auto _kj_switch_subject = (value)._switchSubject(); \
switch (_kj_switch_subject->which())
#endif
#if !_MSC_VER || defined(__clang__)
#define KJ_CASE_ONEOF(name, ...) \
break; \
case ::kj::Decay<decltype(*_kj_switch_subject)>::template tagFor<__VA_ARGS__>(): \
for (auto& name = _kj_switch_subject->template get<__VA_ARGS__>(), *_kj_switch_done = &name; \
_kj_switch_done; _kj_switch_done = nullptr)
#else
// TODO(msvc): The latest MSVC which ships with VS2019 now ICEs on the implementation above. It
// appears we can hack around the problem by moving the `->template get<>()` syntax to an outer
// `if`. (This unfortunately allows wonky syntax like `KJ_CASE_ONEOF(a, B) { } else { }`.)
// https://developercommunity.visualstudio.com/content/problem/1143733/internal-compiler-error-on-v1670.html
#define KJ_CASE_ONEOF(name, ...) \
break; \
case ::kj::Decay<decltype(*_kj_switch_subject)>::template tagFor<__VA_ARGS__>(): \
if (auto* _kj_switch_done = &_kj_switch_subject->template get<__VA_ARGS__>()) \
for (auto& name = *_kj_switch_done; _kj_switch_done; _kj_switch_done = nullptr)
#endif
#define KJ_CASE_ONEOF_DEFAULT break; default:
// Allows switching over a OneOf.
//
// Example:
//
// kj::OneOf<int, float, const char*> variant;
// KJ_SWITCH_ONEOF(variant) {
// KJ_CASE_ONEOF(i, int) {
// doSomethingWithInt(i);
// }
// KJ_CASE_ONEOF(s, const char*) {
// doSomethingWithString(s);
// }
// KJ_CASE_ONEOF_DEFAULT {
// doSomethingElse();
// }
// }
//
// Notes:
// - If you don't handle all possible types and don't include a default branch, you'll get a
// compiler warning, just like a regular switch() over an enum where one of the enum values is
// missing.
// - There's no need for a `break` statement in a KJ_CASE_ONEOF; it is implied.
// - Under C++11 and C++14, only one KJ_SWITCH_ONEOF() can appear in a block. Wrap the switch in
// a pair of braces if you need a second switch in the same block. If C++17 is enabled, this is
// not an issue.
//
// Implementation notes:
// - The use of __VA_ARGS__ is to account for template types that have commas separating type
// parameters, since macros don't recognize <> as grouping.
// - _kj_switch_done is really used as a boolean flag to prevent the for() loop from actually
// looping, but it's defined as a pointer since that's all we can define in this context.
} // namespace kj
KJ_END_HEADER