Line data Source code
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 : /*
8 : * A class storing one of two optional value types that supports in-place lazy
9 : * construction.
10 : */
11 :
12 : #ifndef mozilla_MaybeOneOf_h
13 : #define mozilla_MaybeOneOf_h
14 :
15 : #include "mozilla/Assertions.h"
16 : #include "mozilla/Move.h"
17 : #include "mozilla/OperatorNewExtensions.h"
18 : #include "mozilla/TemplateLib.h"
19 :
20 : #include <new> // for placement new
21 : #include <stddef.h> // for size_t
22 :
23 : namespace mozilla {
24 :
25 : /*
26 : * MaybeOneOf<T1, T2> is like Maybe, but it supports constructing either T1
27 : * or T2. When a MaybeOneOf<T1, T2> is constructed, it is |empty()|, i.e.,
28 : * no value has been constructed and no destructor will be called when the
29 : * MaybeOneOf<T1, T2> is destroyed. Upon calling |construct<T1>()| or
30 : * |construct<T2>()|, a T1 or T2 object will be constructed with the given
31 : * arguments and that object will be destroyed when the owning MaybeOneOf is
32 : * destroyed.
33 : *
34 : * Because MaybeOneOf must be aligned suitable to hold any value stored within
35 : * it, and because |alignas| requirements don't affect platform ABI with respect
36 : * to how parameters are laid out in memory, MaybeOneOf can't be used as the
37 : * type of a function parameter. Pass MaybeOneOf to functions by pointer or
38 : * reference instead.
39 : */
40 : template<class T1, class T2>
41 : class MOZ_NON_PARAM MaybeOneOf
42 : {
43 : static constexpr size_t StorageAlignment =
44 : tl::Max<alignof(T1), alignof(T2)>::value;
45 : static constexpr size_t StorageSize =
46 : tl::Max<sizeof(T1), sizeof(T2)>::value;
47 :
48 : alignas(StorageAlignment) unsigned char storage[StorageSize];
49 :
50 : // GCC fails due to -Werror=strict-aliasing if |storage| is directly cast to
51 : // T*. Indirecting through these functions addresses the problem.
52 138861 : void* data() { return storage; }
53 31854 : const void* data() const { return storage; }
54 :
55 : enum State { None, SomeT1, SomeT2 } state;
56 : template <class T, class Ignored = void> struct Type2State {};
57 :
58 : template <class T>
59 114032 : T& as()
60 : {
61 114032 : MOZ_ASSERT(state == Type2State<T>::result);
62 114032 : return *static_cast<T*>(data());
63 : }
64 :
65 : template <class T>
66 31854 : const T& as() const
67 : {
68 31854 : MOZ_ASSERT(state == Type2State<T>::result);
69 31854 : return *static_cast<const T*>(data());
70 : }
71 :
72 : public:
73 25090 : MaybeOneOf() : state(None) {}
74 24777 : ~MaybeOneOf() { destroyIfConstructed(); }
75 :
76 : MaybeOneOf(MaybeOneOf&& rhs)
77 : : state(None)
78 : {
79 : if (!rhs.empty()) {
80 : if (rhs.constructed<T1>()) {
81 : construct<T1>(Move(rhs.as<T1>()));
82 : rhs.as<T1>().~T1();
83 : } else {
84 : construct<T2>(Move(rhs.as<T2>()));
85 : rhs.as<T2>().~T2();
86 : }
87 : rhs.state = None;
88 : }
89 : }
90 :
91 : MaybeOneOf& operator=(MaybeOneOf&& rhs)
92 : {
93 : MOZ_ASSERT(this != &rhs, "Self-move is prohibited");
94 : this->~MaybeOneOf();
95 : new(this) MaybeOneOf(Move(rhs));
96 : return *this;
97 : }
98 :
99 24795 : bool empty() const { return state == None; }
100 :
101 : template <class T>
102 103093 : bool constructed() const { return state == Type2State<T>::result; }
103 :
104 : template <class T, class... Args>
105 24829 : void construct(Args&&... aArgs)
106 : {
107 24829 : MOZ_ASSERT(state == None);
108 24829 : state = Type2State<T>::result;
109 24829 : ::new (KnownNotNull, data()) T(Forward<Args>(aArgs)...);
110 24829 : }
111 :
112 : template <class T>
113 89221 : T& ref()
114 : {
115 89221 : return as<T>();
116 : }
117 :
118 : template <class T>
119 31854 : const T& ref() const
120 : {
121 31854 : return as<T>();
122 : }
123 :
124 24811 : void destroy()
125 : {
126 24811 : MOZ_ASSERT(state == SomeT1 || state == SomeT2);
127 24811 : if (state == SomeT1) {
128 24794 : as<T1>().~T1();
129 17 : } else if (state == SomeT2) {
130 17 : as<T2>().~T2();
131 : }
132 24811 : state = None;
133 24811 : }
134 :
135 24777 : void destroyIfConstructed()
136 : {
137 24777 : if (!empty()) {
138 24776 : destroy();
139 : }
140 24777 : }
141 :
142 : private:
143 : MaybeOneOf(const MaybeOneOf& aOther) = delete;
144 : const MaybeOneOf& operator=(const MaybeOneOf& aOther) = delete;
145 : };
146 :
147 : template <class T1, class T2>
148 : template <class Ignored>
149 : struct MaybeOneOf<T1, T2>::Type2State<T1, Ignored>
150 : {
151 : typedef MaybeOneOf<T1, T2> Enclosing;
152 : static const typename Enclosing::State result = Enclosing::SomeT1;
153 : };
154 :
155 : template <class T1, class T2>
156 : template <class Ignored>
157 : struct MaybeOneOf<T1, T2>::Type2State<T2, Ignored>
158 : {
159 : typedef MaybeOneOf<T1, T2> Enclosing;
160 : static const typename Enclosing::State result = Enclosing::SomeT2;
161 : };
162 :
163 : } // namespace mozilla
164 :
165 : #endif /* mozilla_MaybeOneOf_h */
|