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

Side by Side Diff: base/optional.h

Issue 2080003002: base::Optional: Use anonymous union instead of base::AlignedMemory (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 4 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 | docs/optional.md » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #ifndef BASE_OPTIONAL_H_ 5 #ifndef BASE_OPTIONAL_H_
6 #define BASE_OPTIONAL_H_ 6 #define BASE_OPTIONAL_H_
7 7
8 #include <type_traits> 8 #include <type_traits>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/aligned_memory.h"
12 #include "base/template_util.h" 11 #include "base/template_util.h"
13 12
14 namespace base { 13 namespace base {
15 14
16 // Specification: 15 // Specification:
17 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t 16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
18 struct in_place_t {}; 17 struct in_place_t {};
19 18
20 // Specification: 19 // Specification:
21 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t 20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
22 struct nullopt_t { 21 struct nullopt_t {
23 constexpr explicit nullopt_t(int) {} 22 constexpr explicit nullopt_t(int) {}
24 }; 23 };
25 24
26 // Specification: 25 // Specification:
27 // http://en.cppreference.com/w/cpp/utility/optional/in_place 26 // http://en.cppreference.com/w/cpp/utility/optional/in_place
28 constexpr in_place_t in_place = {}; 27 constexpr in_place_t in_place = {};
29 28
30 // Specification: 29 // Specification:
31 // http://en.cppreference.com/w/cpp/utility/optional/nullopt 30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
32 constexpr nullopt_t nullopt(0); 31 constexpr nullopt_t nullopt(0);
33 32
34 namespace internal { 33 namespace internal {
35 34
36 template <typename T, bool = base::is_trivially_destructible<T>::value> 35 template <typename T, bool = base::is_trivially_destructible<T>::value>
37 struct OptionalStorage { 36 struct OptionalStorage {
37 OptionalStorage() {};
38 // When T is not trivially destructible we must call its 38 // When T is not trivially destructible we must call its
39 // destructor before deallocating its memory. 39 // destructor before deallocating its memory.
40 ~OptionalStorage() { 40 ~OptionalStorage() {
41 if (!is_null_) 41 if (!is_null_)
42 buffer_.template data_as<T>()->~T(); 42 value_.~T();
43 } 43 }
44 44
45 bool is_null_ = true; 45 bool is_null_ = true;
46 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; 46 union {
47 // empty_ exists so that the union will always be initialized, even when it
danakj 2016/08/15 21:22:57 nit: || around variable names. "|empty_| exists.."
kwiberg-chromium 2016/08/16 08:23:47 Done.
48 // doesn't contain a value. Not initializing it has been observed to
49 // trigger comiler warnings.
50 char empty_ = '\0';
51 T value_;
52 };
47 }; 53 };
48 54
49 template <typename T> 55 template <typename T>
50 struct OptionalStorage<T, true> { 56 struct OptionalStorage<T, true> {
51 // When T is trivially destructible (i.e. its destructor does nothing) 57 OptionalStorage() {};
danakj 2016/08/15 21:22:57 no ; here also or how about =default; instead?
kwiberg-chromium 2016/08/16 08:23:47 Done.
52 // there is no need to call it. 58 // When T is trivially destructible (i.e. its destructor does nothing) there
53 // Since |base::AlignedMemory| is just an array its destructor 59 // is no need to call it. Explicitly defaulting the destructor means it's not
54 // is trivial. Explicitly defaulting the destructor means it's not 60 // user-provided. Those two together make this destructor trivial.
55 // user-provided. All of this together make this destructor trivial.
56 ~OptionalStorage() = default; 61 ~OptionalStorage() = default;
57 62
58 bool is_null_ = true; 63 bool is_null_ = true;
59 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_; 64 union {
65 // empty_ exists so that the union will always be initialized, even when it
danakj 2016/08/15 21:22:57 nitto
kwiberg-chromium 2016/08/16 08:23:47 Ha! Done.
66 // doesn't contain a value. Not initializing it has been observed to
67 // trigger comiler warnings.
68 char empty_ = '\0';
69 T value_;
70 };
60 }; 71 };
61 72
62 } // namespace internal 73 } // namespace internal
63 74
64 // base::Optional is a Chromium version of the C++17 optional class: 75 // base::Optional is a Chromium version of the C++17 optional class:
65 // std::optional documentation: 76 // std::optional documentation:
66 // http://en.cppreference.com/w/cpp/utility/optional 77 // http://en.cppreference.com/w/cpp/utility/optional
67 // Chromium documentation: 78 // Chromium documentation:
68 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md 79 // https://chromium.googlesource.com/chromium/src/+/master/docs/optional.md
69 // 80 //
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 T&& operator*() && { return std::move(value()); } 173 T&& operator*() && { return std::move(value()); }
163 174
164 constexpr explicit operator bool() const { return !storage_.is_null_; } 175 constexpr explicit operator bool() const { return !storage_.is_null_; }
165 176
166 constexpr bool has_value() const { return !storage_.is_null_; } 177 constexpr bool has_value() const { return !storage_.is_null_; }
167 178
168 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 179 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
169 // meant to be 'constexpr const'. 180 // meant to be 'constexpr const'.
170 T& value() & { 181 T& value() & {
171 DCHECK(!storage_.is_null_); 182 DCHECK(!storage_.is_null_);
172 return *storage_.buffer_.template data_as<T>(); 183 return storage_.value_;
173 } 184 }
174 185
175 // TODO(mlamouri): can't use 'constexpr' with DCHECK. 186 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
176 const T& value() const& { 187 const T& value() const& {
177 DCHECK(!storage_.is_null_); 188 DCHECK(!storage_.is_null_);
178 return *storage_.buffer_.template data_as<T>(); 189 return storage_.value_;
179 } 190 }
180 191
181 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was 192 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
182 // meant to be 'constexpr const'. 193 // meant to be 'constexpr const'.
183 T&& value() && { 194 T&& value() && {
184 DCHECK(!storage_.is_null_); 195 DCHECK(!storage_.is_null_);
185 return std::move(*storage_.buffer_.template data_as<T>()); 196 return std::move(storage_.value_);
186 } 197 }
187 198
188 // TODO(mlamouri): can't use 'constexpr' with DCHECK. 199 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
189 const T&& value() const&& { 200 const T&& value() const&& {
190 DCHECK(!storage_.is_null_); 201 DCHECK(!storage_.is_null_);
191 return std::move(*storage_.buffer_.template data_as<T>()); 202 return std::move(storage_.value_);
192 } 203 }
193 204
194 template <class U> 205 template <class U>
195 constexpr T value_or(U&& default_value) const& { 206 constexpr T value_or(U&& default_value) const& {
196 // TODO(mlamouri): add the following assert when possible: 207 // TODO(mlamouri): add the following assert when possible:
197 // static_assert(std::is_copy_constructible<T>::value, 208 // static_assert(std::is_copy_constructible<T>::value,
198 // "T must be copy constructible"); 209 // "T must be copy constructible");
199 static_assert(std::is_convertible<U, T>::value, 210 static_assert(std::is_convertible<U, T>::value,
200 "U must be convertible to T"); 211 "U must be convertible to T");
201 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) 212 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
(...skipping 10 matching lines...) Expand all
212 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value)) 223 return storage_.is_null_ ? static_cast<T>(std::forward<U>(default_value))
213 : std::move(value()); 224 : std::move(value());
214 } 225 }
215 226
216 void swap(Optional& other) { 227 void swap(Optional& other) {
217 if (storage_.is_null_ && other.storage_.is_null_) 228 if (storage_.is_null_ && other.storage_.is_null_)
218 return; 229 return;
219 230
220 if (storage_.is_null_ != other.storage_.is_null_) { 231 if (storage_.is_null_ != other.storage_.is_null_) {
221 if (storage_.is_null_) { 232 if (storage_.is_null_) {
222 Init(std::move(*other.storage_.buffer_.template data_as<T>())); 233 Init(std::move(other.storage_.value_));
223 other.FreeIfNeeded(); 234 other.FreeIfNeeded();
224 } else { 235 } else {
225 other.Init(std::move(*storage_.buffer_.template data_as<T>())); 236 other.Init(std::move(storage_.value_));
226 FreeIfNeeded(); 237 FreeIfNeeded();
227 } 238 }
228 return; 239 return;
229 } 240 }
230 241
231 DCHECK(!storage_.is_null_ && !other.storage_.is_null_); 242 DCHECK(!storage_.is_null_ && !other.storage_.is_null_);
232 using std::swap; 243 using std::swap;
233 swap(**this, *other); 244 swap(**this, *other);
234 } 245 }
235 246
236 void reset() { 247 void reset() {
237 FreeIfNeeded(); 248 FreeIfNeeded();
238 } 249 }
239 250
240 template <class... Args> 251 template <class... Args>
241 void emplace(Args&&... args) { 252 void emplace(Args&&... args) {
242 FreeIfNeeded(); 253 FreeIfNeeded();
243 Init(std::forward<Args>(args)...); 254 Init(std::forward<Args>(args)...);
244 } 255 }
245 256
246 private: 257 private:
247 void Init(const T& value) { 258 void Init(const T& value) {
248 DCHECK(storage_.is_null_); 259 DCHECK(storage_.is_null_);
249 new (storage_.buffer_.void_data()) T(value); 260 new (&storage_.value_) T(value);
250 storage_.is_null_ = false; 261 storage_.is_null_ = false;
251 } 262 }
252 263
253 void Init(T&& value) { 264 void Init(T&& value) {
254 DCHECK(storage_.is_null_); 265 DCHECK(storage_.is_null_);
255 new (storage_.buffer_.void_data()) T(std::move(value)); 266 new (&storage_.value_) T(std::move(value));
256 storage_.is_null_ = false; 267 storage_.is_null_ = false;
257 } 268 }
258 269
259 template <class... Args> 270 template <class... Args>
260 void Init(Args&&... args) { 271 void Init(Args&&... args) {
261 DCHECK(storage_.is_null_); 272 DCHECK(storage_.is_null_);
262 new (storage_.buffer_.void_data()) T(std::forward<Args>(args)...); 273 new (&storage_.value_) T(std::forward<Args>(args)...);
263 storage_.is_null_ = false; 274 storage_.is_null_ = false;
264 } 275 }
265 276
266 void InitOrAssign(const T& value) { 277 void InitOrAssign(const T& value) {
267 if (storage_.is_null_) 278 if (storage_.is_null_)
268 Init(value); 279 Init(value);
269 else 280 else
270 *storage_.buffer_.template data_as<T>() = value; 281 storage_.value_ = value;
271 } 282 }
272 283
273 void InitOrAssign(T&& value) { 284 void InitOrAssign(T&& value) {
274 if (storage_.is_null_) 285 if (storage_.is_null_)
275 Init(std::move(value)); 286 Init(std::move(value));
276 else 287 else
277 *storage_.buffer_.template data_as<T>() = std::move(value); 288 storage_.value_ = std::move(value);
278 } 289 }
279 290
280 void FreeIfNeeded() { 291 void FreeIfNeeded() {
281 if (storage_.is_null_) 292 if (storage_.is_null_)
282 return; 293 return;
283 storage_.buffer_.template data_as<T>()->~T(); 294 storage_.value_.~T();
284 storage_.is_null_ = true; 295 storage_.is_null_ = true;
285 } 296 }
286 297
287 internal::OptionalStorage<T> storage_; 298 internal::OptionalStorage<T> storage_;
288 }; 299 };
289 300
290 template <class T> 301 template <class T>
291 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) { 302 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
292 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs); 303 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
293 } 304 }
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
454 template <class T> 465 template <class T>
455 struct hash<base::Optional<T>> { 466 struct hash<base::Optional<T>> {
456 size_t operator()(const base::Optional<T>& opt) const { 467 size_t operator()(const base::Optional<T>& opt) const {
457 return opt == base::nullopt ? 0 : std::hash<T>()(*opt); 468 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
458 } 469 }
459 }; 470 };
460 471
461 } // namespace std 472 } // namespace std
462 473
463 #endif // BASE_OPTIONAL_H_ 474 #endif // BASE_OPTIONAL_H_
OLDNEW
« no previous file with comments | « no previous file | docs/optional.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698