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
/* A class for optional values and in-place lazy construction. */
8
9
#ifndef mozilla_Maybe_h
10
#define mozilla_Maybe_h
11
12
#include "mozilla/Alignment.h"
13
#include "mozilla/Assertions.h"
14
#include "mozilla/Attributes.h"
15
#include "mozilla/MemoryChecking.h"
16
#include "mozilla/Move.h"
17
#include "mozilla/OperatorNewExtensions.h"
18
#include "mozilla/Poison.h"
19
#include "mozilla/TypeTraits.h"
20
21
#include <new> // for placement new
22
#include <ostream>
23
#include <type_traits>
24
25
class nsCycleCollectionTraversalCallback;
26
27
template <typename T>
28
inline void CycleCollectionNoteChild(
29
nsCycleCollectionTraversalCallback& aCallback, T* aChild, const char* aName,
30
uint32_t aFlags);
31
32
namespace mozilla {
33
34
struct Nothing {};
35
36
namespace detail {
37
38
// You would think that poisoning Maybe instances could just be a call
39
// to mozWritePoison. Unfortunately, using a simple call to
40
// mozWritePoison generates poor code on MSVC for small structures. The
41
// generated code contains (always not-taken) branches and does a bunch
42
// of setup for `rep stos{l,q}`, even though we know at compile time
43
// exactly how many words we're poisoning. Instead, we're going to
44
// force MSVC to generate the code we want via recursive templates.
45
46
// Write the given poisonValue into p at offset*sizeof(uintptr_t).
47
template <size_t offset>
48
inline void WritePoisonAtOffset(void* p, const uintptr_t poisonValue) {
49
memcpy(static_cast<char*>(p) + offset * sizeof(poisonValue), &poisonValue,
50
sizeof(poisonValue));
51
}
52
53
template <size_t Offset, size_t NOffsets>
54
struct InlinePoisoner {
55
static void poison(void* p, const uintptr_t poisonValue) {
56
WritePoisonAtOffset<Offset>(p, poisonValue);
57
InlinePoisoner<Offset + 1, NOffsets>::poison(p, poisonValue);
58
}
59
};
60
61
template <size_t N>
62
struct InlinePoisoner<N, N> {
63
static void poison(void*, const uintptr_t) {
64
// All done!
65
}
66
};
67
68
// We can't generate inline code for large structures, though, because we'll
69
// blow out recursive template instantiation limits, and the code would be
70
// bloated to boot. So provide a fallback to the out-of-line poisoner.
71
template <size_t ObjectSize>
72
struct OutOfLinePoisoner {
73
static void poison(void* p, const uintptr_t) {
74
mozWritePoison(p, ObjectSize);
75
}
76
};
77
78
template <typename T>
79
inline void PoisonObject(T* p) {
80
const uintptr_t POISON = mozPoisonValue();
81
Conditional<(sizeof(T) <= 8 * sizeof(POISON)),
82
InlinePoisoner<0, sizeof(T) / sizeof(POISON)>,
83
OutOfLinePoisoner<sizeof(T)>>::Type::poison(p, POISON);
84
}
85
86
template <typename T>
87
struct MaybePoisoner {
88
static const size_t N = sizeof(T);
89
90
static void poison(void* aPtr) {
91
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
92
if (N >= sizeof(uintptr_t)) {
93
PoisonObject(static_cast<typename RemoveCV<T>::Type*>(aPtr));
94
}
95
#endif
96
MOZ_MAKE_MEM_UNDEFINED(aPtr, N);
97
}
98
};
99
100
} // namespace detail
101
102
/*
103
* Maybe is a container class which contains either zero or one elements. It
104
* serves two roles. It can represent values which are *semantically* optional,
105
* augmenting a type with an explicit 'Nothing' value. In this role, it provides
106
* methods that make it easy to work with values that may be missing, along with
107
* equality and comparison operators so that Maybe values can be stored in
108
* containers. Maybe values can be constructed conveniently in expressions using
109
* type inference, as follows:
110
*
111
* void doSomething(Maybe<Foo> aFoo) {
112
* if (aFoo) // Make sure that aFoo contains a value...
113
* aFoo->takeAction(); // and then use |aFoo->| to access it.
114
* } // |*aFoo| also works!
115
*
116
* doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
117
* doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
118
*
119
* You'll note that it's important to check whether a Maybe contains a value
120
* before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
121
* can avoid these checks, and sometimes write more readable code, using
122
* |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
123
* in the Maybe and provide a default for the 'Nothing' case. You can also use
124
* |apply()| to call a function only if the Maybe holds a value, and |map()| to
125
* transform the value in the Maybe, returning another Maybe with a possibly
126
* different type.
127
*
128
* Maybe's other role is to support lazily constructing objects without using
129
* dynamic storage. A Maybe directly contains storage for a value, but it's
130
* empty by default. |emplace()|, as mentioned above, can be used to construct a
131
* value in Maybe's storage. The value a Maybe contains can be destroyed by
132
* calling |reset()|; this will happen automatically if a Maybe is destroyed
133
* while holding a value.
134
*
135
* It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
136
* value meaning 'Nothing' and any other value meaning 'Some'. You can convert
137
* from such a pointer to a Maybe value using 'ToMaybe()'.
138
*
139
* Maybe is inspired by similar types in the standard library of many other
140
* languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
141
* very similar to std::optional, which was proposed for C++14 and originated in
142
* Boost. The most important differences between Maybe and std::optional are:
143
*
144
* - std::optional<T> may be compared with T. We deliberately forbid that.
145
* - std::optional allows in-place construction without a separate call to
146
* |emplace()| by using a dummy |in_place_t| value to tag the appropriate
147
* constructor.
148
* - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
149
* lacks corresponding methods for |refOr()| and |ptrOr()|.
150
* - std::optional lacks |map()| and |apply()|, making it less suitable for
151
* functional-style code.
152
* - std::optional lacks many convenience functions that Maybe has. Most
153
* unfortunately, it lacks equivalents of the type-inferred constructor
154
* functions |Some()| and |Nothing()|.
155
*/
156
template <class T>
157
class MOZ_NON_PARAM MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe {
158
MOZ_ALIGNAS_IN_STRUCT(T) unsigned char mStorage[sizeof(T)];
159
char mIsSome; // not bool -- guarantees minimal space consumption
160
161
// GCC fails due to -Werror=strict-aliasing if |mStorage| is directly cast to
162
// T*. Indirecting through these functions addresses the problem.
163
void* data() { return mStorage; }
164
const void* data() const { return mStorage; }
165
166
void poisonData() { detail::MaybePoisoner<T>::poison(data()); }
167
168
public:
169
using ValueType = T;
170
171
MOZ_ALLOW_TEMPORARY Maybe() : mIsSome(false) {}
172
~Maybe() { reset(); }
173
174
MOZ_ALLOW_TEMPORARY MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) {}
175
176
Maybe(const Maybe& aOther) : mIsSome(false) {
177
if (aOther.mIsSome) {
178
emplace(*aOther);
179
}
180
}
181
182
/**
183
* Maybe<T> can be copy-constructed from a Maybe<U> if U is convertible to T.
184
*/
185
template <typename U, typename = typename std::enable_if<
186
std::is_convertible<U, T>::value>::type>
187
MOZ_IMPLICIT Maybe(const Maybe<U>& aOther) : mIsSome(false) {
188
if (aOther.isSome()) {
189
emplace(*aOther);
190
}
191
}
192
193
Maybe(Maybe&& aOther) : mIsSome(false) {
194
if (aOther.mIsSome) {
195
emplace(std::move(*aOther));
196
aOther.reset();
197
}
198
}
199
200
/**
201
* Maybe<T> can be move-constructed from a Maybe<U> if U is convertible to T.
202
*/
203
template <typename U, typename = typename std::enable_if<
204
std::is_convertible<U, T>::value>::type>
205
MOZ_IMPLICIT Maybe(Maybe<U>&& aOther) : mIsSome(false) {
206
if (aOther.isSome()) {
207
emplace(std::move(*aOther));
208
aOther.reset();
209
}
210
}
211
212
Maybe& operator=(const Maybe& aOther) {
213
if (&aOther != this) {
214
if (aOther.mIsSome) {
215
if (mIsSome) {
216
ref() = aOther.ref();
217
} else {
218
emplace(*aOther);
219
}
220
} else {
221
reset();
222
}
223
}
224
return *this;
225
}
226
227
template <typename U, typename = typename std::enable_if<
228
std::is_convertible<U, T>::value>::type>
229
Maybe& operator=(const Maybe<U>& aOther) {
230
if (aOther.isSome()) {
231
if (mIsSome) {
232
ref() = aOther.ref();
233
} else {
234
emplace(*aOther);
235
}
236
} else {
237
reset();
238
}
239
return *this;
240
}
241
242
Maybe& operator=(Maybe&& aOther) {
243
MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
244
245
if (aOther.mIsSome) {
246
if (mIsSome) {
247
ref() = std::move(aOther.ref());
248
} else {
249
emplace(std::move(*aOther));
250
}
251
aOther.reset();
252
} else {
253
reset();
254
}
255
256
return *this;
257
}
258
259
template <typename U, typename = typename std::enable_if<
260
std::is_convertible<U, T>::value>::type>
261
Maybe& operator=(Maybe<U>&& aOther) {
262
if (aOther.isSome()) {
263
if (mIsSome) {
264
ref() = std::move(aOther.ref());
265
} else {
266
emplace(std::move(*aOther));
267
}
268
aOther.reset();
269
} else {
270
reset();
271
}
272
273
return *this;
274
}
275
276
/* Methods that check whether this Maybe contains a value */
277
explicit operator bool() const { return isSome(); }
278
bool isSome() const { return mIsSome; }
279
bool isNothing() const { return !mIsSome; }
280
281
/* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|.
282
*/
283
T value() const;
284
285
/*
286
* Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
287
* the default value provided.
288
*/
289
template <typename V>
290
T valueOr(V&& aDefault) const {
291
if (isSome()) {
292
return ref();
293
}
294
return std::forward<V>(aDefault);
295
}
296
297
/*
298
* Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
299
* the value returned from the function or functor provided.
300
*/
301
template <typename F>
302
T valueOrFrom(F&& aFunc) const {
303
if (isSome()) {
304
return ref();
305
}
306
return aFunc();
307
}
308
309
/* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|.
310
*/
311
T* ptr();
312
const T* ptr() const;
313
314
/*
315
* Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
316
* returns the default value provided.
317
*/
318
T* ptrOr(T* aDefault) {
319
if (isSome()) {
320
return ptr();
321
}
322
return aDefault;
323
}
324
325
const T* ptrOr(const T* aDefault) const {
326
if (isSome()) {
327
return ptr();
328
}
329
return aDefault;
330
}
331
332
/*
333
* Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
334
* returns the value returned from the function or functor provided.
335
*/
336
template <typename F>
337
T* ptrOrFrom(F&& aFunc) {
338
if (isSome()) {
339
return ptr();
340
}
341
return aFunc();
342
}
343
344
template <typename F>
345
const T* ptrOrFrom(F&& aFunc) const {
346
if (isSome()) {
347
return ptr();
348
}
349
return aFunc();
350
}
351
352
T* operator->();
353
const T* operator->() const;
354
355
/* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
356
T& ref();
357
const T& ref() const;
358
359
/*
360
* Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
361
* the default value provided.
362
*/
363
T& refOr(T& aDefault) {
364
if (isSome()) {
365
return ref();
366
}
367
return aDefault;
368
}
369
370
const T& refOr(const T& aDefault) const {
371
if (isSome()) {
372
return ref();
373
}
374
return aDefault;
375
}
376
377
/*
378
* Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
379
* value returned from the function or functor provided.
380
*/
381
template <typename F>
382
T& refOrFrom(F&& aFunc) {
383
if (isSome()) {
384
return ref();
385
}
386
return aFunc();
387
}
388
389
template <typename F>
390
const T& refOrFrom(F&& aFunc) const {
391
if (isSome()) {
392
return ref();
393
}
394
return aFunc();
395
}
396
397
T& operator*();
398
const T& operator*() const;
399
400
/* If |isSome()|, runs the provided function or functor on the contents of
401
* this Maybe. */
402
template <typename Func>
403
Maybe& apply(Func&& aFunc) {
404
if (isSome()) {
405
std::forward<Func>(aFunc)(ref());
406
}
407
return *this;
408
}
409
410
template <typename Func>
411
const Maybe& apply(Func&& aFunc) const {
412
if (isSome()) {
413
std::forward<Func>(aFunc)(ref());
414
}
415
return *this;
416
}
417
418
/*
419
* If |isSome()|, runs the provided function and returns the result wrapped
420
* in a Maybe. If |isNothing()|, returns an empty Maybe value with the same
421
* value type as what the provided function would have returned.
422
*/
423
template <typename Func>
424
auto map(Func&& aFunc) {
425
Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
426
if (isSome()) {
427
val.emplace(std::forward<Func>(aFunc)(ref()));
428
}
429
return val;
430
}
431
432
template <typename Func>
433
auto map(Func&& aFunc) const {
434
Maybe<decltype(std::forward<Func>(aFunc)(ref()))> val;
435
if (isSome()) {
436
val.emplace(std::forward<Func>(aFunc)(ref()));
437
}
438
return val;
439
}
440
441
/* If |isSome()|, empties this Maybe and destroys its contents. */
442
void reset() {
443
if (isSome()) {
444
ref().T::~T();
445
mIsSome = false;
446
poisonData();
447
}
448
}
449
450
/*
451
* Constructs a T value in-place in this empty Maybe<T>'s storage. The
452
* arguments to |emplace()| are the parameters to T's constructor.
453
*/
454
template <typename... Args>
455
void emplace(Args&&... aArgs);
456
457
friend std::ostream& operator<<(std::ostream& aStream,
458
const Maybe<T>& aMaybe) {
459
if (aMaybe) {
460
aStream << aMaybe.ref();
461
} else {
462
aStream << "<Nothing>";
463
}
464
return aStream;
465
}
466
};
467
468
template <typename T>
469
T Maybe<T>::value() const {
470
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
471
return ref();
472
}
473
474
template <typename T>
475
T* Maybe<T>::ptr() {
476
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
477
return &ref();
478
}
479
480
template <typename T>
481
const T* Maybe<T>::ptr() const {
482
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
483
return &ref();
484
}
485
486
template <typename T>
487
T* Maybe<T>::operator->() {
488
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
489
return ptr();
490
}
491
492
template <typename T>
493
const T* Maybe<T>::operator->() const {
494
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
495
return ptr();
496
}
497
498
template <typename T>
499
T& Maybe<T>::ref() {
500
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
501
return *static_cast<T*>(data());
502
}
503
504
template <typename T>
505
const T& Maybe<T>::ref() const {
506
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
507
return *static_cast<const T*>(data());
508
}
509
510
template <typename T>
511
T& Maybe<T>::operator*() {
512
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
513
return ref();
514
}
515
516
template <typename T>
517
const T& Maybe<T>::operator*() const {
518
MOZ_DIAGNOSTIC_ASSERT(mIsSome);
519
return ref();
520
}
521
522
template <typename T>
523
template <typename... Args>
524
void Maybe<T>::emplace(Args&&... aArgs) {
525
MOZ_DIAGNOSTIC_ASSERT(!mIsSome);
526
::new (KnownNotNull, data()) T(std::forward<Args>(aArgs)...);
527
mIsSome = true;
528
}
529
530
/*
531
* Some() creates a Maybe<T> value containing the provided T value. If T has a
532
* move constructor, it's used to make this as efficient as possible.
533
*
534
* Some() selects the type of Maybe it returns by removing any const, volatile,
535
* or reference qualifiers from the type of the value you pass to it. This gives
536
* it more intuitive behavior when used in expressions, but it also means that
537
* if you need to construct a Maybe value that holds a const, volatile, or
538
* reference value, you need to use emplace() instead.
539
*/
540
template <typename T, typename U = typename std::remove_cv<
541
typename std::remove_reference<T>::type>::type>
542
Maybe<U> Some(T&& aValue) {
543
Maybe<U> value;
544
value.emplace(std::forward<T>(aValue));
545
return value;
546
}
547
548
template <typename T>
549
Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type> ToMaybe(
550
T* aPtr) {
551
if (aPtr) {
552
return Some(*aPtr);
553
}
554
return Nothing();
555
}
556
557
/*
558
* Two Maybe<T> values are equal if
559
* - both are Nothing, or
560
* - both are Some, and the values they contain are equal.
561
*/
562
template <typename T>
563
bool operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
564
if (aLHS.isNothing() != aRHS.isNothing()) {
565
return false;
566
}
567
return aLHS.isNothing() || *aLHS == *aRHS;
568
}
569
570
template <typename T>
571
bool operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
572
return !(aLHS == aRHS);
573
}
574
575
/*
576
* We support comparison to Nothing to allow reasonable expressions like:
577
* if (maybeValue == Nothing()) { ... }
578
*/
579
template <typename T>
580
bool operator==(const Maybe<T>& aLHS, const Nothing& aRHS) {
581
return aLHS.isNothing();
582
}
583
584
template <typename T>
585
bool operator!=(const Maybe<T>& aLHS, const Nothing& aRHS) {
586
return !(aLHS == aRHS);
587
}
588
589
template <typename T>
590
bool operator==(const Nothing& aLHS, const Maybe<T>& aRHS) {
591
return aRHS.isNothing();
592
}
593
594
template <typename T>
595
bool operator!=(const Nothing& aLHS, const Maybe<T>& aRHS) {
596
return !(aLHS == aRHS);
597
}
598
599
/*
600
* Maybe<T> values are ordered in the same way T values are ordered, except that
601
* Nothing comes before anything else.
602
*/
603
template <typename T>
604
bool operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
605
if (aLHS.isNothing()) {
606
return aRHS.isSome();
607
}
608
if (aRHS.isNothing()) {
609
return false;
610
}
611
return *aLHS < *aRHS;
612
}
613
614
template <typename T>
615
bool operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
616
return !(aLHS < aRHS || aLHS == aRHS);
617
}
618
619
template <typename T>
620
bool operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
621
return aLHS < aRHS || aLHS == aRHS;
622
}
623
624
template <typename T>
625
bool operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS) {
626
return !(aLHS < aRHS);
627
}
628
629
template <typename T>
630
inline void ImplCycleCollectionTraverse(
631
nsCycleCollectionTraversalCallback& aCallback, mozilla::Maybe<T>& aField,
632
const char* aName, uint32_t aFlags = 0) {
633
if (aField) {
634
ImplCycleCollectionTraverse(aCallback, aField.ref(), aName, aFlags);
635
}
636
}
637
638
template <typename T>
639
inline void ImplCycleCollectionUnlink(mozilla::Maybe<T>& aField) {
640
if (aField) {
641
ImplCycleCollectionUnlink(aField.ref());
642
}
643
}
644
645
} // namespace mozilla
646
647
#endif /* mozilla_Maybe_h */