/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_MaybeOneOf_h #define mozilla_MaybeOneOf_h #include "mozilla/Alignment.h" #include "mozilla/Assertions.h" #include "mozilla/Move.h" #include "mozilla/TemplateLib.h" #include // For placement new namespace mozilla { /* * MaybeOneOf is like Maybe, but it supports constructing either T1 * or T2. When a MaybeOneOf is constructed, it is |empty()|, i.e., * no value has been constructed and no destructor will be called when the * MaybeOneOf is destroyed. Upon calling |construct()| or * |construct()|, a T1 or T2 object will be constructed with the given * arguments and that object will be destroyed when the owning MaybeOneOf is * destroyed. */ template class MaybeOneOf { AlignedStorage::value> storage; enum State { None, SomeT1, SomeT2 } state; template struct Type2State {}; template T& as() { MOZ_ASSERT(state == Type2State::result); return *(T*)storage.addr(); } template const T& as() const { MOZ_ASSERT(state == Type2State::result); return *(T*)storage.addr(); } public: MaybeOneOf() : state(None) {} ~MaybeOneOf() { destroyIfConstructed(); } MaybeOneOf(MaybeOneOf&& rhs) : state(None) { if (!rhs.empty()) { if (rhs.constructed()) { construct(Move(rhs.as())); rhs.as().~T1(); } else { construct(Move(rhs.as())); rhs.as().~T2(); } rhs.state = None; } } MaybeOneOf &operator=(MaybeOneOf&& rhs) { MOZ_ASSERT(this != &rhs, "Self-move is prohibited"); this->~MaybeOneOf(); new(this) MaybeOneOf(Move(rhs)); return *this; } bool empty() const { return state == None; } template bool constructed() const { return state == Type2State::result; } template void construct(Args&&... aArgs) { MOZ_ASSERT(state == None); state = Type2State::result; ::new (storage.addr()) T(Forward(aArgs)...); } template T& ref() { return as(); } template const T& ref() const { return as(); } void destroy() { MOZ_ASSERT(state == SomeT1 || state == SomeT2); if (state == SomeT1) { as().~T1(); } else if (state == SomeT2) { as().~T2(); } state = None; } void destroyIfConstructed() { if (!empty()) { destroy(); } } private: MaybeOneOf(const MaybeOneOf& aOther) = delete; const MaybeOneOf& operator=(const MaybeOneOf& aOther) = delete; }; template template struct MaybeOneOf::Type2State { typedef MaybeOneOf Enclosing; static const typename Enclosing::State result = Enclosing::SomeT1; }; template template struct MaybeOneOf::Type2State { typedef MaybeOneOf Enclosing; static const typename Enclosing::State result = Enclosing::SomeT2; }; } // namespace mozilla #endif /* mozilla_MaybeOneOf_h */