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

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: 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') | webrtc/base/optional_unittest.cc » ('J')
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.
ossu 2016/06/17 14:51:48 I kept the original copyright message, since I don
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() {};
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
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_; 47 T value_;
198 }; 48 };
199 }; 49 };
200 50
51 template <typename T>
52 struct OptionalStorage<T, true> {
53 OptionalStorage() {};
54 // When T is trivially destructible (i.e. its destructor does nothing)
55 // there is no need to call it.
56 // Since |rtc::AlignedMemory| is just an array its destructor
57 // is trivial. Explicitly defaulting the destructor means it's not
58 // user-provided. All of this together make this destructor trivial.
59 ~OptionalStorage() = default;
60
61 bool is_null_ = true;
62 union {
63 T value_;
64 };
65 };
66
67 } // namespace internal
68
69 // rtc::Optional has been borrowed from Chromium's base::Optional, with some
70 // alterations. It is a version of the C++17 optional class:
71 // std::optional documentation:
72 // http://en.cppreference.com/w/cpp/utility/optional Chromium documentation:
73 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
74 //
75 // These are the differences between the specification and the implementation:
76 // - The constructor and emplace method using initializer_list are not
77 // implemented because 'initializer_list' is banned from WebRTC.
78 // - Constructors do not use 'constexpr' as it is a C++14 extension.
79 // - 'constexpr' might be missing in some places for reasons specified locally.
80 // - No exceptions are thrown, because they are banned from WebRTC.
81 // - All the non-members are in the 'rtc' namespace instead of 'std'.
82 //
83 // The WebRTC version uses this one neat union trick to implement
84 // OptionalStorage rather than using Chromium's AlignedStorage.
85 template <typename T>
86 class Optional {
87 public:
88 using value_type = T;
89
90 constexpr Optional() = default;
91 Optional(rtc::nullopt_t) : Optional() {}
92
93 Optional(const Optional& other) {
94 if (!other.storage_.is_null_)
95 Init(other.value());
96 }
97
98 Optional(Optional&& other) {
99 if (!other.storage_.is_null_)
100 Init(std::move(other.value()));
101 }
102
103 Optional(const T& value) { Init(value); }
104
105 Optional(T&& value) { Init(std::move(value)); }
106
107 template <class... Args>
108 explicit Optional(rtc::in_place_t, Args&&... args) {
109 emplace(std::forward<Args>(args)...);
110 }
111
112 ~Optional() = default;
113
114 Optional& operator=(rtc::nullopt_t) {
115 FreeIfNeeded();
116 return *this;
117 }
118
119 Optional& operator=(const Optional& other) {
120 if (other.storage_.is_null_) {
121 FreeIfNeeded();
122 return *this;
123 }
124
125 InitOrAssign(other.value());
126 return *this;
127 }
128
129 Optional& operator=(Optional&& other) {
130 if (other.storage_.is_null_) {
131 FreeIfNeeded();
132 return *this;
133 }
134
135 InitOrAssign(std::move(other.value()));
136 return *this;
137 }
138
139 template <class U>
140 typename std::enable_if<std::is_same<std::decay<U>, T>::value,
141 Optional&>::type
142 operator=(U&& value) {
143 InitOrAssign(std::forward<U>(value));
144 return *this;
145 }
146
147 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK.
148 const T* operator->() const {
149 RTC_DCHECK(!storage_.is_null_);
150 return &value();
151 }
152
153 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
154 // meant to be 'constexpr const'.
155 T* operator->() {
156 RTC_DCHECK(!storage_.is_null_);
157 return &value();
158 }
159
160 constexpr const T& operator*() const& { return value(); }
161
162 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
163 // meant to be 'constexpr const'.
164 T& operator*() & { return value(); }
165
166 constexpr const T&& operator*() const&& { return std::move(value()); }
167
168 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
169 // meant to be 'constexpr const'.
170 T&& operator*() && { return std::move(value()); }
171
172 constexpr explicit operator bool() const { return !storage_.is_null_; }
173
174 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
175 // meant to be 'constexpr const'.
176 T& value() & {
177 RTC_DCHECK(!storage_.is_null_);
178 return storage_.value_;
179 }
180
181 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK.
182 const T& value() const& {
183 RTC_DCHECK(!storage_.is_null_);
184 return storage_.value_;
185 }
186
187 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
188 // meant to be 'constexpr const'.
189 T&& value() && {
190 RTC_DCHECK(!storage_.is_null_);
191 return std::move(storage_.value_);
192 }
193
194 // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK.
195 const T&& value() const&& {
196 RTC_DCHECK(!storage_.is_null_);
197 return std::move(storage_.value_);
198 }
199
200 template <class U>
201 constexpr T value_or(U&& default_value) const& {
202 // TODO(mlamouri): add the following assert when possible:
203 // static_assert(std::is_copy_constructible<T>::value,
204 // "T must be copy constructible");
205 static_assert(std::is_convertible<U, T>::value,
206 "U must be convertible to T");
207 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
208 : value();
209 }
210
211 template <class U>
212 T value_or(U&& default_value) && {
213 // TODO(mlamouri): add the following assert when possible:
214 // static_assert(std::is_move_constructible<T>::value,
215 // "T must be move constructible");
216 static_assert(std::is_convertible<U, T>::value,
217 "U must be convertible to T");
218 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
219 : std::move(value());
220 }
221
222 void swap(Optional& other) {
223 if (storage_.is_null_ && other.storage_.is_null_)
224 return;
225
226 if (storage_.is_null_ != other.storage_.is_null_) {
227 if (storage_.is_null_) {
228 Init(std::move(other.storage_.value_));
229 other.FreeIfNeeded();
230 } else {
231 other.Init(std::move(storage_.value_));
232 FreeIfNeeded();
233 }
234 return;
235 }
236
237 RTC_DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
238 using std::swap;
239 swap(**this, *other);
240 }
241
242 template <class... Args>
243 void emplace(Args&&... args) {
244 FreeIfNeeded();
245 Init(std::forward<Args>(args)...);
246 }
247
248 private:
249 void Init(const T& value) {
250 RTC_DCHECK(storage_.is_null_);
251 new (&storage_.value_) T(value);
252 storage_.is_null_ = false;
253 }
254
255 void Init(T&& value) {
256 RTC_DCHECK(storage_.is_null_);
257 new (&storage_.value_) T(std::move(value));
258 storage_.is_null_ = false;
259 }
260
261 template <class... Args>
262 void Init(Args&&... args) {
263 RTC_DCHECK(storage_.is_null_);
264 new (&storage_.value_) T(std::forward<Args>(args)...);
265 storage_.is_null_ = false;
266 }
267
268 void InitOrAssign(const T& value) {
269 if (storage_.is_null_)
270 Init(value);
271 else
272 storage_.value_ = value;
273 }
274
275 void InitOrAssign(T&& value) {
276 if (storage_.is_null_)
277 Init(std::move(value));
278 else
279 storage_.value_ = std::move(value);
280 }
281
282 void FreeIfNeeded() {
283 if (storage_.is_null_)
284 return;
285 storage_.value_.~T();
286 storage_.is_null_ = true;
287 }
288
289 internal::OptionalStorage<T> storage_;
290 };
291
292 template <class T>
293 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
294 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
295 }
296
297 template <class T>
298 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
299 return !(lhs == rhs);
300 }
301
302 template <class T>
303 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
304 return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs);
305 }
306
307 template <class T>
308 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
309 return !(rhs < lhs);
310 }
311
312 template <class T>
313 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
314 return rhs < lhs;
315 }
316
317 template <class T>
318 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
319 return !(lhs < rhs);
320 }
321
322 template <class T>
323 constexpr bool operator==(const Optional<T>& opt, rtc::nullopt_t) {
324 return !opt;
325 }
326
327 template <class T>
328 constexpr bool operator==(rtc::nullopt_t, const Optional<T>& opt) {
329 return !opt;
330 }
331
332 template <class T>
333 constexpr bool operator!=(const Optional<T>& opt, rtc::nullopt_t) {
334 return !!opt;
335 }
336
337 template <class T>
338 constexpr bool operator!=(rtc::nullopt_t, const Optional<T>& opt) {
339 return !!opt;
340 }
341
342 template <class T>
343 constexpr bool operator<(const Optional<T>& opt, rtc::nullopt_t) {
344 return false;
345 }
346
347 template <class T>
348 constexpr bool operator<(rtc::nullopt_t, const Optional<T>& opt) {
349 return !!opt;
350 }
351
352 template <class T>
353 constexpr bool operator<=(const Optional<T>& opt, rtc::nullopt_t) {
354 return !opt;
355 }
356
357 template <class T>
358 constexpr bool operator<=(rtc::nullopt_t, const Optional<T>& opt) {
359 return true;
360 }
361
362 template <class T>
363 constexpr bool operator>(const Optional<T>& opt, rtc::nullopt_t) {
364 return !!opt;
365 }
366
367 template <class T>
368 constexpr bool operator>(rtc::nullopt_t, const Optional<T>& opt) {
369 return false;
370 }
371
372 template <class T>
373 constexpr bool operator>=(const Optional<T>& opt, rtc::nullopt_t) {
374 return true;
375 }
376
377 template <class T>
378 constexpr bool operator>=(rtc::nullopt_t, const Optional<T>& opt) {
379 return !opt;
380 }
381
382 template <class T>
383 constexpr bool operator==(const Optional<T>& opt, const T& value) {
384 return opt != nullopt ? *opt == value : false;
385 }
386
387 template <class T>
388 constexpr bool operator==(const T& value, const Optional<T>& opt) {
389 return opt == value;
390 }
391
392 template <class T>
393 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
394 return !(opt == value);
395 }
396
397 template <class T>
398 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
399 return !(opt == value);
400 }
401
402 template <class T>
403 constexpr bool operator<(const Optional<T>& opt, const T& value) {
404 return opt != nullopt ? *opt < value : true;
405 }
406
407 template <class T>
408 constexpr bool operator<(const T& value, const Optional<T>& opt) {
409 return opt != nullopt ? value < *opt : false;
410 }
411
412 template <class T>
413 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
414 return !(opt > value);
415 }
416
417 template <class T>
418 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
419 return !(value > opt);
420 }
421
422 template <class T>
423 constexpr bool operator>(const Optional<T>& opt, const T& value) {
424 return value < opt;
425 }
426
427 template <class T>
428 constexpr bool operator>(const T& value, const Optional<T>& opt) {
429 return opt < value;
430 }
431
432 template <class T>
433 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
434 return !(opt < value);
435 }
436
437 template <class T>
438 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
439 return !(value < opt);
440 }
441
442 template <class T>
443 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
444 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
445 }
446
447 template <class T>
448 void swap(Optional<T>& lhs, Optional<T>& rhs) {
449 lhs.swap(rhs);
450 }
451
201 } // namespace rtc 452 } // namespace rtc
202 453
454 namespace std {
455
456 template <class T>
457 struct hash<rtc::Optional<T>> {
458 size_t operator()(const rtc::Optional<T>& opt) const {
459 return opt == rtc::nullopt ? 0 : std::hash<T>()(*opt);
460 }
461 };
462
463 } // namespace std
464
203 #endif // WEBRTC_BASE_OPTIONAL_H_ 465 #endif // WEBRTC_BASE_OPTIONAL_H_
OLDNEW
« no previous file with comments | « no previous file | webrtc/base/optional_unittest.cc » ('j') | webrtc/base/optional_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698