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

Side by Side Diff: base/optional.h

Issue 1245163002: Base: add Optional<T>. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review comments Created 4 years, 9 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
OLDNEW
(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 <type_traits>
9
10 #include "base/logging.h"
11 #include "base/memory/aligned_memory.h"
12
13 namespace base {
14
15 // Specification:
16 // http://en.cppreference.com/w/cpp/utility/optional/in_place_t
17 struct in_place_t {};
18
19 // Specification:
20 // http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
21 struct nullopt_t {
22 constexpr explicit nullopt_t(int) {}
23 };
24
25 // Specification:
26 // http://en.cppreference.com/w/cpp/utility/optional/in_place
27 constexpr in_place_t in_place;
horo 2016/03/24 06:25:58 I think you need an explicit initializer to initia
mlamouri (slow - plz ping) 2016/03/24 09:20:44 danajk@ was against using this.
horo 2016/03/24 09:47:22 I'm not familiar with constexpr. But when I added
dcheng 2016/03/24 10:20:51 Some alternatives: constexpr in_place_t in_place
danakj 2016/03/24 20:38:51 = {} sounds good.
danakj 2016/04/12 23:37:53 Let's do this ^ still.
28
29 // Specification:
30 // http://en.cppreference.com/w/cpp/utility/optional/nullopt
31 constexpr nullopt_t nullopt(0);
32
33 // base::Optional is a Chromium version of the C++17 optional class:
34 // http://en.cppreference.com/w/cpp/utility/optional
35 // The following known differences apply:
36 // - The constructor and emplace method using initializer_list are not
37 // implemented because 'initializer_list' is banned from Chromium.
38 // - Constructors do not use 'constexpr' as it is a C++14 extension.
39 // - 'constexpr' might be missing in some places for reasons specified locally.
40 // - No exceptions are thrown, because they are banned from Chromium.
41 // - All the non-members are in the 'base' namespace instead of 'std'.
42 template <typename T>
43 class Optional {
44 public:
45 constexpr Optional() = default;
46 Optional(base::nullopt_t) : Optional() {}
47
48 Optional(const Optional& other) {
49 if (!other.is_null_)
50 Init(other.value());
51 }
52
53 Optional(Optional&& other) {
54 if (!other.is_null_)
55 Init(std::move(other.value()));
56 }
57
58 Optional(const T& value) { Init(value); }
59
60 Optional(T&& value) { Init(std::move(value)); }
61
62 template <class... Args>
63 explicit Optional(base::in_place_t, Args&&... args) {
64 emplace(std::forward<Args>(args)...);
65 }
66
67 ~Optional() {
68 // TODO(mlamouri): use is_trivially_destructible when possible.
danakj 2016/03/21 21:49:38 Can you show what the error is when you use it?
mlamouri (slow - plz ping) 2016/03/24 09:20:44 Sure: In file included from ../../base/optional_un
danakj 2016/03/24 20:38:51 Oh, gcc is missing that, ok. We should add it to b
69 FreeIfNeeded();
70 }
71
72 Optional& operator=(base::nullopt_t) {
73 FreeIfNeeded();
74 return *this;
75 }
76
77 Optional& operator=(const Optional& other) {
78 if (other.is_null_) {
79 FreeIfNeeded();
80 return *this;
81 }
82
83 InitOrAssign(other.value());
84 return *this;
85 }
86
87 Optional& operator=(Optional&& other) {
88 if (other.is_null_) {
89 FreeIfNeeded();
90 return *this;
91 }
92
93 InitOrAssign(std::move(other.value()));
94 return *this;
95 }
96
97 template <class U>
98 typename std::enable_if<std::is_same<std::decay<U>, T>::value,
99 Optional&>::type
100 operator=(U&& value) {
101 InitOrAssign(std::forward<U>(value));
102 return *this;
103 }
104
105 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
106 const T* operator->() const {
107 DCHECK(!is_null_);
108 return &value();
109 }
110
111 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
112 // meant to be 'constexpr const'.
113 T* operator->() {
114 DCHECK(!is_null_);
115 return &value();
116 }
117
118 constexpr const T& operator*() const& { return value(); }
119
120 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
121 // meant to be 'constexpr const'.
122 T& operator*() & { return value(); }
123
124 constexpr const T&& operator*() const&& { return std::move(value()); }
125
126 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
127 // meant to be 'constexpr const'.
128 T&& operator*() && { return std::move(value()); }
129
130 constexpr explicit operator bool() const { return !is_null_; }
131
132 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
133 // meant to be 'constexpr const'.
134 T& value() & {
135 DCHECK(!is_null_);
136 return *buffer_.template data_as<T>();
137 }
138
139 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
140 const T& value() const& {
141 DCHECK(!is_null_);
142 return *buffer_.template data_as<T>();
143 }
144
145 // TODO(mlamouri): using 'constexpr' here breaks compiler that assume it was
146 // meant to be 'constexpr const'.
147 T&& value() && {
148 DCHECK(!is_null_);
149 return std::move(*buffer_.template data_as<T>());
150 }
151
152 // TODO(mlamouri): can't use 'constexpr' with DCHECK.
153 const T&& value() const&& {
154 DCHECK(!is_null_);
155 return std::move(*buffer_.template data_as<T>());
156 }
157
158 template <class U>
159 constexpr T value_or(U&& default_value) const& {
160 // TODO(mlamouri): switch to is_copy_convertible when possible.
danakj 2016/03/21 21:49:37 It should assert both when possible, not switch to
mlamouri (slow - plz ping) 2016/03/24 09:20:44 Done.
161 static_assert(std::is_convertible<U, T>::value,
162 "U must be convertible to T");
163 return is_null_ ? static_cast<T>(std::forward<U>(default_value)) : value();
164 }
165
166 template <class U>
167 T value_or(U&& default_value) && {
168 // TODO(mlamouri): switch to is_move_convertible when possible.
danakj 2016/03/21 21:49:38 ditto
mlamouri (slow - plz ping) 2016/03/24 09:20:44 Done.
169 static_assert(std::is_convertible<U, T>::value,
170 "U must be convertible to T");
171 return is_null_ ? static_cast<T>(std::forward<U>(default_value))
172 : std::move(value());
173 }
174
175 void swap(Optional& other) {
176 if (is_null_ && other.is_null_)
177 return;
178
179 if (is_null_ != other.is_null_) {
180 if (is_null_) {
181 Init(std::move(*other.buffer_.template data_as<T>()));
182 other.FreeIfNeeded();
183 } else {
184 other.Init(std::move(*buffer_.template data_as<T>()));
185 FreeIfNeeded();
186 }
187 return;
188 }
189
190 DCHECK(!is_null_ && !other.is_null_);
191 using std::swap;
192 swap(**this, *other);
193 }
194
195 template <class... Args>
196 void emplace(Args&&... args) {
197 FreeIfNeeded();
198 Init(std::forward<Args>(args)...);
199 }
200
201 private:
202 void Init(const T& value) {
203 DCHECK(is_null_);
204 new (buffer_.template data_as<T>()) T(value);
205 is_null_ = false;
206 }
207
208 void Init(T&& value) {
209 DCHECK(is_null_);
210 new (buffer_.template data_as<T>()) T(std::move(value));
211 is_null_ = false;
212 }
213
214 template <class... Args>
215 void Init(Args&&... args) {
216 DCHECK(is_null_);
217 new (buffer_.template data_as<T>()) T(std::forward<Args>(args)...);
218 is_null_ = false;
219 }
220
221 void InitOrAssign(const T& value) {
222 if (is_null_)
223 Init(value);
224 else
225 *buffer_.template data_as<T>() = value;
226 }
227
228 void InitOrAssign(T&& value) {
229 if (is_null_)
230 Init(std::move(value));
231 else
232 *buffer_.template data_as<T>() = std::move(value);
233 }
234
235 void FreeIfNeeded() {
236 if (is_null_)
237 return;
238 buffer_.template data_as<T>()->~T();
239 is_null_ = true;
240 }
241
242 bool is_null_ = true;
243 base::AlignedMemory<sizeof(T), ALIGNOF(T)> buffer_;
244 };
245
246 template <class T>
247 constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs) {
248 return !!lhs != !!rhs ? false : lhs == nullopt || (*lhs == *rhs);
249 }
250
251 template <class T>
252 constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs) {
253 return !(lhs == rhs);
254 }
255
256 template <class T>
257 constexpr bool operator<(const Optional<T>& lhs, const Optional<T>& rhs) {
258 return rhs == nullopt ? false : lhs == nullopt ? true : *lhs < *rhs;
danakj 2016/03/21 21:49:37 can you use () around the nested expression?
mlamouri (slow - plz ping) 2016/03/24 09:20:45 If I understood correctly, done.
259 }
260
261 template <class T>
262 constexpr bool operator<=(const Optional<T>& lhs, const Optional<T>& rhs) {
263 return !(rhs < lhs);
264 }
265
266 template <class T>
267 constexpr bool operator>(const Optional<T>& lhs, const Optional<T>& rhs) {
268 return rhs < lhs;
269 }
270
271 template <class T>
272 constexpr bool operator>=(const Optional<T>& lhs, const Optional<T>& rhs) {
273 return !(lhs < rhs);
274 }
275
276 template <class T>
277 constexpr bool operator==(const Optional<T>& opt, base::nullopt_t) {
278 return !opt;
279 }
280
281 template <class T>
282 constexpr bool operator==(base::nullopt_t, const Optional<T>& opt) {
283 return !opt;
284 }
285
286 template <class T>
287 constexpr bool operator!=(const Optional<T>& opt, base::nullopt_t) {
288 return !!opt;
289 }
290
291 template <class T>
292 constexpr bool operator!=(base::nullopt_t, const Optional<T>& opt) {
293 return !!opt;
294 }
295
296 template <class T>
297 constexpr bool operator<(const Optional<T>& opt, base::nullopt_t) {
298 return false;
299 }
300
301 template <class T>
302 constexpr bool operator<(base::nullopt_t, const Optional<T>& opt) {
303 return !!opt;
304 }
305
306 template <class T>
307 constexpr bool operator<=(const Optional<T>& opt, base::nullopt_t) {
308 return !opt;
309 }
310
311 template <class T>
312 constexpr bool operator<=(base::nullopt_t, const Optional<T>& opt) {
313 return true;
314 }
315
316 template <class T>
317 constexpr bool operator>(const Optional<T>& opt, base::nullopt_t) {
318 return !!opt;
319 }
320
321 template <class T>
322 constexpr bool operator>(base::nullopt_t, const Optional<T>& opt) {
323 return false;
324 }
325
326 template <class T>
327 constexpr bool operator>=(const Optional<T>& opt, base::nullopt_t) {
328 return true;
329 }
330
331 template <class T>
332 constexpr bool operator>=(base::nullopt_t, const Optional<T>& opt) {
333 return !opt;
334 }
335
336 template <class T>
337 constexpr bool operator==(const Optional<T>& opt, const T& value) {
338 return opt != nullopt ? *opt == value : false;
339 }
340
341 template <class T>
342 constexpr bool operator==(const T& value, const Optional<T>& opt) {
343 return opt != nullopt ? value == *opt : false;
344 }
345
346 template <class T>
347 constexpr bool operator!=(const Optional<T>& opt, const T& value) {
348 return opt != nullopt ? *opt != value : true;
349 }
350
351 template <class T>
352 constexpr bool operator!=(const T& value, const Optional<T>& opt) {
353 return opt != nullopt ? value != *opt : true;
354 }
355
356 template <class T>
357 constexpr bool operator<(const Optional<T>& opt, const T& value) {
358 return opt != nullopt ? *opt < value : true;
359 }
360
361 template <class T>
362 constexpr bool operator<(const T& value, const Optional<T>& opt) {
363 return opt != nullopt ? value < *opt : false;
364 }
365
366 template <class T>
367 constexpr bool operator<=(const Optional<T>& opt, const T& value) {
368 return !(opt > value);
369 }
370
371 template <class T>
372 constexpr bool operator<=(const T& value, const Optional<T>& opt) {
373 return !(value > opt);
374 }
375
376 template <class T>
377 constexpr bool operator>(const Optional<T>& opt, const T& value) {
378 return opt != nullopt ? value < *opt : false;
379 }
380
381 template <class T>
382 constexpr bool operator>(const T& value, const Optional<T>& opt) {
383 return opt != nullopt ? *opt < value : true;
384 }
385
386 template <class T>
387 constexpr bool operator>=(const Optional<T>& opt, const T& value) {
388 return !(opt < value);
389 }
390
391 template <class T>
392 constexpr bool operator>=(const T& value, const Optional<T>& opt) {
393 return !(value < opt);
394 }
395
396 template <class T>
397 constexpr Optional<typename std::decay<T>::type> make_optional(T&& value) {
398 return Optional<typename std::decay<T>::type>(std::forward<T>(value));
399 }
400
401 template <class T>
402 void swap(Optional<T>& lhs, Optional<T>& rhs) {
403 lhs.swap(rhs);
404 }
405
406 } // namespace base
407
408 namespace std {
409
410 template <class T>
411 struct hash<base::Optional<T>> {
412 size_t operator()(const base::Optional<T>& opt) const {
413 return opt == base::nullopt ? 0 : std::hash<T>()(*opt);
414 }
415 };
416
417 } // namespace std
418
419 #endif // BASE_OPTIONAL_H_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/optional_unittest.cc » ('j') | base/optional_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698