1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_MaybeOneOf_h 8 #define mozilla_MaybeOneOf_h 9 10 #include "mozilla/Alignment.h" 11 #include "mozilla/Assertions.h" 12 #include "mozilla/Move.h" 13 #include "mozilla/TemplateLib.h" 14 15 #include <new> // For placement new 16 17 namespace mozilla { 18 19 /* 20 * MaybeOneOf<T1, T2> is like Maybe, but it supports constructing either T1 21 * or T2. When a MaybeOneOf<T1, T2> is constructed, it is |empty()|, i.e., 22 * no value has been constructed and no destructor will be called when the 23 * MaybeOneOf<T1, T2> is destroyed. Upon calling |construct<T1>()| or 24 * |construct<T2>()|, a T1 or T2 object will be constructed with the given 25 * arguments and that object will be destroyed when the owning MaybeOneOf is 26 * destroyed. 27 */ 28 template<class T1, class T2> 29 class MaybeOneOf 30 { 31 AlignedStorage<tl::Max<sizeof(T1), sizeof(T2)>::value> storage; 32 33 enum State { None, SomeT1, SomeT2 } state; 34 template <class T, class Ignored = void> struct Type2State {}; 35 36 template <class T> as()37 T& as() 38 { 39 MOZ_ASSERT(state == Type2State<T>::result); 40 return *(T*)storage.addr(); 41 } 42 43 template <class T> as()44 const T& as() const 45 { 46 MOZ_ASSERT(state == Type2State<T>::result); 47 return *(T*)storage.addr(); 48 } 49 50 public: MaybeOneOf()51 MaybeOneOf() : state(None) {} ~MaybeOneOf()52 ~MaybeOneOf() { destroyIfConstructed(); } 53 MaybeOneOf(MaybeOneOf && rhs)54 MaybeOneOf(MaybeOneOf&& rhs) 55 : state(None) 56 { 57 if (!rhs.empty()) { 58 if (rhs.constructed<T1>()) { 59 construct<T1>(Move(rhs.as<T1>())); 60 rhs.as<T1>().~T1(); 61 } else { 62 construct<T2>(Move(rhs.as<T2>())); 63 rhs.as<T2>().~T2(); 64 } 65 rhs.state = None; 66 } 67 } 68 69 MaybeOneOf &operator=(MaybeOneOf&& rhs) 70 { 71 MOZ_ASSERT(this != &rhs, "Self-move is prohibited"); 72 this->~MaybeOneOf(); 73 new(this) MaybeOneOf(Move(rhs)); 74 return *this; 75 } 76 empty()77 bool empty() const { return state == None; } 78 79 template <class T> constructed()80 bool constructed() const { return state == Type2State<T>::result; } 81 82 template <class T, class... Args> construct(Args &&...aArgs)83 void construct(Args&&... aArgs) 84 { 85 MOZ_ASSERT(state == None); 86 state = Type2State<T>::result; 87 ::new (storage.addr()) T(Forward<Args>(aArgs)...); 88 } 89 90 template <class T> ref()91 T& ref() 92 { 93 return as<T>(); 94 } 95 96 template <class T> ref()97 const T& ref() const 98 { 99 return as<T>(); 100 } 101 destroy()102 void destroy() 103 { 104 MOZ_ASSERT(state == SomeT1 || state == SomeT2); 105 if (state == SomeT1) { 106 as<T1>().~T1(); 107 } else if (state == SomeT2) { 108 as<T2>().~T2(); 109 } 110 state = None; 111 } 112 destroyIfConstructed()113 void destroyIfConstructed() 114 { 115 if (!empty()) { 116 destroy(); 117 } 118 } 119 120 private: 121 MaybeOneOf(const MaybeOneOf& aOther) = delete; 122 const MaybeOneOf& operator=(const MaybeOneOf& aOther) = delete; 123 }; 124 125 template <class T1, class T2> 126 template <class Ignored> 127 struct MaybeOneOf<T1, T2>::Type2State<T1, Ignored> 128 { 129 typedef MaybeOneOf<T1, T2> Enclosing; 130 static const typename Enclosing::State result = Enclosing::SomeT1; 131 }; 132 133 template <class T1, class T2> 134 template <class Ignored> 135 struct MaybeOneOf<T1, T2>::Type2State<T2, Ignored> 136 { 137 typedef MaybeOneOf<T1, T2> Enclosing; 138 static const typename Enclosing::State result = Enclosing::SomeT2; 139 }; 140 141 } // namespace mozilla 142 143 #endif /* mozilla_MaybeOneOf_h */ 144