// SPDX-License-Identifier: MIT #ifndef RGBDS_EITHER_HPP #define RGBDS_EITHER_HPP #include #include #include "helpers.hpp" // assume template union Either { typedef T1 type1; typedef T2 type2; private: template struct Field { constexpr static unsigned tag_value = V; unsigned tag = tag_value; T value; Field() : value() {} Field(T &value_) : value(value_) {} Field(T const &value_) : value(value_) {} Field(T &&value_) : value(std::move(value_)) {} }; // The `_tag` unifies with the first `tag` member of each `struct`. constexpr static unsigned nulltag = 0; unsigned _tag = nulltag; Field _t1; Field _t2; // Value accessors; the function parameters are dummies for overload resolution. // Only used to implement `field()` below. auto &pick(T1 *) { return _t1; } auto const &pick(T1 *) const { return _t1; } auto &pick(T2 *) { return _t2; } auto const &pick(T2 *) const { return _t2; } // Generic field accessors; for internal use only. template auto &field() { return pick(static_cast(nullptr)); } template auto const &field() const { return pick(static_cast(nullptr)); } public: // Equivalent of `std::monostate` for `std::variant`s. Either() : _tag() {} // These constructors cannot be generic over the value type, because that would prevent // constructible values from being inferred, e.g. a `const char *` string literal for an // `std::string` field value. Either(T1 &value) : _t1(value) {} Either(T2 &value) : _t2(value) {} Either(T1 const &value) : _t1(value) {} Either(T2 const &value) : _t2(value) {} Either(T1 &&value) : _t1(std::move(value)) {} Either(T2 &&value) : _t2(std::move(value)) {} // Destructor manually calls the appropriate value destructor. ~Either() { if (_tag == _t1.tag_value) { _t1.value.~T1(); } else if (_tag == _t2.tag_value) { _t2.value.~T2(); } } // Copy assignment operators for each possible value. Either &operator=(T1 const &value) { _t1.tag = _t1.tag_value; new (&_t1.value) T1(value); return *this; } Either &operator=(T2 const &value) { _t2.tag = _t2.tag_value; new (&_t2.value) T2(value); return *this; } // Move assignment operators for each possible value. Either &operator=(T1 &&value) { _t1.tag = _t1.tag_value; new (&_t1.value) T1(std::move(value)); return *this; } Either &operator=(T2 &&value) { _t2.tag = _t2.tag_value; new (&_t2.value) T2(std::move(value)); return *this; } // Copy assignment operator from another `Either`. Either &operator=(Either other) { if (other._tag == other._t1.tag_value) { *this = other._t1.value; } else if (other._tag == other._t2.tag_value) { *this = other._t2.value; } else { _tag = nulltag; } return *this; } // Copy constructor from another `Either`; implemented in terms of value assignment operators. Either(Either const &other) { if (other._tag == other._t1.tag_value) { *this = other._t1.value; } else if (other._tag == other._t2.tag_value) { *this = other._t2.value; } else { _tag = nulltag; } } // Move constructor from another `Either`; implemented in terms of value assignment operators. Either(Either &&other) { if (other._tag == other._t1.tag_value) { *this = std::move(other._t1.value); } else if (other._tag == other._t2.tag_value) { *this = std::move(other._t2.value); } else { _tag = nulltag; } } // Equivalent of `.emplace()` for `std::variant`s. template void emplace(Args &&...args) { this->~Either(); if constexpr (std::is_same_v) { _t1.tag = _t1.tag_value; new (&_t1.value) T1(std::forward(args)...); } else if constexpr (std::is_same_v) { _t2.tag = _t2.tag_value; new (&_t2.value) T2(std::forward(args)...); } else { _tag = nulltag; } } // Equivalent of `std::holds_alternative()` for `std::variant`s. bool empty() const { return _tag == nulltag; } // Equivalent of `std::holds_alternative()` for `std::variant`s. template bool holds() const { if constexpr (std::is_same_v) { return _tag == _t1.tag_value; } else if constexpr (std::is_same_v) { return _tag == _t2.tag_value; } else { return false; } } // Equivalent of `std::get()` for `std::variant`s. template auto &get() { assume(holds()); return field().value; } template auto const &get() const { assume(holds()); return field().value; } }; #endif // RGBDS_EITHER_HPP