Index: webrtc/base/optional.h |
diff --git a/webrtc/base/optional.h b/webrtc/base/optional.h |
index 25cfbfe41752597d340864f3cc46ce56794763b4..fc527081f19992072b96ccb04d90922cd866b3a2 100644 |
--- a/webrtc/base/optional.h |
+++ b/webrtc/base/optional.h |
@@ -1,203 +1,465 @@ |
-/* |
- * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
- * |
- * Use of this source code is governed by a BSD-style license |
- * that can be found in the LICENSE file in the root of the source |
- * tree. An additional intellectual property rights grant can be found |
- * in the file PATENTS. All contributing project authors may |
- * be found in the AUTHORS file in the root of the source tree. |
- */ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
ossu
2016/06/17 14:51:48
I kept the original copyright message, since I don
|
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
#ifndef WEBRTC_BASE_OPTIONAL_H_ |
#define WEBRTC_BASE_OPTIONAL_H_ |
-#include <algorithm> |
-#include <memory> |
-#include <utility> |
+#include <type_traits> |
#include "webrtc/base/checks.h" |
+#include "webrtc/base/template_util.h" |
namespace rtc { |
-// Simple std::optional-wannabe. It either contains a T or not. |
-// |
-// A moved-from Optional<T> may only be destroyed, and assigned to if T allows |
-// being assigned to after having been moved from. Specifically, you may not |
-// assume that it just doesn't contain a value anymore. |
-// |
-// Examples of good places to use Optional: |
-// |
-// - As a class or struct member, when the member doesn't always have a value: |
-// struct Prisoner { |
-// std::string name; |
-// Optional<int> cell_number; // Empty if not currently incarcerated. |
-// }; |
-// |
-// - As a return value for functions that may fail to return a value on all |
-// allowed inputs. For example, a function that searches an array might |
-// return an Optional<size_t> (the index where it found the element, or |
-// nothing if it didn't find it); and a function that parses numbers might |
-// return Optional<double> (the parsed number, or nothing if parsing failed). |
-// |
-// Examples of bad places to use Optional: |
-// |
-// - As a return value for functions that may fail because of disallowed |
-// inputs. For example, a string length function should not return |
-// Optional<size_t> so that it can return nothing in case the caller passed |
-// it a null pointer; the function should probably use RTC_[D]CHECK instead, |
-// and return plain size_t. |
+// Specification: |
+// http://en.cppreference.com/w/cpp/utility/optional/in_place_t |
+struct in_place_t {}; |
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t |
+struct nullopt_t { |
+ constexpr explicit nullopt_t(int) {} |
+}; |
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/utility/optional/in_place |
+constexpr in_place_t in_place = {}; |
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/utility/optional/nullopt |
+constexpr nullopt_t nullopt(0); |
+ |
+namespace internal { |
+ |
+template <typename T, bool = rtc::is_trivially_destructible<T>::value> |
+struct OptionalStorage { |
+ OptionalStorage() {}; |
+ // When T is not trivially destructible we must call its |
+ // destructor before deallocating its memory. |
+ ~OptionalStorage() { |
+ if (!is_null_) |
+ value_.~T(); |
+ } |
+ |
+ bool is_null_ = true; |
+ union { |
+ T value_; |
+ }; |
+}; |
+ |
+template <typename T> |
+struct OptionalStorage<T, true> { |
+ OptionalStorage() {}; |
+ // When T is trivially destructible (i.e. its destructor does nothing) |
+ // there is no need to call it. |
+ // Since |rtc::AlignedMemory| is just an array its destructor |
+ // is trivial. Explicitly defaulting the destructor means it's not |
+ // user-provided. All of this together make this destructor trivial. |
+ ~OptionalStorage() = default; |
+ |
+ bool is_null_ = true; |
+ union { |
+ T value_; |
+ }; |
+}; |
+ |
+} // namespace internal |
+ |
+// rtc::Optional has been borrowed from Chromium's base::Optional, with some |
+// alterations. It is a version of the C++17 optional class: |
+// std::optional documentation: |
+// http://en.cppreference.com/w/cpp/utility/optional Chromium documentation: |
+// https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md |
// |
-// - As a return value for functions that may fail to return a value on all |
-// allowed inputs, but need to tell the caller what went wrong. Returning |
-// Optional<double> when parsing a single number as in the example above |
-// might make sense, but any larger parse job is probably going to need to |
-// tell the caller what the problem was, not just that there was one. |
+// These are the differences between the specification and the implementation: |
+// - The constructor and emplace method using initializer_list are not |
+// implemented because 'initializer_list' is banned from WebRTC. |
+// - Constructors do not use 'constexpr' as it is a C++14 extension. |
+// - 'constexpr' might be missing in some places for reasons specified locally. |
+// - No exceptions are thrown, because they are banned from WebRTC. |
+// - All the non-members are in the 'rtc' namespace instead of 'std'. |
// |
-// TODO(kwiberg): Get rid of this class when the standard library has |
-// std::optional (and we're allowed to use it). |
+// The WebRTC version uses this one neat union trick to implement |
+// OptionalStorage rather than using Chromium's AlignedStorage. |
template <typename T> |
-class Optional final { |
+class Optional { |
public: |
- // Construct an empty Optional. |
- Optional() : has_value_(false) {} |
+ using value_type = T; |
- // Construct an Optional that contains a value. |
- explicit Optional(const T& value) : has_value_(true) { |
- new (&value_) T(value); |
- } |
- explicit Optional(T&& value) : has_value_(true) { |
- new (&value_) T(std::move(value)); |
- } |
+ constexpr Optional() = default; |
+ Optional(rtc::nullopt_t) : Optional() {} |
- // Copy constructor: copies the value from m if it has one. |
- Optional(const Optional& m) : has_value_(m.has_value_) { |
- if (has_value_) |
- new (&value_) T(m.value_); |
+ Optional(const Optional& other) { |
+ if (!other.storage_.is_null_) |
+ Init(other.value()); |
} |
- // Move constructor: if m has a value, moves the value from m, leaving m |
- // still in a state where it has a value, but a moved-from one (the |
- // properties of which depends on T; the only general guarantee is that we |
- // can destroy m). |
- Optional(Optional&& m) : has_value_(m.has_value_) { |
- if (has_value_) |
- new (&value_) T(std::move(m.value_)); |
+ Optional(Optional&& other) { |
+ if (!other.storage_.is_null_) |
+ Init(std::move(other.value())); |
} |
- ~Optional() { |
- if (has_value_) |
- value_.~T(); |
+ Optional(const T& value) { Init(value); } |
+ |
+ Optional(T&& value) { Init(std::move(value)); } |
+ |
+ template <class... Args> |
+ explicit Optional(rtc::in_place_t, Args&&... args) { |
+ emplace(std::forward<Args>(args)...); |
} |
- // Copy assignment. Uses T's copy assignment if both sides have a value, T's |
- // copy constructor if only the right-hand side has a value. |
- Optional& operator=(const Optional& m) { |
- if (m.has_value_) { |
- if (has_value_) { |
- value_ = m.value_; // T's copy assignment. |
- } else { |
- new (&value_) T(m.value_); // T's copy constructor. |
- has_value_ = true; |
- } |
- } else if (has_value_) { |
- value_.~T(); |
- has_value_ = false; |
- } |
+ ~Optional() = default; |
+ |
+ Optional& operator=(rtc::nullopt_t) { |
+ FreeIfNeeded(); |
return *this; |
} |
- // Move assignment. Uses T's move assignment if both sides have a value, T's |
- // move constructor if only the right-hand side has a value. The state of m |
- // after it's been moved from is as for the move constructor. |
- Optional& operator=(Optional&& m) { |
- if (m.has_value_) { |
- if (has_value_) { |
- value_ = std::move(m.value_); // T's move assignment. |
- } else { |
- new (&value_) T(std::move(m.value_)); // T's move constructor. |
- has_value_ = true; |
- } |
- } else if (has_value_) { |
- value_.~T(); |
- has_value_ = false; |
+ Optional& operator=(const Optional& other) { |
+ if (other.storage_.is_null_) { |
+ FreeIfNeeded(); |
+ return *this; |
} |
+ |
+ InitOrAssign(other.value()); |
return *this; |
} |
- // Swap the values if both m1 and m2 have values; move the value if only one |
- // of them has one. |
- friend void swap(Optional& m1, Optional& m2) { |
- if (m1.has_value_) { |
- if (m2.has_value_) { |
- // Both have values: swap. |
- using std::swap; |
- swap(m1.value_, m2.value_); |
- } else { |
- // Only m1 has a value: move it to m2. |
- new (&m2.value_) T(std::move(m1.value_)); |
- m1.value_.~T(); // Destroy the moved-from value. |
- m1.has_value_ = false; |
- m2.has_value_ = true; |
- } |
- } else if (m2.has_value_) { |
- // Only m2 has a value: move it to m1. |
- new (&m1.value_) T(std::move(m2.value_)); |
- m2.value_.~T(); // Destroy the moved-from value. |
- m1.has_value_ = true; |
- m2.has_value_ = false; |
+ Optional& operator=(Optional&& other) { |
+ if (other.storage_.is_null_) { |
+ FreeIfNeeded(); |
+ return *this; |
} |
+ |
+ InitOrAssign(std::move(other.value())); |
+ return *this; |
} |
- // Conversion to bool to test if we have a value. |
- explicit operator bool() const { return has_value_; } |
+ template <class U> |
+ typename std::enable_if<std::is_same<std::decay<U>, T>::value, |
+ Optional&>::type |
+ operator=(U&& value) { |
+ InitOrAssign(std::forward<U>(value)); |
+ return *this; |
+ } |
- // Dereferencing. Only allowed if we have a value. |
+ // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK. |
const T* operator->() const { |
- RTC_DCHECK(has_value_); |
- return &value_; |
+ RTC_DCHECK(!storage_.is_null_); |
+ return &value(); |
} |
+ |
+ // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
+ // meant to be 'constexpr const'. |
T* operator->() { |
- RTC_DCHECK(has_value_); |
- return &value_; |
+ RTC_DCHECK(!storage_.is_null_); |
+ return &value(); |
+ } |
+ |
+ constexpr const T& operator*() const& { return value(); } |
+ |
+ // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
+ // meant to be 'constexpr const'. |
+ T& operator*() & { return value(); } |
+ |
+ constexpr const T&& operator*() const&& { return std::move(value()); } |
+ |
+ // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
+ // meant to be 'constexpr const'. |
+ T&& operator*() && { return std::move(value()); } |
+ |
+ constexpr explicit operator bool() const { return !storage_.is_null_; } |
+ |
+ // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
+ // meant to be 'constexpr const'. |
+ T& value() & { |
+ RTC_DCHECK(!storage_.is_null_); |
+ return storage_.value_; |
+ } |
+ |
+ // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK. |
+ const T& value() const& { |
+ RTC_DCHECK(!storage_.is_null_); |
+ return storage_.value_; |
+ } |
+ |
+ // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was |
+ // meant to be 'constexpr const'. |
+ T&& value() && { |
+ RTC_DCHECK(!storage_.is_null_); |
+ return std::move(storage_.value_); |
} |
- const T& operator*() const { |
- RTC_DCHECK(has_value_); |
- return value_; |
+ |
+ // TODO(mlamouri): can't use 'constexpr' with RTC_DCHECK. |
+ const T&& value() const&& { |
+ RTC_DCHECK(!storage_.is_null_); |
+ return std::move(storage_.value_); |
} |
- T& operator*() { |
- RTC_DCHECK(has_value_); |
- return value_; |
+ |
+ template <class U> |
+ constexpr T value_or(U&& default_value) const& { |
+ // TODO(mlamouri): add the following assert when possible: |
+ // static_assert(std::is_copy_constructible<T>::value, |
+ // "T must be copy constructible"); |
+ static_assert(std::is_convertible<U, T>::value, |
+ "U must be convertible to T"); |
+ return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
+ : value(); |
} |
- // Dereference with a default value in case we don't have a value. |
- const T& value_or(const T& default_val) const { |
- return has_value_ ? value_ : default_val; |
+ template <class U> |
+ T value_or(U&& default_value) && { |
+ // TODO(mlamouri): add the following assert when possible: |
+ // static_assert(std::is_move_constructible<T>::value, |
+ // "T must be move constructible"); |
+ static_assert(std::is_convertible<U, T>::value, |
+ "U must be convertible to T"); |
+ return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) |
+ : std::move(value()); |
} |
- // Equality tests. Two Optionals are equal if they contain equivalent values, |
- // or |
- // if they're both empty. |
- friend bool operator==(const Optional& m1, const Optional& m2) { |
- return m1.has_value_ && m2.has_value_ ? m1.value_ == m2.value_ |
- : m1.has_value_ == m2.has_value_; |
+ void swap(Optional& other) { |
+ if (storage_.is_null_ && other.storage_.is_null_) |
+ return; |
+ |
+ if (storage_.is_null_ != other.storage_.is_null_) { |
+ if (storage_.is_null_) { |
+ Init(std::move(other.storage_.value_)); |
+ other.FreeIfNeeded(); |
+ } else { |
+ other.Init(std::move(storage_.value_)); |
+ FreeIfNeeded(); |
+ } |
+ return; |
+ } |
+ |
+ RTC_DCHECK(!storage_.is_null_ && !other.storage_.is_null_); |
+ using std::swap; |
+ swap(**this, *other); |
} |
- friend bool operator!=(const Optional& m1, const Optional& m2) { |
- return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_ |
- : m1.has_value_ != m2.has_value_; |
+ |
+ template <class... Args> |
+ void emplace(Args&&... args) { |
+ FreeIfNeeded(); |
+ Init(std::forward<Args>(args)...); |
} |
private: |
- bool has_value_; // True iff value_ contains a live value. |
- union { |
- // By placing value_ in a union, we get to manage its construction and |
- // destruction manually: the Optional constructors won't automatically |
- // construct it, and the Optional destructor won't automatically destroy |
- // it. Basically, this just allocates a properly sized and aligned block of |
- // memory in which we can manually put a T with placement new. |
- T value_; |
- }; |
+ void Init(const T& value) { |
+ RTC_DCHECK(storage_.is_null_); |
+ new (&storage_.value_) T(value); |
+ storage_.is_null_ = false; |
+ } |
+ |
+ void Init(T&& value) { |
+ RTC_DCHECK(storage_.is_null_); |
+ new (&storage_.value_) T(std::move(value)); |
+ storage_.is_null_ = false; |
+ } |
+ |
+ template <class... Args> |
+ void Init(Args&&... args) { |
+ RTC_DCHECK(storage_.is_null_); |
+ new (&storage_.value_) T(std::forward<Args>(args)...); |
+ storage_.is_null_ = false; |
+ } |
+ |
+ void InitOrAssign(const T& value) { |
+ if (storage_.is_null_) |
+ Init(value); |
+ else |
+ storage_.value_ = value; |
+ } |
+ |
+ void InitOrAssign(T&& value) { |
+ if (storage_.is_null_) |
+ Init(std::move(value)); |
+ else |
+ storage_.value_ = std::move(value); |
+ } |
+ |
+ void FreeIfNeeded() { |
+ if (storage_.is_null_) |
+ return; |
+ storage_.value_.~T(); |
+ storage_.is_null_ = true; |
+ } |
+ |
+ internal::OptionalStorage<T> storage_; |
}; |
+template <class T> |
+constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); |
+} |
+ |
+template <class T> |
+constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !(lhs == rhs); |
+} |
+ |
+template <class T> |
+constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return rhs == nullopt ? false : (lhs == nullopt ? true : *lhs < *rhs); |
+} |
+ |
+template <class T> |
+constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !(rhs < lhs); |
+} |
+ |
+template <class T> |
+constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return rhs < lhs; |
+} |
+ |
+template <class T> |
+constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !(lhs < rhs); |
+} |
+ |
+template <class T> |
+constexpr bool operator==(const Optional<T>& opt, rtc::nullopt_t) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator==(rtc::nullopt_t, const Optional<T>& opt) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator!=(const Optional<T>& opt, rtc::nullopt_t) { |
+ return !!opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator!=(rtc::nullopt_t, const Optional<T>& opt) { |
+ return !!opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator<(const Optional<T>& opt, rtc::nullopt_t) { |
+ return false; |
+} |
+ |
+template <class T> |
+constexpr bool operator<(rtc::nullopt_t, const Optional<T>& opt) { |
+ return !!opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator<=(const Optional<T>& opt, rtc::nullopt_t) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator<=(rtc::nullopt_t, const Optional<T>& opt) { |
+ return true; |
+} |
+ |
+template <class T> |
+constexpr bool operator>(const Optional<T>& opt, rtc::nullopt_t) { |
+ return !!opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator>(rtc::nullopt_t, const Optional<T>& opt) { |
+ return false; |
+} |
+ |
+template <class T> |
+constexpr bool operator>=(const Optional<T>& opt, rtc::nullopt_t) { |
+ return true; |
+} |
+ |
+template <class T> |
+constexpr bool operator>=(rtc::nullopt_t, const Optional<T>& opt) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator==(const Optional<T>& opt, const T& value) { |
+ return opt != nullopt ? *opt == value : false; |
+} |
+ |
+template <class T> |
+constexpr bool operator==(const T& value, const Optional<T>& opt) { |
+ return opt == value; |
+} |
+ |
+template <class T> |
+constexpr bool operator!=(const Optional<T>& opt, const T& value) { |
+ return !(opt == value); |
+} |
+ |
+template <class T> |
+constexpr bool operator!=(const T& value, const Optional<T>& opt) { |
+ return !(opt == value); |
+} |
+ |
+template <class T> |
+constexpr bool operator<(const Optional<T>& opt, const T& value) { |
+ return opt != nullopt ? *opt < value : true; |
+} |
+ |
+template <class T> |
+constexpr bool operator<(const T& value, const Optional<T>& opt) { |
+ return opt != nullopt ? value < *opt : false; |
+} |
+ |
+template <class T> |
+constexpr bool operator<=(const Optional<T>& opt, const T& value) { |
+ return !(opt > value); |
+} |
+ |
+template <class T> |
+constexpr bool operator<=(const T& value, const Optional<T>& opt) { |
+ return !(value > opt); |
+} |
+ |
+template <class T> |
+constexpr bool operator>(const Optional<T>& opt, const T& value) { |
+ return value < opt; |
+} |
+ |
+template <class T> |
+constexpr bool operator>(const T& value, const Optional<T>& opt) { |
+ return opt < value; |
+} |
+ |
+template <class T> |
+constexpr bool operator>=(const Optional<T>& opt, const T& value) { |
+ return !(opt < value); |
+} |
+ |
+template <class T> |
+constexpr bool operator>=(const T& value, const Optional<T>& opt) { |
+ return !(value < opt); |
+} |
+ |
+template <class T> |
+constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) { |
+ return Optional<typename std::decay<T>::type>(std::forward<T>(value)); |
+} |
+ |
+template <class T> |
+void swap(Optional<T>& lhs, Optional<T>& rhs) { |
+ lhs.swap(rhs); |
+} |
+ |
} // namespace rtc |
+namespace std { |
+ |
+template <class T> |
+struct hash<rtc::Optional<T>> { |
+ size_t operator()(const rtc::Optional<T>& opt) const { |
+ return opt == rtc::nullopt ? 0 : std::hash<T>()(*opt); |
+ } |
+}; |
+ |
+} // namespace std |
+ |
#endif // WEBRTC_BASE_OPTIONAL_H_ |