blob: e98ab2e3ce7e9765386bcb8cbb47c92f4a7b42fa [file] [log] [blame]
Karl Wibergbe579832015-11-10 22:34:18 +01001/*
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#ifndef WEBRTC_BASE_OPTIONAL_H_
12#define WEBRTC_BASE_OPTIONAL_H_
13
14#include <algorithm>
kwibergbfefb032016-05-01 14:53:46 -070015#include <memory>
Karl Wibergbe579832015-11-10 22:34:18 +010016#include <utility>
17
18#include "webrtc/base/checks.h"
19
20namespace rtc {
21
kwibergd0404802016-05-09 06:06:05 -070022// Simple std::optional-wannabe. It either contains a T or not.
Karl Wibergbe579832015-11-10 22:34:18 +010023//
24// A moved-from Optional<T> may only be destroyed, and assigned to if T allows
25// being assigned to after having been moved from. Specifically, you may not
26// assume that it just doesn't contain a value anymore.
27//
28// Examples of good places to use Optional:
29//
30// - As a class or struct member, when the member doesn't always have a value:
31// struct Prisoner {
32// std::string name;
33// Optional<int> cell_number; // Empty if not currently incarcerated.
34// };
35//
36// - As a return value for functions that may fail to return a value on all
37// allowed inputs. For example, a function that searches an array might
38// return an Optional<size_t> (the index where it found the element, or
39// nothing if it didn't find it); and a function that parses numbers might
40// return Optional<double> (the parsed number, or nothing if parsing failed).
41//
42// Examples of bad places to use Optional:
43//
44// - As a return value for functions that may fail because of disallowed
45// inputs. For example, a string length function should not return
46// Optional<size_t> so that it can return nothing in case the caller passed
47// it a null pointer; the function should probably use RTC_[D]CHECK instead,
48// and return plain size_t.
49//
50// - As a return value for functions that may fail to return a value on all
51// allowed inputs, but need to tell the caller what went wrong. Returning
52// Optional<double> when parsing a single number as in the example above
53// might make sense, but any larger parse job is probably going to need to
54// tell the caller what the problem was, not just that there was one.
55//
56// TODO(kwiberg): Get rid of this class when the standard library has
57// std::optional (and we're allowed to use it).
58template <typename T>
59class Optional final {
60 public:
61 // Construct an empty Optional.
ossuea416942016-06-30 02:14:54 -070062 Optional() : has_value_(false), empty_('\0') {}
Karl Wibergbe579832015-11-10 22:34:18 +010063
64 // Construct an Optional that contains a value.
kwibergd0404802016-05-09 06:06:05 -070065 explicit Optional(const T& value) : has_value_(true) {
66 new (&value_) T(value);
67 }
68 explicit Optional(T&& value) : has_value_(true) {
69 new (&value_) T(std::move(value));
70 }
Karl Wibergbe579832015-11-10 22:34:18 +010071
kwibergd0404802016-05-09 06:06:05 -070072 // Copy constructor: copies the value from m if it has one.
73 Optional(const Optional& m) : has_value_(m.has_value_) {
74 if (has_value_)
75 new (&value_) T(m.value_);
76 }
Karl Wibergbe579832015-11-10 22:34:18 +010077
kwibergd0404802016-05-09 06:06:05 -070078 // Move constructor: if m has a value, moves the value from m, leaving m
79 // still in a state where it has a value, but a moved-from one (the
80 // properties of which depends on T; the only general guarantee is that we
81 // can destroy m).
82 Optional(Optional&& m) : has_value_(m.has_value_) {
83 if (has_value_)
84 new (&value_) T(std::move(m.value_));
85 }
Karl Wibergbe579832015-11-10 22:34:18 +010086
kwibergd0404802016-05-09 06:06:05 -070087 ~Optional() {
88 if (has_value_)
89 value_.~T();
90 }
91
92 // Copy assignment. Uses T's copy assignment if both sides have a value, T's
93 // copy constructor if only the right-hand side has a value.
94 Optional& operator=(const Optional& m) {
95 if (m.has_value_) {
96 if (has_value_) {
97 value_ = m.value_; // T's copy assignment.
98 } else {
99 new (&value_) T(m.value_); // T's copy constructor.
100 has_value_ = true;
101 }
102 } else if (has_value_) {
103 value_.~T();
104 has_value_ = false;
105 }
106 return *this;
107 }
108
109 // Move assignment. Uses T's move assignment if both sides have a value, T's
110 // move constructor if only the right-hand side has a value. The state of m
111 // after it's been moved from is as for the move constructor.
112 Optional& operator=(Optional&& m) {
113 if (m.has_value_) {
114 if (has_value_) {
115 value_ = std::move(m.value_); // T's move assignment.
116 } else {
117 new (&value_) T(std::move(m.value_)); // T's move constructor.
118 has_value_ = true;
119 }
120 } else if (has_value_) {
121 value_.~T();
122 has_value_ = false;
123 }
124 return *this;
125 }
126
127 // Swap the values if both m1 and m2 have values; move the value if only one
128 // of them has one.
Karl Wibergbe579832015-11-10 22:34:18 +0100129 friend void swap(Optional& m1, Optional& m2) {
kwibergd0404802016-05-09 06:06:05 -0700130 if (m1.has_value_) {
131 if (m2.has_value_) {
132 // Both have values: swap.
133 using std::swap;
134 swap(m1.value_, m2.value_);
135 } else {
136 // Only m1 has a value: move it to m2.
137 new (&m2.value_) T(std::move(m1.value_));
138 m1.value_.~T(); // Destroy the moved-from value.
139 m1.has_value_ = false;
140 m2.has_value_ = true;
141 }
142 } else if (m2.has_value_) {
143 // Only m2 has a value: move it to m1.
144 new (&m1.value_) T(std::move(m2.value_));
145 m2.value_.~T(); // Destroy the moved-from value.
146 m1.has_value_ = true;
147 m2.has_value_ = false;
148 }
Karl Wibergbe579832015-11-10 22:34:18 +0100149 }
150
151 // Conversion to bool to test if we have a value.
152 explicit operator bool() const { return has_value_; }
153
154 // Dereferencing. Only allowed if we have a value.
155 const T* operator->() const {
156 RTC_DCHECK(has_value_);
157 return &value_;
158 }
159 T* operator->() {
160 RTC_DCHECK(has_value_);
161 return &value_;
162 }
163 const T& operator*() const {
164 RTC_DCHECK(has_value_);
165 return value_;
166 }
167 T& operator*() {
168 RTC_DCHECK(has_value_);
169 return value_;
170 }
171
172 // Dereference with a default value in case we don't have a value.
173 const T& value_or(const T& default_val) const {
174 return has_value_ ? value_ : default_val;
175 }
176
177 // Equality tests. Two Optionals are equal if they contain equivalent values,
178 // or
179 // if they're both empty.
180 friend bool operator==(const Optional& m1, const Optional& m2) {
181 return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_
182 : m1.has_value_ == m2.has_value_;
183 }
184 friend bool operator!=(const Optional& m1, const Optional& m2) {
185 return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_
186 : m1.has_value_ != m2.has_value_;
187 }
188
189 private:
kwibergd0404802016-05-09 06:06:05 -0700190 bool has_value_; // True iff value_ contains a live value.
191 union {
ossuea416942016-06-30 02:14:54 -0700192 // empty_ exists only to make it possible to initialize the union, even when
193 // it doesn't contain any data. If the union goes uninitialized, it may
194 // trigger compiler warnings.
195 char empty_;
kwibergd0404802016-05-09 06:06:05 -0700196 // By placing value_ in a union, we get to manage its construction and
197 // destruction manually: the Optional constructors won't automatically
198 // construct it, and the Optional destructor won't automatically destroy
199 // it. Basically, this just allocates a properly sized and aligned block of
200 // memory in which we can manually put a T with placement new.
201 T value_;
202 };
Karl Wibergbe579832015-11-10 22:34:18 +0100203};
204
205} // namespace rtc
206
207#endif // WEBRTC_BASE_OPTIONAL_H_