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