OLD | NEW |
---|---|
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_ |
OLD | NEW |