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

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: closer to spec implementation 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
« no previous file with comments | « base/base.gypi ('k') | base/optional_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "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_
OLDNEW
« no previous file with comments | « base/base.gypi ('k') | base/optional_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698