| /* -*- 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 <new> // For placement new |
| |
| namespace mozilla { |
| |
| /* |
| * MaybeOneOf<T1, T2> is like Maybe, but it supports constructing either T1 |
| * or T2. When a MaybeOneOf<T1, T2> is constructed, it is |empty()|, i.e., |
| * no value has been constructed and no destructor will be called when the |
| * MaybeOneOf<T1, T2> is destroyed. Upon calling |construct<T1>()| or |
| * |construct<T2>()|, 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 T1, class T2> |
| class MaybeOneOf |
| { |
| AlignedStorage<tl::Max<sizeof(T1), sizeof(T2)>::value> storage; |
| |
| enum State { None, SomeT1, SomeT2 } state; |
| template <class T, class Ignored = void> struct Type2State {}; |
| |
| template <class T> |
| T& as() |
| { |
| MOZ_ASSERT(state == Type2State<T>::result); |
| return *(T*)storage.addr(); |
| } |
| |
| template <class T> |
| const T& as() const |
| { |
| MOZ_ASSERT(state == Type2State<T>::result); |
| return *(T*)storage.addr(); |
| } |
| |
| public: |
| MaybeOneOf() : state(None) {} |
| ~MaybeOneOf() { destroyIfConstructed(); } |
| |
| MaybeOneOf(MaybeOneOf&& rhs) |
| : state(None) |
| { |
| if (!rhs.empty()) { |
| if (rhs.constructed<T1>()) { |
| construct<T1>(Move(rhs.as<T1>())); |
| rhs.as<T1>().~T1(); |
| } else { |
| construct<T2>(Move(rhs.as<T2>())); |
| rhs.as<T2>().~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 <class T> |
| bool constructed() const { return state == Type2State<T>::result; } |
| |
| template <class T, class... Args> |
| void construct(Args&&... aArgs) |
| { |
| MOZ_ASSERT(state == None); |
| state = Type2State<T>::result; |
| ::new (storage.addr()) T(Forward<Args>(aArgs)...); |
| } |
| |
| template <class T> |
| T& ref() |
| { |
| return as<T>(); |
| } |
| |
| template <class T> |
| const T& ref() const |
| { |
| return as<T>(); |
| } |
| |
| void destroy() |
| { |
| MOZ_ASSERT(state == SomeT1 || state == SomeT2); |
| if (state == SomeT1) { |
| as<T1>().~T1(); |
| } else if (state == SomeT2) { |
| as<T2>().~T2(); |
| } |
| state = None; |
| } |
| |
| void destroyIfConstructed() |
| { |
| if (!empty()) { |
| destroy(); |
| } |
| } |
| |
| private: |
| MaybeOneOf(const MaybeOneOf& aOther) = delete; |
| const MaybeOneOf& operator=(const MaybeOneOf& aOther) = delete; |
| }; |
| |
| template <class T1, class T2> |
| template <class Ignored> |
| struct MaybeOneOf<T1, T2>::Type2State<T1, Ignored> |
| { |
| typedef MaybeOneOf<T1, T2> Enclosing; |
| static const typename Enclosing::State result = Enclosing::SomeT1; |
| }; |
| |
| template <class T1, class T2> |
| template <class Ignored> |
| struct MaybeOneOf<T1, T2>::Type2State<T2, Ignored> |
| { |
| typedef MaybeOneOf<T1, T2> Enclosing; |
| static const typename Enclosing::State result = Enclosing::SomeT2; |
| }; |
| |
| } // namespace mozilla |
| |
| #endif /* mozilla_MaybeOneOf_h */ |