Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(348)

Side by Side Diff: webrtc/base/optional.h

Issue 2071003003: Imported Optional from Chromium with some modifications. Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Added an empty member to the OptionalStorage union, to make the union always initializable. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | webrtc/base/optional_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be
3 * 3 // found in the LICENSE file.
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 4
11 #ifndef WEBRTC_BASE_OPTIONAL_H_ 5 #ifndef WEBRTC_BASE_OPTIONAL_H_
12 #define WEBRTC_BASE_OPTIONAL_H_ 6 #define WEBRTC_BASE_OPTIONAL_H_
13 7
14 #include <algorithm> 8 #include <type_traits>
15 #include <memory>
16 #include <utility>
17 9
18 #include "webrtc/base/checks.h" 10 #include "webrtc/base/checks.h"
11 #include "webrtc/base/template_util.h"
19 12
20 namespace rtc { 13 namespace rtc {
21 14
22 // Simple std::optional-wannabe. It either contains a T or not. 15 // Specification:
23 // 16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
24 // A moved-from Optional<T> may only be destroyed, and assigned to if T allows 17 struct in_place_t {};
25 // being assigned to after having been moved from. Specifically, you may not 18
26 // assume that it just doesn't contain a value anymore. 19 // Specification:
27 // 20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
28 // Examples of good places to use Optional: 21 struct nullopt_t {
29 // 22 constexpr explicit nullopt_t(int) {}
30 // - As a class or struct member, when the member doesn't always have a value: 23 };
31 // struct Prisoner { 24
32 // std::string name; 25 // Specification:
33 // Optional<int> cell_number; // Empty if not currently incarcerated. 26 // http://en.cppreference.com/w/cpp/utility/optional/in_place
34 // }; 27 constexpr in_place_t in_place = {};
35 // 28
36 // - As a return value for functions that may fail to return a value on all 29 // Specification:
37 // allowed inputs. For example, a function that searches an array might 30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
38 // return an Optional<size_t> (the index where it found the element, or 31 constexpr nullopt_t nullopt(0);
39 // nothing if it didn't find it); and a function that parses numbers might 32
40 // return Optional<double> (the parsed number, or nothing if parsing failed). 33 namespace internal {
41 // 34
42 // Examples of bad places to use Optional: 35 template <typename T, bool = rtc::is_trivially_destructible<T>::value>
43 // 36 struct OptionalStorage {
44 // - As a return value for functions that may fail because of disallowed 37 OptionalStorage() : empty_('\0') {};
45 // inputs. For example, a string length function should not return 38 // When T is not trivially destructible we must call its
46 // Optional<size_t> so that it can return nothing in case the caller passed 39 // destructor before deallocating its memory.
47 // it a null pointer; the function should probably use RTC_[D]CHECK instead, 40 ~OptionalStorage() {
48 // and return plain size_t. 41 if (!is_null_)
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).
58 template <typename T>
59 class Optional final {
60 public:
61 // Construct an empty Optional.
62 Optional() : has_value_(false) {}
63
64 // Construct an Optional that contains a value.
65 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 }
71
72 // 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 }
77
78 // 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 }
86
87 ~Optional() {
88 if (has_value_)
89 value_.~T(); 42 value_.~T();
90 } 43 }
91 44
92 // Copy assignment. Uses T's copy assignment if both sides have a value, T's 45 bool is_null_ = true;
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.
129 friend void swap(Optional& m1, Optional& m2) {
130 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 }
149 }
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:
190 bool has_value_; // True iff value_ contains a live value.
191 union { 46 union {
192 // By placing value_ in a union, we get to manage its construction and 47 char empty_;
193 // destruction manually: the Optional constructors won't automatically
194 // construct it, and the Optional destructor won't automatically destroy
195 // it. Basically, this just allocates a properly sized and aligned block of
196 // memory in which we can manually put a T with placement new.
197 T value_; 48 T value_;
198 }; 49 };
199 }; 50 };
200 51
52 template <typename T>
53 struct OptionalStorage<T, true> {
54 OptionalStorage() : empty_('\0') {};
55 // When T is trivially destructible (i.e. its destructor does nothing)
56 // there is no need to call it.
57 // Since |rtc::AlignedMemory| is just an array its destructor
58 // is trivial. Explicitly defaulting the destructor means it's not
59 // user-provided. All of this together make this destructor trivial.
60 ~OptionalStorage() = default;
61
62 bool is_null_ = true;
63 union {
64 char empty_;
65 T value_;
66 };
67 };
68
69 } // namespace internal
70
71 // rtc::Optional has been borrowed from Chromium's base::Optional, with some
72 // alterations. It is a version of the C++17 optional class:
73 // std::optional documentation:
74 // http://en.cppreference.com/w/cpp/utility/optional Chromium documentation:
75 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
76 //
77 // These are the differences between the specification and the implementation:
78 // - The constructor and emplace method using initializer_list are not
79 // implemented because 'initializer_list' is banned from WebRTC.
80 // - Constructors do not use 'constexpr' as it is a C++14 extension.
81 // - 'constexpr' might be missing in some places for reasons specified locally.
82 // - No exceptions are thrown, because they are banned from WebRTC.
83 // - All the non-members are in the 'rtc' namespace instead of 'std'.
84 //
85 // The WebRTC version uses this one neat union trick to implement
86 // OptionalStorage rather than using Chromium's AlignedStorage.
87 template <typename T>
88 class Optional {
89 public:
90 using value_type = T;
91
92 constexpr Optional() {}
93 Optional(rtc::nullopt_t) {}
94
95 Optional(const Optional& other) {
96 if (!other.storage_.is_null_)
97 Init(other.value());
98 }
99
100 Optional(Optional&& other) {
101 if (!other.storage_.is_null_)
102 Init(std::move(other.value()));
103 }
104
105 Optional(const T& value) { Init(value); }
106
107 Optional(T&& value) { Init(std::move(value)); }
108
109 template <class... Args>
110 explicit Optional(rtc::in_place_t, Args&&... args) {
111 emplace(std::forward<Args>(args)...);
112 }
113
114 ~Optional() = default;
115
116 Optional& operator=(rtc::nullopt_t) {
117 FreeIfNeeded();
118 return *this;
119 }
120
121 Optional& operator=(const Optional& other) {
122 if (other.storage_.is_null_) {
123 FreeIfNeeded();
124 return *this;
125 }
126
127 InitOrAssign(other.value());
128 return *this;
129 }
130
131 Optional& operator=(Optional&& other) {
132 if (other.storage_.is_null_) {
133 FreeIfNeeded();
134 return *this;
135 }
136
137 InitOrAssign(std::move(other.value()));
138 return *this;
139 }
140
141 template <class U>
142 typename std::enable_if<std::is_same<std::decay<U>, T>::value,
143 Optional&>::type
144 operator=(U&& value) {
145 InitOrAssign(std::forward<U>(value));
146 return *this;
147 }
148
149 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK.
150 const T* operator->() const {
151 RTC_DCHECK(!storage_.is_null_);
152 return &value();
153 }
154
155 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
156 // meant to be 'constexpr const'.
157 T* operator->() {
158 RTC_DCHECK(!storage_.is_null_);
159 return &value();
160 }
161
162 constexpr const T& operator*() const& { return value(); }
163
164 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
165 // meant to be 'constexpr const'.
166 T& operator*() & { return value(); }
167
168 constexpr const T&& operator*() const&& { return std::move(value()); }
169
170 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
171 // meant to be 'constexpr const'.
172 T&& operator*() && { return std::move(value()); }
173
174 constexpr explicit operator bool() const { return !storage_.is_null_; }
175
176 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
177 // meant to be 'constexpr const'.
178 T& value() & {
179 RTC_DCHECK(!storage_.is_null_);
180 return storage_.value_;
181 }
182
183 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK.
184 const T& value() const& {
185 RTC_DCHECK(!storage_.is_null_);
186 return storage_.value_;
187 }
188
189 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
190 // meant to be 'constexpr const'.
191 T&& value() && {
192 RTC_DCHECK(!storage_.is_null_);
193 return std::move(storage_.value_);
194 }
195
196 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK.
197 const T&& value() const&& {
198 RTC_DCHECK(!storage_.is_null_);
199 return std::move(storage_.value_);
200 }
201
202 template <class U>
203 constexpr T value_or(U&& default_value) const& {
204 // TODO(mlamouri): add the following assert when possible:
205 // static_assert(std::is_copy_constructible<T>::value,
206 // "T must be copy constructible");
207 static_assert(std::is_convertible<U, T>::value,
208 "U must be convertible to T");
209 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
210 : value();
211 }
212
213 template <class U>
214 T value_or(U&& default_value) && {
215 // TODO(mlamouri): add the following assert when possible:
216 // static_assert(std::is_move_constructible<T>::value,
217 // "T must be move constructible");
218 static_assert(std::is_convertible<U, T>::value,
219 "U must be convertible to T");
220 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
221 : std::move(value());
222 }
223
224 void swap(Optional& other) {
225 if (storage_.is_null_ && other.storage_.is_null_)
226 return;
227
228 if (storage_.is_null_ != other.storage_.is_null_) {
229 if (storage_.is_null_) {
230 Init(std::move(other.storage_.value_));
231 other.FreeIfNeeded();
232 } else {
233 other.Init(std::move(storage_.value_));
234 FreeIfNeeded();
235 }
236 return;
237 }
238
239 RTC_DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
240 using std::swap;
241 swap(**this, *other);
242 }
243
244 template <class... Args>
245 void emplace(Args&&... args) {
246 FreeIfNeeded();
247 Init(std::forward<Args>(args)...);
248 }
249
250 private:
251 void Init(const T& value) {
252 RTC_DCHECK(storage_.is_null_);
253 new (&storage_.value_) T(value);
254 storage_.is_null_ = false;
255 }
256
257 void Init(T&& value) {
258 RTC_DCHECK(storage_.is_null_);
259 new (&storage_.value_) T(std::move(value));
260 storage_.is_null_ = false;
261 }
262
263 template <class... Args>
264 void Init(Args&&... args) {
265 RTC_DCHECK(storage_.is_null_);
266 new (&storage_.value_) T(std::forward<Args>(args)...);
267 storage_.is_null_ = false;
268 }
269
270 void InitOrAssign(const T& value) {
271 if (storage_.is_null_)
272 Init(value);
273 else
274 storage_.value_ = value;
275 }
276
277 void InitOrAssign(T&& value) {
278 if (storage_.is_null_)
279 Init(std::move(value));
280 else
281 storage_.value_ = std::move(value);
282 }
283
284 void FreeIfNeeded() {
285 if (storage_.is_null_)
286 return;
287 storage_.value_.~T();
288 storage_.is_null_ = true;
289 }
290
291 internal::OptionalStorage<T> storage_;
292 };
293
294 template <class T>
295 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
296 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
297 }
298
299 template <class T>
300 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
301 return !(lhs == rhs);
302 }
303
304 template <class T>
305 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
306 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
307 }
308
309 template <class T>
310 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
311 return !(rhs < lhs);
312 }
313
314 template <class T>
315 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
316 return rhs < lhs;
317 }
318
319 template <class T>
320 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
321 return !(lhs < rhs);
322 }
323
324 template <class T>
325 constexpr bool operator==(const Optional<T>& opt, rtc::nullopt_t) {
326 return !opt;
327 }
328
329 template <class T>
330 constexpr bool operator==(rtc::nullopt_t, const Optional<T>& opt) {
331 return !opt;
332 }
333
334 template <class T>
335 constexpr bool operator!=(const Optional<T>& opt, rtc::nullopt_t) {
336 return !!opt;
337 }
338
339 template <class T>
340 constexpr bool operator!=(rtc::nullopt_t, const Optional<T>& opt) {
341 return !!opt;
342 }
343
344 template <class T>
345 constexpr bool operator<(const Optional<T>& opt, rtc::nullopt_t) {
346 return false;
347 }
348
349 template <class T>
350 constexpr bool operator<(rtc::nullopt_t, const Optional<T>& opt) {
351 return !!opt;
352 }
353
354 template <class T>
355 constexpr bool operator<=(const Optional<T>& opt, rtc::nullopt_t) {
356 return !opt;
357 }
358
359 template <class T>
360 constexpr bool operator<=(rtc::nullopt_t, const Optional<T>& opt) {
361 return true;
362 }
363
364 template <class T>
365 constexpr bool operator>(const Optional<T>& opt, rtc::nullopt_t) {
366 return !!opt;
367 }
368
369 template <class T>
370 constexpr bool operator>(rtc::nullopt_t, const Optional<T>& opt) {
371 return false;
372 }
373
374 template <class T>
375 constexpr bool operator>=(const Optional<T>& opt, rtc::nullopt_t) {
376 return true;
377 }
378
379 template <class T>
380 constexpr bool operator>=(rtc::nullopt_t, const Optional<T>& opt) {
381 return !opt;
382 }
383
384 template <class T>
385 constexpr bool operator==(const Optional<T>& opt, const T& value) {
386 return opt != nullopt ? *opt == value : false;
387 }
388
389 template <class T>
390 constexpr bool operator==(const T& value, const Optional<T>& opt) {
391 return opt == value;
392 }
393
394 template <class T>
395 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
396 return !(opt == value);
397 }
398
399 template <class T>
400 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
401 return !(opt == value);
402 }
403
404 template <class T>
405 constexpr bool operator<(const Optional<T>& opt, const T& value) {
406 return opt != nullopt ? *opt < value : true;
407 }
408
409 template <class T>
410 constexpr bool operator<(const T& value, const Optional<T>& opt) {
411 return opt != nullopt ? value < *opt : false;
412 }
413
414 template <class T>
415 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
416 return !(opt > value);
417 }
418
419 template <class T>
420 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
421 return !(value > opt);
422 }
423
424 template <class T>
425 constexpr bool operator>(const Optional<T>& opt, const T& value) {
426 return value < opt;
427 }
428
429 template <class T>
430 constexpr bool operator>(const T& value, const Optional<T>& opt) {
431 return opt < value;
432 }
433
434 template <class T>
435 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
436 return !(opt < value);
437 }
438
439 template <class T>
440 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
441 return !(value < opt);
442 }
443
444 template <class T>
445 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
446 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
447 }
448
449 template <class T>
450 void swap(Optional<T>& lhs, Optional<T>& rhs) {
451 lhs.swap(rhs);
452 }
453
201 } // namespace rtc 454 } // namespace rtc
202 455
456 namespace std {
457
458 template <class T>
459 struct hash<rtc::Optional<T>> {
460 size_t operator()(const rtc::Optional<T>& opt) const {
461 return opt == rtc::nullopt ? 0 : std::hash<T>()(*opt);
462 }
463 };
464
465 } // namespace std
466
203 #endif // WEBRTC_BASE_OPTIONAL_H_ 467 #endif // WEBRTC_BASE_OPTIONAL_H_
OLDNEW
« no previous file with comments | « no previous file | webrtc/base/optional_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698