Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef BASE_OPTIONAL_H_ | |
| 6 #define BASE_OPTIONAL_H_ | |
| 7 | |
| 8 #include "base/logging.h" | |
| 9 #include "base/memory/aligned_memory.h" | |
| 10 | |
| 11 namespace base { | |
| 12 | |
| 13 // Specification: | |
| 14 // http://en.cppreference.com/w/cpp/experimental/optional/in_place_t | |
| 15 struct in_place_t {}; | |
| 16 | |
| 17 // Specification: | |
| 18 // http://en.cppreference.com/w/cpp/experimental/optional/nullopt_t | |
| 19 struct nullopt_t { | |
| 20 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.
| |
| 21 }; | |
| 22 | |
| 23 // Specification: | |
| 24 // http://en.cppreference.com/w/cpp/experimental/optional/in_place | |
| 25 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.
| |
| 26 | |
| 27 // Specification: | |
| 28 // http://en.cppreference.com/w/cpp/experimental/optional/nullopt | |
| 29 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.
| |
| 30 | |
| 31 // base::Optional is a Chromium version of the C++ library experimental optional | |
| 32 // 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.
| |
| 33 // The following known differences apply: | |
| 34 // - 'constexpr' is not used because it is currently banned from Chromium. | |
| 35 // - The constructor and emplace method using initializer_list are not | |
| 36 // implemented because 'initializer_list' is banned from Chromium. | |
| 37 // - No exceptions are thrown, because they are banned from Chromium. | |
| 38 // - 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.
| |
| 39 // - 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.
| |
| 40 // - 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.
| |
| 41 template <typename T> | |
| 42 class Optional { | |
| 43 public: | |
| 44 Optional() = default; | |
| 45 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.
| |
| 46 | |
| 47 Optional(const Optional& other) { | |
| 48 if (!other.is_null_) | |
| 49 init(other.value()); | |
| 50 } | |
| 51 | |
| 52 Optional(Optional&& other) { | |
| 53 if (!other.is_null_) | |
| 54 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 .
| |
| 55 } | |
| 56 | |
| 57 Optional(const T& value) { | |
| 58 init(value); | |
| 59 } | |
| 60 | |
| 61 Optional(T&& value) { | |
| 62 init(std::move(value)); | |
| 63 } | |
| 64 | |
| 65 template <class... Args> | |
| 66 explicit Optional(base::in_place_t, Args&&... args) { | |
| 67 emplace(std::forward<Args>(args)...); | |
| 68 } | |
| 69 | |
| 70 ~Optional() { | |
| 71 free_if_needed(); | |
| 72 } | |
| 73 | |
| 74 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.
| |
| 75 free_if_needed(); | |
| 76 return *this; | |
| 77 } | |
| 78 | |
| 79 Optional& operator=(const Optional& other) { | |
| 80 if (other.is_null_) { | |
| 81 free_if_needed(); | |
| 82 return *this; | |
| 83 } | |
| 84 | |
| 85 init_or_assign(other.value()); | |
| 86 return *this; | |
| 87 } | |
| 88 | |
| 89 Optional& operator=(Optional&& other) { | |
| 90 if (other.is_null_) { | |
| 91 free_if_needed(); | |
| 92 return *this; | |
| 93 } | |
| 94 | |
| 95 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.
| |
| 96 return *this; | |
| 97 } | |
| 98 | |
| 99 template<class U> | |
| 100 auto operator=(U&& value) | |
| 101 -> typename std::enable_if | |
| 102 <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
| |
| 103 { | |
| 104 init_or_assign(std::forward<U>(value)); | |
| 105 return *this; | |
| 106 } | |
| 107 | |
| 108 const T* operator->() const { | |
| 109 DCHECK(!is_null_); | |
| 110 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.
| |
| 111 } | |
| 112 | |
| 113 T* operator->() { | |
| 114 DCHECK(!is_null_); | |
| 115 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.
| |
| 116 } | |
| 117 | |
| 118 const T& operator*() const & { | |
| 119 return value(); | |
| 120 } | |
| 121 | |
| 122 T& operator*() & { | |
| 123 return value(); | |
| 124 } | |
| 125 | |
| 126 const T&& operator*() const && { | |
| 127 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.
| |
| 128 } | |
| 129 | |
| 130 T&& operator*() && { | |
| 131 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.
| |
| 132 } | |
| 133 | |
| 134 #if 1 | |
| 135 explicit operator bool() const { return !is_null_; } | |
| 136 #else | |
| 137 // Allow Optional<T> to be used in boolean expressions, but not implicitely | |
| 138 // convertible to a real bool (which is dangerous). Nor using an explicit bool | |
| 139 // operator (which is not allowed). | |
| 140 // | |
| 141 // Note that this trick is only safe when the == and != operators are declared | |
| 142 // explicitly, as otherwise "optional_1 == optional_2" will compile but do the | |
| 143 // wrong thing (i.e., convert to Testable and then do the comparison). | |
| 144 // | |
| 145 // Note that this isn't following the C++ specification which requires using | |
| 146 // an explicit bool operator. | |
| 147 private: | |
| 148 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).
| |
| 149 | |
| 150 public: | |
| 151 // TODO: make this work. | |
| 152 operator Testable() const { | |
| 153 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.
| |
| 154 } | |
| 155 #endif | |
| 156 | |
| 157 T& value() & { | |
| 158 DCHECK(!is_null_); | |
| 159 return *buffer_.template data_as<T>(); | |
| 160 } | |
| 161 | |
| 162 const T& value() const & { | |
| 163 DCHECK(!is_null_); | |
| 164 return *buffer_.template data_as<T>(); | |
| 165 } | |
| 166 | |
| 167 T&& value() && { | |
| 168 DCHECK(!is_null_); | |
| 169 return std::move(*buffer_.template data_as<T>()); | |
| 170 } | |
| 171 | |
| 172 const T&& value() const && { | |
| 173 DCHECK(!is_null_); | |
| 174 return std::move(*buffer_.template data_as<T>()); | |
| 175 } | |
| 176 | |
| 177 template <class U> | |
| 178 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.
| |
| 179 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.
| |
| 180 } | |
| 181 | |
| 182 template <class U> | |
| 183 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.
| |
| 184 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.
| |
| 185 } | |
| 186 | |
| 187 void swap(Optional& other) { | |
| 188 if (is_null_ && other.is_null_) | |
| 189 return; | |
| 190 | |
| 191 if (is_null_ != other.is_null_) { | |
| 192 if (is_null_) { | |
| 193 init(std::move(*other.buffer_.template data_as<T>())); | |
| 194 other.free_if_needed(); | |
| 195 } else { | |
| 196 other.init(std::move(*buffer_.template data_as<T>())); | |
| 197 free_if_needed(); | |
| 198 } | |
| 199 return; | |
| 200 } | |
| 201 | |
| 202 DCHECK(!is_null_ && !other.is_null_); | |
| 203 using std::swap; | |
| 204 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_*
| |
| 205 } | |
| 206 | |
| 207 template <class... Args> | |
| 208 void emplace(Args&&... args) { | |
| 209 free_if_needed(); | |
| 210 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 :)
| |
| 211 } | |
| 212 | |
| 213 private: | |
| 214 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
| |
| 215 DCHECK(is_null_); | |
| 216 new (buffer_.template data_as<T>()) T(value); | |
| 217 is_null_ = false; | |
| 218 } | |
| 219 | |
| 220 void init(T&& value) { | |
| 221 DCHECK(is_null_); | |
| 222 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.
| |
| 223 is_null_ = false; | |
| 224 } | |
| 225 | |
| 226 void init_or_assign(const T& value) { | |
| 227 if (is_null_) | |
| 228 init(value); | |
| 229 else | |
| 230 *buffer_.template data_as<T>() = value; | |
| 231 } | |
| 232 | |
| 233 void init_or_assign(T&& value) { | |
| 234 if (is_null_) | |
| 235 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.
| |
| 236 else | |
| 237 *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.
| |
| 238 } | |
| 239 | |
| 240 void free_if_needed() { | |
| 241 if (is_null_) | |
| 242 return; | |
| 243 buffer_.template data_as<T>()->~T(); | |
| 244 is_null_ = true; | |
| 245 } | |
| 246 | |
| 247 bool is_null_ = true; | |
| 248 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; | |
| 249 }; | |
| 250 | |
| 251 template <class T> | |
| 252 bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { | |
| 253 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.
| |
| 254 return false; | |
| 255 return !bool(lhs) || (*lhs == *rhs); | |
| 256 } | |
| 257 | |
| 258 template <class T> | |
| 259 bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) { | |
| 260 return !(lhs == rhs); | |
| 261 } | |
| 262 | |
| 263 template <class T> | |
| 264 bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) { | |
| 265 if (!bool(rhs)) | |
| 266 return false; | |
| 267 if (!bool(lhs)) | |
| 268 return true; | |
| 269 return *lhs < *rhs; | |
| 270 } | |
| 271 | |
| 272 template <class T> | |
| 273 bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) { | |
| 274 return !(rhs < lhs); | |
| 275 } | |
| 276 | |
| 277 template <class T> | |
| 278 bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) { | |
| 279 return rhs < lhs; | |
| 280 } | |
| 281 | |
| 282 template <class T> | |
| 283 bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) { | |
| 284 return !(lhs < rhs); | |
| 285 } | |
| 286 | |
| 287 template <class T> | |
| 288 bool operator==(const Optional<T>& opt, base::nullopt_t) { | |
| 289 return !opt; | |
| 290 } | |
| 291 | |
| 292 template <class T> | |
| 293 bool operator==(base::nullopt_t, const Optional<T>& opt) { | |
| 294 return !opt; | |
| 295 } | |
| 296 | |
| 297 template <class T> | |
| 298 bool operator!=(const Optional<T>& opt, base::nullopt_t) { | |
| 299 return bool(opt); | |
| 300 } | |
| 301 | |
| 302 template <class T> | |
| 303 bool operator!=(base::nullopt_t, const Optional<T>& opt) { | |
| 304 return bool(opt); | |
| 305 } | |
| 306 | |
| 307 template <class T> | |
| 308 bool operator<(const Optional<T>& opt, base::nullopt_t) { | |
| 309 return false; | |
| 310 } | |
| 311 | |
| 312 template <class T> | |
| 313 bool operator<(base::nullopt_t, const Optional<T>& opt) { | |
| 314 return bool(opt); | |
| 315 } | |
| 316 | |
| 317 template <class T> | |
| 318 bool operator<=(const Optional<T>& opt, base::nullopt_t) { | |
| 319 return !opt; | |
| 320 } | |
| 321 | |
| 322 template <class T> | |
| 323 bool operator<=(base::nullopt_t, const Optional<T>& opt) { | |
| 324 return true; | |
| 325 } | |
| 326 | |
| 327 template <class T> | |
| 328 bool operator>(const Optional<T>& opt, base::nullopt_t) { | |
| 329 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
| |
| 330 } | |
| 331 | |
| 332 template <class T> | |
| 333 bool operator>(base::nullopt_t, const Optional<T>& opt) { | |
| 334 return false; | |
| 335 } | |
| 336 | |
| 337 template <class T> | |
| 338 bool operator>=(const Optional<T>& opt, base::nullopt_t) { | |
| 339 return true; | |
| 340 } | |
| 341 | |
| 342 template <class T> | |
| 343 bool operator>=(base::nullopt_t, const Optional<T>& opt) { | |
| 344 return !opt; | |
| 345 } | |
| 346 | |
| 347 template <class T> | |
| 348 bool operator==(const Optional<T>& opt, const T& value) { | |
| 349 return bool(opt) ? *opt == value : false; | |
| 350 } | |
| 351 | |
| 352 template <class T> | |
| 353 bool operator==(const T& value, const Optional<T>& opt) { | |
| 354 return bool(opt) ? value == *opt : false; | |
| 355 } | |
| 356 | |
| 357 template <class T> | |
| 358 bool operator!=(const Optional<T>& opt, const T& value) { | |
| 359 return bool(opt) ? *opt != value : true; | |
| 360 } | |
| 361 | |
| 362 template <class T> | |
| 363 bool operator!=(const T& value, const Optional<T>& opt) { | |
| 364 return bool(opt) ? value != *opt : true; | |
| 365 } | |
| 366 | |
| 367 template <class T> | |
| 368 bool operator<(const Optional<T>& opt, const T& value) { | |
| 369 return bool(opt) ? *opt < value : true; | |
| 370 } | |
| 371 | |
| 372 template <class T> | |
| 373 bool operator<(const T& value, const Optional<T>& opt) { | |
| 374 return bool(opt) ? value < *opt : false; | |
| 375 } | |
| 376 | |
| 377 template <class T> | |
| 378 bool operator<=(const Optional<T>& opt, const T& value) { | |
| 379 return !(opt > value); | |
| 380 } | |
| 381 | |
| 382 template <class T> | |
| 383 bool operator<=(const T& value, const Optional<T>& opt) { | |
| 384 return !(value > opt); | |
| 385 } | |
| 386 | |
| 387 template <class T> | |
| 388 bool operator>(const Optional<T>& opt, const T& value) { | |
| 389 return bool(opt) ? value < *opt : false; | |
| 390 } | |
| 391 | |
| 392 template <class T> | |
| 393 bool operator>(const T& value, const Optional<T>& opt) { | |
| 394 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.
| |
| 395 } | |
| 396 | |
| 397 template <class T> | |
| 398 bool operator>=(const Optional<T>& opt, const T& value) { | |
| 399 return !(opt < value); | |
| 400 } | |
| 401 | |
| 402 template <class T> | |
| 403 bool operator>=(const T& value, const Optional<T>& opt) { | |
| 404 return !(value < opt); | |
| 405 } | |
| 406 | |
| 407 template <class T> | |
| 408 Optional<typename std::decay<T>::type> make_optional(T&& value) { | |
| 409 return Optional<typename std::decay<T>::type>(std::forward<T>(value)); | |
| 410 } | |
| 411 | |
| 412 template <class T> | |
| 413 void swap(Optional<T>& lhs, Optional<T>& rhs) { | |
| 414 lhs.swap(rhs); | |
| 415 } | |
| 416 | |
| 417 // 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.
| |
| 418 | |
| 419 } // namespace base | |
| 420 | |
| 421 #endif // BASE_OPTIONAL_H_ | |
| OLD | NEW |