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 "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
static constexpr size_t StorageAlignment =
43
tl::Max<alignof(T1), alignof(T2)>::value;
44
static constexpr size_t StorageSize = tl::Max<sizeof(T1), sizeof(T2)>::value;
45
46
alignas(StorageAlignment) unsigned char storage[StorageSize];
47
48
// GCC fails due to -Werror=strict-aliasing if |storage| is directly cast to
49
// T*. Indirecting through these functions addresses the problem.
50
void* data() { return storage; }
51
const void* data() const { return storage; }
52
53
enum State { None, SomeT1, SomeT2 } state;
54
template <class T, class Ignored = void>
55
struct Type2State {};
56
57
template <class T>
58
T& as() {
59
MOZ_ASSERT(state == Type2State<T>::result);
60
return *static_cast<T*>(data());
61
}
62
63
template <class T>
64
const T& as() const {
65
MOZ_ASSERT(state == Type2State<T>::result);
66
return *static_cast<const T*>(data());
67
}
68
69
public:
70
MaybeOneOf() : state(None) {}
71
~MaybeOneOf() { destroyIfConstructed(); }
72
73
MaybeOneOf(MaybeOneOf&& rhs) : state(None) {
74
if (!rhs.empty()) {
75
if (rhs.constructed<T1>()) {
76
construct<T1>(std::move(rhs.as<T1>()));
77
rhs.as<T1>().~T1();
78
} else {
79
construct<T2>(std::move(rhs.as<T2>()));
80
rhs.as<T2>().~T2();
81
}
82
rhs.state = None;
83
}
84
}
85
86
MaybeOneOf& operator=(MaybeOneOf&& rhs) {
87
MOZ_ASSERT(this != &rhs, "Self-move is prohibited");
88
this->~MaybeOneOf();
89
new (this) MaybeOneOf(std::move(rhs));
90
return *this;
91
}
92
93
bool empty() const { return state == None; }
94
95
template <class T>
96
bool constructed() const {
97
return state == Type2State<T>::result;
98
}
99
100
template <class T, class... Args>
101
void construct(Args&&... aArgs) {
102
MOZ_ASSERT(state == None);
103
state = Type2State<T>::result;
104
::new (KnownNotNull, data()) T(std::forward<Args>(aArgs)...);
105
}
106
107
template <class T>
108
T& ref() {
109
return as<T>();
110
}
111
112
template <class T>
113
const T& ref() const {
114
return as<T>();
115
}
116
117
void destroy() {
118
MOZ_ASSERT(state == SomeT1 || state == SomeT2);
119
if (state == SomeT1) {
120
as<T1>().~T1();
121
} else if (state == SomeT2) {
122
as<T2>().~T2();
123
}
124
state = None;
125
}
126
127
void destroyIfConstructed() {
128
if (!empty()) {
129
destroy();
130
}
131
}
132
133
private:
134
MaybeOneOf(const MaybeOneOf& aOther) = delete;
135
const MaybeOneOf& operator=(const MaybeOneOf& aOther) = delete;
136
};
137
138
template <class T1, class T2>
139
template <class Ignored>
140
struct MaybeOneOf<T1, T2>::Type2State<T1, Ignored> {
141
typedef MaybeOneOf<T1, T2> Enclosing;
142
static const typename Enclosing::State result = Enclosing::SomeT1;
143
};
144
145
template <class T1, class T2>
146
template <class Ignored>
147
struct MaybeOneOf<T1, T2>::Type2State<T2, Ignored> {
148
typedef MaybeOneOf<T1, T2> Enclosing;
149
static const typename Enclosing::State result = Enclosing::SomeT2;
150
};
151
152
} // namespace mozilla
153
154
#endif /* mozilla_MaybeOneOf_h */