Index: base/optional.h |
diff --git a/base/optional.h b/base/optional.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c7eae67bf6ac7677b2600f72b6e98e6fa771b2c3 |
--- /dev/null |
+++ b/base/optional.h |
@@ -0,0 +1,421 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef BASE_OPTIONAL_H_ |
+#define BASE_OPTIONAL_H_ |
+ |
+#include "base/logging.h" |
+#include "base/memory/aligned_memory.h" |
+ |
+namespace base { |
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/experimental/optional/in_place_t |
+struct in_place_t {}; |
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/experimental/optional/nullopt_t |
+struct nullopt_t { |
+ explicit nullopt_t(int) { } |
danakj
2016/03/04 00:31:08
Can you throw a TODO on here that it should be con
danakj
2016/03/04 00:31:09
nit: no space in {}. did you git cl format?
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+}; |
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/experimental/optional/in_place |
+const in_place_t in_place{}; |
danakj
2016/03/04 00:31:09
this is brace-initialization which we don't allow
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ |
+// Specification: |
+// http://en.cppreference.com/w/cpp/experimental/optional/nullopt |
+const nullopt_t nullopt(0); |
danakj
2016/03/04 00:31:08
TODO for constexpr here too?
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ |
+// base::Optional is a Chromium version of the C++ library experimental optional |
+// class: http://en.cppreference.com/w/cpp/experimental/optional/optional |
danakj
2016/03/04 00:31:08
Link should be http://en.cppreference.com/w/cpp/ex
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+// The following known differences apply: |
+// - 'constexpr' is not used because it is currently banned from Chromium. |
+// - The constructor and emplace method using initializer_list are not |
+// implemented because 'initializer_list' is banned from Chromium. |
+// - No exceptions are thrown, because they are banned from Chromium. |
+// - Private member |val| (for exposition) is not implemented. |
danakj
2016/03/04 00:31:08
The |val| is just an example, I don't think you ne
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+// - The destructor doesn't use std::std::is_trivially_destructible. |
danakj
2016/03/04 00:31:08
can you explain why? msvc problems?
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Didn't even work locally. I've added a TODO.
|
+// - All the non-members are in the 'base' namespace instead of `std'.` |
danakj
2016/03/04 00:31:07
quotes got a little weird around "std"
mlamouri (slow - plz ping)
2016/03/18 01:16:02
Done.
|
+template <typename T> |
+class Optional { |
+ public: |
+ Optional() = default; |
+ Optional(base::nullopt_t opt) : Optional() { } |
danakj
2016/03/04 00:31:08
nit: you could leave the parameter unnamed
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done.
|
+ |
+ Optional(const Optional& other) { |
+ if (!other.is_null_) |
+ init(other.value()); |
+ } |
+ |
+ Optional(Optional&& other) { |
+ if (!other.is_null_) |
+ init(std::move(*other.buffer_.template data_as<T>())); |
danakj
2016/03/04 00:31:08
why not std::move(other.value()) ? it should cast
mlamouri (slow - plz ping)
2016/03/18 01:16:02
Hmm, I think I've implemented this method before .
|
+ } |
+ |
+ Optional(const T& value) { |
+ init(value); |
+ } |
+ |
+ Optional(T&& value) { |
+ init(std::move(value)); |
+ } |
+ |
+ template <class... Args> |
+ explicit Optional(base::in_place_t, Args&&... args) { |
+ emplace(std::forward<Args>(args)...); |
+ } |
+ |
+ ~Optional() { |
+ free_if_needed(); |
+ } |
+ |
+ Optional& operator=(base::nullopt_t opt) { |
danakj
2016/03/04 00:31:08
nit: can leave the parameter unnamed
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ free_if_needed(); |
+ return *this; |
+ } |
+ |
+ Optional& operator=(const Optional& other) { |
+ if (other.is_null_) { |
+ free_if_needed(); |
+ return *this; |
+ } |
+ |
+ init_or_assign(other.value()); |
+ return *this; |
+ } |
+ |
+ Optional& operator=(Optional&& other) { |
+ if (other.is_null_) { |
+ free_if_needed(); |
+ return *this; |
+ } |
+ |
+ init_or_assign(std::move(*other.buffer_.template data_as<T>())); |
danakj
2016/03/04 00:31:09
why not std::move(other.value()) ? it should cast
mlamouri (slow - plz ping)
2016/03/18 01:16:04
As above.
Done.
|
+ return *this; |
+ } |
+ |
+ template<class U> |
+ auto operator=(U&& value) |
+ -> typename std::enable_if |
+<std::is_same<std::decay<U>, T>::value, Optional&>::type |
danakj
2016/03/04 00:31:07
The formatting here.. is that clang-format?
Can y
mlamouri (slow - plz ping)
2016/03/18 01:16:03
No. This CL wasn't meant to be done but just for y
|
+ { |
+ init_or_assign(std::forward<U>(value)); |
+ return *this; |
+ } |
+ |
+ const T* operator->() const { |
+ DCHECK(!is_null_); |
+ return buffer_.template data_as<T>(); |
danakj
2016/03/04 00:31:07
return &value()?
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done.
|
+ } |
+ |
+ T* operator->() { |
+ DCHECK(!is_null_); |
+ return buffer_.template data_as<T>(); |
danakj
2016/03/04 00:31:07
return &value()?
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done.
|
+ } |
+ |
+ const T& operator*() const & { |
+ return value(); |
+ } |
+ |
+ T& operator*() & { |
+ return value(); |
+ } |
+ |
+ const T&& operator*() const && { |
+ return std::forward<T>(value()); |
danakj
2016/03/04 00:31:08
why forward? just std::move(value())?
optional wo
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ } |
+ |
+ T&& operator*() && { |
+ return std::forward<T>(value()); |
danakj
2016/03/04 00:31:09
std::move(value())?
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ } |
+ |
+#if 1 |
+ explicit operator bool() const { return !is_null_; } |
+#else |
+ // Allow Optional<T> to be used in boolean expressions, but not implicitely |
+ // convertible to a real bool (which is dangerous). Nor using an explicit bool |
+ // operator (which is not allowed). |
+ // |
+ // Note that this trick is only safe when the == and != operators are declared |
+ // explicitly, as otherwise "optional_1 == optional_2" will compile but do the |
+ // wrong thing (i.e., convert to Testable and then do the comparison). |
+ // |
+ // Note that this isn't following the C++ specification which requires using |
+ // an explicit bool operator. |
+ private: |
+ typedef T* Optional::*Testable; |
danakj
2016/03/04 00:31:08
using Optional::*Testable = bool?
danakj
2016/03/04 00:31:09
prefer "using" to "typedef"
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done (deprecated).
|
+ |
+ public: |
+ // TODO: make this work. |
+ operator Testable() const { |
+ return is_null_ ? nullptr : &Optional::buffer_.template data_as<T>(); |
danakj
2016/03/04 00:31:08
return is_null_ ? nullptr : &Optional::is_null_ ?
mlamouri (slow - plz ping)
2016/03/18 01:16:03
\o/
I removed the dirty workaround.
|
+ } |
+#endif |
+ |
+ T& value() & { |
+ DCHECK(!is_null_); |
+ return *buffer_.template data_as<T>(); |
+ } |
+ |
+ const T& value() const & { |
+ DCHECK(!is_null_); |
+ return *buffer_.template data_as<T>(); |
+ } |
+ |
+ T&& value() && { |
+ DCHECK(!is_null_); |
+ return std::move(*buffer_.template data_as<T>()); |
+ } |
+ |
+ const T&& value() const && { |
+ DCHECK(!is_null_); |
+ return std::move(*buffer_.template data_as<T>()); |
+ } |
+ |
+ template <class U> |
+ T value_or(U&& default_value) const { |
danakj
2016/03/04 00:31:08
this should be qualified as const&
danakj
2016/03/04 00:31:08
can you add some static asserts here:
// TODO(dan
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done.
|
+ return is_null_ ? default_value : value(); |
danakj
2016/03/04 00:31:07
i think in the null case you need to actually retu
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Oups. I completely missed that in the spec.
Done.
|
+ } |
+ |
+ template <class U> |
+ T value_or(U&& default_value) { |
danakj
2016/03/04 00:31:08
can you add some static asserts here:
// TODO(dan
danakj
2016/03/04 00:31:08
this should be qualified as &&
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done.
|
+ return is_null_ ? default_value : value(); |
danakj
2016/03/04 00:31:08
in null case: return static_cast<T>(std::forward<U
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Thanks for noticing. I missed this from the spec.
|
+ } |
+ |
+ void swap(Optional& other) { |
+ if (is_null_ && other.is_null_) |
+ return; |
+ |
+ if (is_null_ != other.is_null_) { |
+ if (is_null_) { |
+ init(std::move(*other.buffer_.template data_as<T>())); |
+ other.free_if_needed(); |
+ } else { |
+ other.init(std::move(*buffer_.template data_as<T>())); |
+ free_if_needed(); |
+ } |
+ return; |
+ } |
+ |
+ DCHECK(!is_null_ && !other.is_null_); |
+ using std::swap; |
+ swap(**this, *other); |
danakj
2016/03/04 00:31:07
I think this has to be a typo in cppreference? It
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Yes: OptionalTest.Swap_*
|
+ } |
+ |
+ template <class... Args> |
+ void emplace(Args&&... args) { |
+ free_if_needed(); |
+ init(T(std::forward<Args>(args)...)); |
danakj
2016/03/04 00:31:09
This isn't quite the same thing as constructing fr
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done... I think :)
|
+ } |
+ |
+ private: |
+ void init(const T& value) { |
danakj
2016/03/04 00:31:08
can you name these private methods in CamelCase st
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done. However, my intention was to keep a consiste
|
+ DCHECK(is_null_); |
+ new (buffer_.template data_as<T>()) T(value); |
+ is_null_ = false; |
+ } |
+ |
+ void init(T&& value) { |
+ DCHECK(is_null_); |
+ new (buffer_.template data_as<T>()) T(std::forward<T>(value)); |
danakj
2016/03/04 00:31:08
just move() here, not forward. the function is not
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ is_null_ = false; |
+ } |
+ |
+ void init_or_assign(const T& value) { |
+ if (is_null_) |
+ init(value); |
+ else |
+ *buffer_.template data_as<T>() = value; |
+ } |
+ |
+ void init_or_assign(T&& value) { |
+ if (is_null_) |
+ init(std::forward<T>(value)); |
danakj
2016/03/04 00:31:09
just std::move() here
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ else |
+ *buffer_.template data_as<T>() = std::forward<T>(value); |
danakj
2016/03/04 00:31:07
and here
mlamouri (slow - plz ping)
2016/03/18 01:16:03
Done.
|
+ } |
+ |
+ void free_if_needed() { |
+ if (is_null_) |
+ return; |
+ buffer_.template data_as<T>()->~T(); |
+ is_null_ = true; |
+ } |
+ |
+ bool is_null_ = true; |
+ base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; |
+}; |
+ |
+template <class T> |
+bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ if (bool(lhs) != bool(rhs)) |
danakj
2016/03/04 00:31:08
These are cast-styles that the google style guide
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Done.
|
+ return false; |
+ return !bool(lhs) || (*lhs == *rhs); |
+} |
+ |
+template <class T> |
+bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !(lhs == rhs); |
+} |
+ |
+template <class T> |
+bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ if (!bool(rhs)) |
+ return false; |
+ if (!bool(lhs)) |
+ return true; |
+ return *lhs < *rhs; |
+} |
+ |
+template <class T> |
+bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !(rhs < lhs); |
+} |
+ |
+template <class T> |
+bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return rhs < lhs; |
+} |
+ |
+template <class T> |
+bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) { |
+ return !(lhs < rhs); |
+} |
+ |
+template <class T> |
+bool operator==(const Optional<T>& opt, base::nullopt_t) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+bool operator==(base::nullopt_t, const Optional<T>& opt) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+bool operator!=(const Optional<T>& opt, base::nullopt_t) { |
+ return bool(opt); |
+} |
+ |
+template <class T> |
+bool operator!=(base::nullopt_t, const Optional<T>& opt) { |
+ return bool(opt); |
+} |
+ |
+template <class T> |
+bool operator<(const Optional<T>& opt, base::nullopt_t) { |
+ return false; |
+} |
+ |
+template <class T> |
+bool operator<(base::nullopt_t, const Optional<T>& opt) { |
+ return bool(opt); |
+} |
+ |
+template <class T> |
+bool operator<=(const Optional<T>& opt, base::nullopt_t) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+bool operator<=(base::nullopt_t, const Optional<T>& opt) { |
+ return true; |
+} |
+ |
+template <class T> |
+bool operator>(const Optional<T>& opt, base::nullopt_t) { |
+ return bool(opt); |
danakj
2016/03/04 00:31:08
can you write the > operators here and below in te
mlamouri (slow - plz ping)
2016/03/18 01:16:03
I could but my goal here was to follow the spec as
danakj
2016/03/21 21:49:37
I'd still prefer that you wrote them in terms of p
|
+} |
+ |
+template <class T> |
+bool operator>(base::nullopt_t, const Optional<T>& opt) { |
+ return false; |
+} |
+ |
+template <class T> |
+bool operator>=(const Optional<T>& opt, base::nullopt_t) { |
+ return true; |
+} |
+ |
+template <class T> |
+bool operator>=(base::nullopt_t, const Optional<T>& opt) { |
+ return !opt; |
+} |
+ |
+template <class T> |
+bool operator==(const Optional<T>& opt, const T& value) { |
+ return bool(opt) ? *opt == value : false; |
+} |
+ |
+template <class T> |
+bool operator==(const T& value, const Optional<T>& opt) { |
+ return bool(opt) ? value == *opt : false; |
+} |
+ |
+template <class T> |
+bool operator!=(const Optional<T>& opt, const T& value) { |
+ return bool(opt) ? *opt != value : true; |
+} |
+ |
+template <class T> |
+bool operator!=(const T& value, const Optional<T>& opt) { |
+ return bool(opt) ? value != *opt : true; |
+} |
+ |
+template <class T> |
+bool operator<(const Optional<T>& opt, const T& value) { |
+ return bool(opt) ? *opt < value : true; |
+} |
+ |
+template <class T> |
+bool operator<(const T& value, const Optional<T>& opt) { |
+ return bool(opt) ? value < *opt : false; |
+} |
+ |
+template <class T> |
+bool operator<=(const Optional<T>& opt, const T& value) { |
+ return !(opt > value); |
+} |
+ |
+template <class T> |
+bool operator<=(const T& value, const Optional<T>& opt) { |
+ return !(value > opt); |
+} |
+ |
+template <class T> |
+bool operator>(const Optional<T>& opt, const T& value) { |
+ return bool(opt) ? value < *opt : false; |
+} |
+ |
+template <class T> |
+bool operator>(const T& value, const Optional<T>& opt) { |
+ return bool(opt) ? *opt < value : true; |
danakj
2016/03/04 00:31:08
same here, write these in terms of the operator <
mlamouri (slow - plz ping)
2016/03/18 01:16:03
As above.
|
+} |
+ |
+template <class T> |
+bool operator>=(const Optional<T>& opt, const T& value) { |
+ return !(opt < value); |
+} |
+ |
+template <class T> |
+bool operator>=(const T& value, const Optional<T>& opt) { |
+ return !(value < opt); |
+} |
+ |
+template <class T> |
+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); |
+} |
+ |
+// TODO: implement hash function. |
danakj
2016/03/04 00:31:08
put a name or a bug on it
mlamouri (slow - plz ping)
2016/03/18 01:16:04
Implemented it.
|
+ |
+} // namespace base |
+ |
+#endif // BASE_OPTIONAL_H_ |