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 |