Source code

Revision control

Other Tools

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