1 | /* |
2 | * Copyright 2012-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #include <folly/Optional.h> |
18 | #include <folly/Portability.h> |
19 | #include <folly/portability/GMock.h> |
20 | #include <folly/portability/GTest.h> |
21 | |
22 | #include <algorithm> |
23 | #include <initializer_list> |
24 | #include <iomanip> |
25 | #include <memory> |
26 | #include <string> |
27 | #include <tuple> |
28 | #include <type_traits> |
29 | #include <unordered_map> |
30 | #include <vector> |
31 | |
32 | #include <boost/optional.hpp> |
33 | |
34 | using std::shared_ptr; |
35 | using std::unique_ptr; |
36 | |
37 | namespace folly { |
38 | |
39 | namespace { |
40 | |
41 | template <class V> |
42 | std::ostream& operator<<(std::ostream& os, const Optional<V>& v) { |
43 | if (v) { |
44 | os << "Optional(" << v.value() << ')'; |
45 | } else { |
46 | os << "None" ; |
47 | } |
48 | return os; |
49 | } |
50 | |
51 | struct NoDefault { |
52 | NoDefault(int, int) {} |
53 | char a, b, c; |
54 | }; |
55 | |
56 | } // namespace |
57 | |
58 | static_assert(sizeof(Optional<char>) == 2, "" ); |
59 | static_assert(sizeof(Optional<int>) == 8, "" ); |
60 | static_assert(sizeof(Optional<NoDefault>) == 4, "" ); |
61 | static_assert(sizeof(Optional<char>) == sizeof(boost::optional<char>), "" ); |
62 | static_assert(sizeof(Optional<short>) == sizeof(boost::optional<short>), "" ); |
63 | static_assert(sizeof(Optional<int>) == sizeof(boost::optional<int>), "" ); |
64 | static_assert(sizeof(Optional<double>) == sizeof(boost::optional<double>), "" ); |
65 | |
66 | TEST(Optional, NoDefault) { |
67 | Optional<NoDefault> x; |
68 | EXPECT_FALSE(x); |
69 | x.emplace(4, 5); |
70 | EXPECT_TRUE(bool(x)); |
71 | x.clear(); |
72 | EXPECT_FALSE(x); |
73 | } |
74 | |
75 | TEST(Optional, Emplace) { |
76 | Optional<std::vector<int>> opt; |
77 | auto& values1 = opt.emplace(3, 4); |
78 | EXPECT_THAT(values1, testing::ElementsAre(4, 4, 4)); |
79 | auto& values2 = opt.emplace(2, 5); |
80 | EXPECT_THAT(values2, testing::ElementsAre(5, 5)); |
81 | } |
82 | |
83 | TEST(Optional, EmplaceInitializerList) { |
84 | Optional<std::vector<int>> opt; |
85 | auto& values1 = opt.emplace({3, 4, 5}); |
86 | EXPECT_THAT(values1, testing::ElementsAre(3, 4, 5)); |
87 | auto& values2 = opt.emplace({4, 5, 6}); |
88 | EXPECT_THAT(values2, testing::ElementsAre(4, 5, 6)); |
89 | } |
90 | |
91 | TEST(Optional, Reset) { |
92 | Optional<int> opt(3); |
93 | opt.reset(); |
94 | EXPECT_FALSE(opt); |
95 | } |
96 | |
97 | TEST(Optional, String) { |
98 | Optional<std::string> maybeString; |
99 | EXPECT_FALSE(maybeString); |
100 | maybeString = "hello" ; |
101 | EXPECT_TRUE(bool(maybeString)); |
102 | } |
103 | |
104 | TEST(Optional, Const) { |
105 | { // default construct |
106 | Optional<const int> opt; |
107 | EXPECT_FALSE(bool(opt)); |
108 | opt.emplace(4); |
109 | EXPECT_EQ(*opt, 4); |
110 | opt.emplace(5); |
111 | EXPECT_EQ(*opt, 5); |
112 | opt.clear(); |
113 | EXPECT_FALSE(bool(opt)); |
114 | } |
115 | { // copy-constructed |
116 | const int x = 6; |
117 | Optional<const int> opt(x); |
118 | EXPECT_EQ(*opt, 6); |
119 | } |
120 | { // move-constructed |
121 | const int x = 7; |
122 | Optional<const int> opt(std::move(x)); |
123 | EXPECT_EQ(*opt, 7); |
124 | } |
125 | // no assignment allowed |
126 | } |
127 | |
128 | TEST(Optional, Simple) { |
129 | Optional<int> opt; |
130 | EXPECT_FALSE(bool(opt)); |
131 | EXPECT_EQ(42, opt.value_or(42)); |
132 | opt = 4; |
133 | EXPECT_TRUE(bool(opt)); |
134 | EXPECT_EQ(4, *opt); |
135 | EXPECT_EQ(4, opt.value_or(42)); |
136 | opt = 5; |
137 | EXPECT_EQ(5, *opt); |
138 | opt.clear(); |
139 | EXPECT_FALSE(bool(opt)); |
140 | } |
141 | |
142 | namespace { |
143 | |
144 | class MoveTester { |
145 | public: |
146 | /* implicit */ MoveTester(const char* s) : s_(s) {} |
147 | MoveTester(const MoveTester&) = default; |
148 | MoveTester(MoveTester&& other) noexcept { |
149 | s_ = std::move(other.s_); |
150 | other.s_ = "" ; |
151 | } |
152 | MoveTester& operator=(const MoveTester&) = default; |
153 | MoveTester& operator=(MoveTester&& other) noexcept { |
154 | s_ = std::move(other.s_); |
155 | other.s_ = "" ; |
156 | return *this; |
157 | } |
158 | |
159 | private: |
160 | friend bool operator==(const MoveTester& o1, const MoveTester& o2); |
161 | std::string s_; |
162 | }; |
163 | |
164 | bool operator==(const MoveTester& o1, const MoveTester& o2) { |
165 | return o1.s_ == o2.s_; |
166 | } |
167 | |
168 | } // namespace |
169 | |
170 | TEST(Optional, value_or_rvalue_arg) { |
171 | Optional<MoveTester> opt; |
172 | MoveTester dflt = "hello" ; |
173 | EXPECT_EQ("hello" , opt.value_or(dflt)); |
174 | EXPECT_EQ("hello" , dflt); |
175 | EXPECT_EQ("hello" , opt.value_or(std::move(dflt))); |
176 | EXPECT_EQ("" , dflt); |
177 | EXPECT_EQ("world" , opt.value_or("world" )); |
178 | |
179 | dflt = "hello" ; |
180 | // Make sure that the const overload works on const objects |
181 | const auto& optc = opt; |
182 | EXPECT_EQ("hello" , optc.value_or(dflt)); |
183 | EXPECT_EQ("hello" , dflt); |
184 | EXPECT_EQ("hello" , optc.value_or(std::move(dflt))); |
185 | EXPECT_EQ("" , dflt); |
186 | EXPECT_EQ("world" , optc.value_or("world" )); |
187 | |
188 | dflt = "hello" ; |
189 | opt = "meow" ; |
190 | EXPECT_EQ("meow" , opt.value_or(dflt)); |
191 | EXPECT_EQ("hello" , dflt); |
192 | EXPECT_EQ("meow" , opt.value_or(std::move(dflt))); |
193 | EXPECT_EQ("hello" , dflt); // only moved if used |
194 | } |
195 | |
196 | TEST(Optional, value_or_noncopyable) { |
197 | Optional<std::unique_ptr<int>> opt; |
198 | std::unique_ptr<int> dflt(new int(42)); |
199 | EXPECT_EQ(42, *std::move(opt).value_or(std::move(dflt))); |
200 | } |
201 | |
202 | struct ExpectingDeleter { |
203 | explicit ExpectingDeleter(int expected_) : expected(expected_) {} |
204 | int expected; |
205 | void operator()(const int* ptr) { |
206 | EXPECT_EQ(*ptr, expected); |
207 | delete ptr; |
208 | } |
209 | }; |
210 | |
211 | TEST(Optional, value_move) { |
212 | auto ptr = Optional<std::unique_ptr<int, ExpectingDeleter>>( |
213 | {new int(42), ExpectingDeleter{1337}}) |
214 | .value(); |
215 | *ptr = 1337; |
216 | } |
217 | |
218 | TEST(Optional, dereference_move) { |
219 | auto ptr = *Optional<std::unique_ptr<int, ExpectingDeleter>>( |
220 | {new int(42), ExpectingDeleter{1337}}); |
221 | *ptr = 1337; |
222 | } |
223 | |
224 | TEST(Optional, EmptyConstruct) { |
225 | Optional<int> opt; |
226 | EXPECT_FALSE(bool(opt)); |
227 | Optional<int> test1(opt); |
228 | EXPECT_FALSE(bool(test1)); |
229 | Optional<int> test2(std::move(opt)); |
230 | EXPECT_FALSE(bool(test2)); |
231 | } |
232 | |
233 | TEST(Optional, InPlaceConstruct) { |
234 | using A = std::pair<int, double>; |
235 | Optional<A> opt(in_place, 5, 3.2); |
236 | EXPECT_TRUE(bool(opt)); |
237 | EXPECT_EQ(5, opt->first); |
238 | } |
239 | |
240 | TEST(Optional, InPlaceNestedConstruct) { |
241 | using A = std::pair<int, double>; |
242 | Optional<Optional<A>> opt(in_place, in_place, 5, 3.2); |
243 | EXPECT_TRUE(bool(opt)); |
244 | EXPECT_TRUE(bool(*opt)); |
245 | EXPECT_EQ(5, (*opt)->first); |
246 | } |
247 | |
248 | TEST(Optional, Unique) { |
249 | Optional<unique_ptr<int>> opt; |
250 | |
251 | opt.clear(); |
252 | EXPECT_FALSE(bool(opt)); |
253 | // empty->emplaced |
254 | opt.emplace(new int(5)); |
255 | EXPECT_TRUE(bool(opt)); |
256 | EXPECT_EQ(5, **opt); |
257 | |
258 | opt.clear(); |
259 | // empty->moved |
260 | opt = std::make_unique<int>(6); |
261 | EXPECT_EQ(6, **opt); |
262 | // full->moved |
263 | opt = std::make_unique<int>(7); |
264 | EXPECT_EQ(7, **opt); |
265 | |
266 | // move it out by move construct |
267 | Optional<unique_ptr<int>> moved(std::move(opt)); |
268 | EXPECT_TRUE(bool(moved)); |
269 | EXPECT_FALSE(bool(opt)); |
270 | EXPECT_EQ(7, **moved); |
271 | |
272 | EXPECT_TRUE(bool(moved)); |
273 | opt = std::move(moved); // move it back by move assign |
274 | EXPECT_FALSE(bool(moved)); |
275 | EXPECT_TRUE(bool(opt)); |
276 | EXPECT_EQ(7, **opt); |
277 | } |
278 | |
279 | TEST(Optional, Shared) { |
280 | shared_ptr<int> ptr; |
281 | Optional<shared_ptr<int>> opt; |
282 | EXPECT_FALSE(bool(opt)); |
283 | // empty->emplaced |
284 | opt.emplace(new int(5)); |
285 | EXPECT_TRUE(bool(opt)); |
286 | ptr = opt.value(); |
287 | EXPECT_EQ(ptr.get(), opt->get()); |
288 | EXPECT_EQ(2, ptr.use_count()); |
289 | opt.clear(); |
290 | EXPECT_EQ(1, ptr.use_count()); |
291 | // full->copied |
292 | opt = ptr; |
293 | EXPECT_EQ(2, ptr.use_count()); |
294 | EXPECT_EQ(ptr.get(), opt->get()); |
295 | opt.clear(); |
296 | EXPECT_EQ(1, ptr.use_count()); |
297 | // full->moved |
298 | opt = std::move(ptr); |
299 | EXPECT_EQ(1, opt->use_count()); |
300 | EXPECT_EQ(nullptr, ptr.get()); |
301 | { |
302 | Optional<shared_ptr<int>> copied(opt); |
303 | EXPECT_EQ(2, opt->use_count()); |
304 | Optional<shared_ptr<int>> moved(std::move(opt)); |
305 | EXPECT_EQ(2, moved->use_count()); |
306 | moved.emplace(new int(6)); |
307 | EXPECT_EQ(1, moved->use_count()); |
308 | copied = moved; |
309 | EXPECT_EQ(2, moved->use_count()); |
310 | } |
311 | } |
312 | |
313 | TEST(Optional, Order) { |
314 | std::vector<Optional<int>> vect{ |
315 | {none}, |
316 | {3}, |
317 | {1}, |
318 | {none}, |
319 | {2}, |
320 | }; |
321 | std::vector<Optional<int>> expected{ |
322 | {none}, |
323 | {none}, |
324 | {1}, |
325 | {2}, |
326 | {3}, |
327 | }; |
328 | std::sort(vect.begin(), vect.end()); |
329 | EXPECT_EQ(vect, expected); |
330 | } |
331 | |
332 | TEST(Optional, Swap) { |
333 | Optional<std::string> a; |
334 | Optional<std::string> b; |
335 | |
336 | swap(a, b); |
337 | EXPECT_FALSE(a.hasValue()); |
338 | EXPECT_FALSE(b.hasValue()); |
339 | |
340 | a = "hello" ; |
341 | EXPECT_TRUE(a.hasValue()); |
342 | EXPECT_FALSE(b.hasValue()); |
343 | EXPECT_EQ("hello" , a.value()); |
344 | |
345 | swap(a, b); |
346 | EXPECT_FALSE(a.hasValue()); |
347 | EXPECT_TRUE(b.hasValue()); |
348 | EXPECT_EQ("hello" , b.value()); |
349 | |
350 | a = "bye" ; |
351 | EXPECT_TRUE(a.hasValue()); |
352 | EXPECT_EQ("bye" , a.value()); |
353 | |
354 | swap(a, b); |
355 | EXPECT_TRUE(a.hasValue()); |
356 | EXPECT_TRUE(b.hasValue()); |
357 | EXPECT_EQ("hello" , a.value()); |
358 | EXPECT_EQ("bye" , b.value()); |
359 | } |
360 | |
361 | TEST(Optional, Comparisons) { |
362 | Optional<int> o_; |
363 | Optional<int> o1(1); |
364 | Optional<int> o2(2); |
365 | |
366 | EXPECT_TRUE(o_ <= (o_)); |
367 | EXPECT_TRUE(o_ == (o_)); |
368 | EXPECT_TRUE(o_ >= (o_)); |
369 | |
370 | EXPECT_TRUE(o1 < o2); |
371 | EXPECT_TRUE(o1 <= o2); |
372 | EXPECT_TRUE(o1 <= (o1)); |
373 | EXPECT_TRUE(o1 == (o1)); |
374 | EXPECT_TRUE(o1 != o2); |
375 | EXPECT_TRUE(o1 >= (o1)); |
376 | EXPECT_TRUE(o2 >= o1); |
377 | EXPECT_TRUE(o2 > o1); |
378 | |
379 | EXPECT_FALSE(o2 < o1); |
380 | EXPECT_FALSE(o2 <= o1); |
381 | EXPECT_FALSE(o2 <= o1); |
382 | EXPECT_FALSE(o2 == o1); |
383 | EXPECT_FALSE(o1 != (o1)); |
384 | EXPECT_FALSE(o1 >= o2); |
385 | EXPECT_FALSE(o1 >= o2); |
386 | EXPECT_FALSE(o1 > o2); |
387 | |
388 | /* folly::Optional explicitly doesn't support comparisons with contained value |
389 | EXPECT_TRUE(1 < o2); |
390 | EXPECT_TRUE(1 <= o2); |
391 | EXPECT_TRUE(1 <= o1); |
392 | EXPECT_TRUE(1 == o1); |
393 | EXPECT_TRUE(2 != o1); |
394 | EXPECT_TRUE(1 >= o1); |
395 | EXPECT_TRUE(2 >= o1); |
396 | EXPECT_TRUE(2 > o1); |
397 | |
398 | EXPECT_FALSE(o2 < 1); |
399 | EXPECT_FALSE(o2 <= 1); |
400 | EXPECT_FALSE(o2 <= 1); |
401 | EXPECT_FALSE(o2 == 1); |
402 | EXPECT_FALSE(o2 != 2); |
403 | EXPECT_FALSE(o1 >= 2); |
404 | EXPECT_FALSE(o1 >= 2); |
405 | EXPECT_FALSE(o1 > 2); |
406 | */ |
407 | |
408 | // boost::optional does support comparison with contained value, which can |
409 | // lead to confusion when a bool is contained |
410 | boost::optional<int> boi(3); |
411 | EXPECT_TRUE(boi < 5); |
412 | EXPECT_TRUE(boi <= 4); |
413 | EXPECT_TRUE(boi == 3); |
414 | EXPECT_TRUE(boi != 2); |
415 | EXPECT_TRUE(boi >= 1); |
416 | EXPECT_TRUE(boi > 0); |
417 | EXPECT_TRUE(1 < boi); |
418 | EXPECT_TRUE(2 <= boi); |
419 | EXPECT_TRUE(3 == boi); |
420 | EXPECT_TRUE(4 != boi); |
421 | EXPECT_TRUE(5 >= boi); |
422 | EXPECT_TRUE(6 > boi); |
423 | |
424 | boost::optional<bool> bob(false); |
425 | EXPECT_TRUE((bool)bob); |
426 | EXPECT_TRUE(bob == false); // well that was confusing |
427 | EXPECT_FALSE(bob != false); |
428 | } |
429 | |
430 | TEST(Optional, HeterogeneousComparisons) { |
431 | using opt8 = Optional<uint8_t>; |
432 | using opt64 = Optional<uint64_t>; |
433 | |
434 | EXPECT_TRUE(opt8(4) == uint64_t(4)); |
435 | EXPECT_FALSE(opt8(8) == uint64_t(4)); |
436 | EXPECT_FALSE(opt8() == uint64_t(4)); |
437 | |
438 | EXPECT_TRUE(uint64_t(4) == opt8(4)); |
439 | EXPECT_FALSE(uint64_t(4) == opt8(8)); |
440 | EXPECT_FALSE(uint64_t(4) == opt8()); |
441 | |
442 | EXPECT_FALSE(opt8(4) != uint64_t(4)); |
443 | EXPECT_TRUE(opt8(8) != uint64_t(4)); |
444 | EXPECT_TRUE(opt8() != uint64_t(4)); |
445 | |
446 | EXPECT_FALSE(uint64_t(4) != opt8(4)); |
447 | EXPECT_TRUE(uint64_t(4) != opt8(8)); |
448 | EXPECT_TRUE(uint64_t(4) != opt8()); |
449 | |
450 | EXPECT_TRUE(opt8() == opt64()); |
451 | EXPECT_TRUE(opt8(4) == opt64(4)); |
452 | EXPECT_FALSE(opt8(8) == opt64(4)); |
453 | EXPECT_FALSE(opt8() == opt64(4)); |
454 | EXPECT_FALSE(opt8(4) == opt64()); |
455 | |
456 | EXPECT_FALSE(opt8() != opt64()); |
457 | EXPECT_FALSE(opt8(4) != opt64(4)); |
458 | EXPECT_TRUE(opt8(8) != opt64(4)); |
459 | EXPECT_TRUE(opt8() != opt64(4)); |
460 | EXPECT_TRUE(opt8(4) != opt64()); |
461 | |
462 | EXPECT_TRUE(opt8() < opt64(4)); |
463 | EXPECT_TRUE(opt8(4) < opt64(8)); |
464 | EXPECT_FALSE(opt8() < opt64()); |
465 | EXPECT_FALSE(opt8(4) < opt64(4)); |
466 | EXPECT_FALSE(opt8(8) < opt64(4)); |
467 | EXPECT_FALSE(opt8(4) < opt64()); |
468 | |
469 | EXPECT_FALSE(opt8() > opt64(4)); |
470 | EXPECT_FALSE(opt8(4) > opt64(8)); |
471 | EXPECT_FALSE(opt8() > opt64()); |
472 | EXPECT_FALSE(opt8(4) > opt64(4)); |
473 | EXPECT_TRUE(opt8(8) > opt64(4)); |
474 | EXPECT_TRUE(opt8(4) > opt64()); |
475 | |
476 | EXPECT_TRUE(opt8() <= opt64(4)); |
477 | EXPECT_TRUE(opt8(4) <= opt64(8)); |
478 | EXPECT_TRUE(opt8() <= opt64()); |
479 | EXPECT_TRUE(opt8(4) <= opt64(4)); |
480 | EXPECT_FALSE(opt8(8) <= opt64(4)); |
481 | EXPECT_FALSE(opt8(4) <= opt64()); |
482 | |
483 | EXPECT_FALSE(opt8() >= opt64(4)); |
484 | EXPECT_FALSE(opt8(4) >= opt64(8)); |
485 | EXPECT_TRUE(opt8() >= opt64()); |
486 | EXPECT_TRUE(opt8(4) >= opt64(4)); |
487 | EXPECT_TRUE(opt8(8) >= opt64(4)); |
488 | EXPECT_TRUE(opt8(4) >= opt64()); |
489 | } |
490 | |
491 | TEST(Optional, NoneComparisons) { |
492 | using opt = Optional<int>; |
493 | EXPECT_TRUE(opt() == none); |
494 | EXPECT_TRUE(none == opt()); |
495 | EXPECT_FALSE(opt(1) == none); |
496 | EXPECT_FALSE(none == opt(1)); |
497 | |
498 | EXPECT_FALSE(opt() != none); |
499 | EXPECT_FALSE(none != opt()); |
500 | EXPECT_TRUE(opt(1) != none); |
501 | EXPECT_TRUE(none != opt(1)); |
502 | |
503 | EXPECT_FALSE(opt() < none); |
504 | EXPECT_FALSE(none < opt()); |
505 | EXPECT_FALSE(opt(1) < none); |
506 | EXPECT_TRUE(none < opt(1)); |
507 | |
508 | EXPECT_FALSE(opt() > none); |
509 | EXPECT_FALSE(none > opt()); |
510 | EXPECT_FALSE(none > opt(1)); |
511 | EXPECT_TRUE(opt(1) > none); |
512 | |
513 | EXPECT_TRUE(opt() <= none); |
514 | EXPECT_TRUE(none <= opt()); |
515 | EXPECT_FALSE(opt(1) <= none); |
516 | EXPECT_TRUE(none <= opt(1)); |
517 | |
518 | EXPECT_TRUE(opt() >= none); |
519 | EXPECT_TRUE(none >= opt()); |
520 | EXPECT_TRUE(opt(1) >= none); |
521 | EXPECT_FALSE(none >= opt(1)); |
522 | } |
523 | |
524 | TEST(Optional, Conversions) { |
525 | Optional<bool> mbool; |
526 | Optional<short> mshort; |
527 | Optional<char*> mstr; |
528 | Optional<int> mint; |
529 | |
530 | // These don't compile |
531 | // bool b = mbool; |
532 | // short s = mshort; |
533 | // char* c = mstr; |
534 | // int x = mint; |
535 | // char* c(mstr); |
536 | // short s(mshort); |
537 | // int x(mint); |
538 | |
539 | // intended explicit operator bool, for if (opt). |
540 | bool b(mbool); |
541 | EXPECT_FALSE(b); |
542 | |
543 | // Truthy tests work and are not ambiguous |
544 | if (mbool && mshort && mstr && mint) { // only checks not-empty |
545 | if (*mbool && *mshort && *mstr && *mint) { // only checks value |
546 | ; |
547 | } |
548 | } |
549 | |
550 | mbool = false; |
551 | EXPECT_TRUE(bool(mbool)); |
552 | EXPECT_FALSE(*mbool); |
553 | |
554 | mbool = true; |
555 | EXPECT_TRUE(bool(mbool)); |
556 | EXPECT_TRUE(*mbool); |
557 | |
558 | mbool = none; |
559 | EXPECT_FALSE(bool(mbool)); |
560 | |
561 | // No conversion allowed; does not compile |
562 | // EXPECT_TRUE(mbool == false); |
563 | } |
564 | |
565 | TEST(Optional, Pointee) { |
566 | Optional<int> x; |
567 | EXPECT_FALSE(get_pointer(x)); |
568 | x = 1; |
569 | EXPECT_TRUE(get_pointer(x)); |
570 | *get_pointer(x) = 2; |
571 | EXPECT_TRUE(*x == 2); |
572 | x = none; |
573 | EXPECT_FALSE(get_pointer(x)); |
574 | } |
575 | |
576 | namespace { |
577 | class ConstructibleWithArgsOnly { |
578 | public: |
579 | explicit ConstructibleWithArgsOnly(int, double) {} |
580 | |
581 | ConstructibleWithArgsOnly() = delete; |
582 | ConstructibleWithArgsOnly(const ConstructibleWithArgsOnly&) = delete; |
583 | ConstructibleWithArgsOnly(ConstructibleWithArgsOnly&&) = delete; |
584 | ConstructibleWithArgsOnly& operator=(const ConstructibleWithArgsOnly&) = |
585 | delete; |
586 | ConstructibleWithArgsOnly& operator=(ConstructibleWithArgsOnly&&) = delete; |
587 | }; |
588 | |
589 | class ConstructibleWithInitializerListAndArgsOnly { |
590 | public: |
591 | ConstructibleWithInitializerListAndArgsOnly(std::initializer_list<int>, int) { |
592 | } |
593 | |
594 | ConstructibleWithInitializerListAndArgsOnly() = delete; |
595 | ConstructibleWithInitializerListAndArgsOnly( |
596 | const ConstructibleWithInitializerListAndArgsOnly&) = delete; |
597 | ConstructibleWithInitializerListAndArgsOnly( |
598 | ConstructibleWithInitializerListAndArgsOnly&&) = delete; |
599 | ConstructibleWithInitializerListAndArgsOnly& operator=( |
600 | const ConstructibleWithInitializerListAndArgsOnly&) = delete; |
601 | ConstructibleWithInitializerListAndArgsOnly& operator=( |
602 | ConstructibleWithInitializerListAndArgsOnly&&) = delete; |
603 | }; |
604 | } // namespace |
605 | |
606 | TEST(Optional, MakeOptional) { |
607 | // const L-value version |
608 | const std::string s("abc" ); |
609 | auto optStr = folly::make_optional(s); |
610 | ASSERT_TRUE(optStr.hasValue()); |
611 | EXPECT_EQ(*optStr, "abc" ); |
612 | *optStr = "cde" ; |
613 | EXPECT_EQ(s, "abc" ); |
614 | EXPECT_EQ(*optStr, "cde" ); |
615 | |
616 | // L-value version |
617 | std::string s2("abc" ); |
618 | auto optStr2 = folly::make_optional(s2); |
619 | ASSERT_TRUE(optStr2.hasValue()); |
620 | EXPECT_EQ(*optStr2, "abc" ); |
621 | *optStr2 = "cde" ; |
622 | // it's vital to check that s2 wasn't clobbered |
623 | EXPECT_EQ(s2, "abc" ); |
624 | |
625 | // L-value reference version |
626 | std::string& s3(s2); |
627 | auto optStr3 = folly::make_optional(s3); |
628 | ASSERT_TRUE(optStr3.hasValue()); |
629 | EXPECT_EQ(*optStr3, "abc" ); |
630 | *optStr3 = "cde" ; |
631 | EXPECT_EQ(s3, "abc" ); |
632 | |
633 | // R-value ref version |
634 | unique_ptr<int> pInt(new int(3)); |
635 | auto optIntPtr = folly::make_optional(std::move(pInt)); |
636 | EXPECT_TRUE(pInt.get() == nullptr); |
637 | ASSERT_TRUE(optIntPtr.hasValue()); |
638 | EXPECT_EQ(**optIntPtr, 3); |
639 | |
640 | // variadic version |
641 | { |
642 | auto&& optional = make_optional<ConstructibleWithArgsOnly>(int{}, double{}); |
643 | std::ignore = optional; |
644 | } |
645 | { |
646 | using Type = ConstructibleWithInitializerListAndArgsOnly; |
647 | auto&& optional = make_optional<Type>({int{}}, double{}); |
648 | std::ignore = optional; |
649 | } |
650 | } |
651 | |
652 | TEST(Optional, InitializerListConstruct) { |
653 | using Type = ConstructibleWithInitializerListAndArgsOnly; |
654 | auto&& optional = Optional<Type>{in_place, {int{}}, double{}}; |
655 | std::ignore = optional; |
656 | } |
657 | |
658 | TEST(Optional, TestDisambiguationMakeOptionalVariants) { |
659 | { |
660 | auto optional = make_optional<int>(1); |
661 | std::ignore = optional; |
662 | } |
663 | { |
664 | auto optional = make_optional(1); |
665 | std::ignore = optional; |
666 | } |
667 | } |
668 | |
669 | TEST(Optional, SelfAssignment) { |
670 | Optional<int> a = 42; |
671 | a = static_cast<decltype(a)&>(a); // suppress self-assign warning |
672 | ASSERT_TRUE(a.hasValue() && a.value() == 42); |
673 | |
674 | Optional<int> b = 23333333; |
675 | b = static_cast<decltype(b)&&>(b); // suppress self-move warning |
676 | ASSERT_TRUE(b.hasValue() && b.value() == 23333333); |
677 | } |
678 | |
679 | namespace { |
680 | |
681 | class ContainsOptional { |
682 | public: |
683 | ContainsOptional() {} |
684 | explicit ContainsOptional(int x) : opt_(x) {} |
685 | bool hasValue() const { |
686 | return opt_.hasValue(); |
687 | } |
688 | int value() const { |
689 | return opt_.value(); |
690 | } |
691 | |
692 | ContainsOptional(const ContainsOptional& other) = default; |
693 | ContainsOptional& operator=(const ContainsOptional& other) = default; |
694 | ContainsOptional(ContainsOptional&& other) = default; |
695 | ContainsOptional& operator=(ContainsOptional&& other) = default; |
696 | |
697 | private: |
698 | Optional<int> opt_; |
699 | }; |
700 | |
701 | } // namespace |
702 | |
703 | /** |
704 | * Test that a class containing an Optional can be copy and move assigned. |
705 | * This was broken under gcc 4.7 until assignment operators were explicitly |
706 | * defined. |
707 | */ |
708 | TEST(Optional, AssignmentContained) { |
709 | { |
710 | ContainsOptional source(5), target; |
711 | target = source; |
712 | EXPECT_TRUE(target.hasValue()); |
713 | EXPECT_EQ(5, target.value()); |
714 | } |
715 | |
716 | { |
717 | ContainsOptional source(5), target; |
718 | target = std::move(source); |
719 | EXPECT_TRUE(target.hasValue()); |
720 | EXPECT_EQ(5, target.value()); |
721 | EXPECT_FALSE(source.hasValue()); |
722 | } |
723 | |
724 | { |
725 | ContainsOptional opt_uninit, target(10); |
726 | target = opt_uninit; |
727 | EXPECT_FALSE(target.hasValue()); |
728 | } |
729 | } |
730 | |
731 | TEST(Optional, Exceptions) { |
732 | Optional<int> empty; |
733 | EXPECT_THROW(empty.value(), OptionalEmptyException); |
734 | } |
735 | |
736 | TEST(Optional, NoThrowDefaultConstructible) { |
737 | EXPECT_TRUE(std::is_nothrow_default_constructible<Optional<bool>>::value); |
738 | } |
739 | |
740 | namespace { |
741 | |
742 | struct NoDestructor {}; |
743 | |
744 | struct WithDestructor { |
745 | ~WithDestructor(); |
746 | }; |
747 | |
748 | } // namespace |
749 | |
750 | TEST(Optional, TriviallyDestructible) { |
751 | // These could all be static_asserts but EXPECT_* give much nicer output on |
752 | // failure. |
753 | EXPECT_TRUE(std::is_trivially_destructible<Optional<NoDestructor>>::value); |
754 | EXPECT_TRUE(std::is_trivially_destructible<Optional<int>>::value); |
755 | EXPECT_FALSE(std::is_trivially_destructible<Optional<WithDestructor>>::value); |
756 | } |
757 | |
758 | TEST(Optional, Hash) { |
759 | // Test it's usable in std::unordered map (compile time check) |
760 | std::unordered_map<Optional<int>, Optional<int>> obj; |
761 | // Also check the std::hash template can be instantiated by the compiler |
762 | std::hash<Optional<int>>()(none); |
763 | std::hash<Optional<int>>()(3); |
764 | } |
765 | |
766 | namespace { |
767 | |
768 | struct WithConstMember { |
769 | /* implicit */ WithConstMember(int val) : x(val) {} |
770 | const int x; |
771 | }; |
772 | |
773 | // Make this opaque to the optimizer by preventing inlining. |
774 | FOLLY_NOINLINE void replaceWith2(Optional<WithConstMember>& o) { |
775 | o.emplace(2); |
776 | } |
777 | |
778 | } // namespace |
779 | |
780 | TEST(Optional, ConstMember) { |
781 | // Verify that the compiler doesn't optimize out the second load of |
782 | // o->x based on the assumption that the field is const. |
783 | // |
784 | // Current Optional implementation doesn't defend against that |
785 | // assumption, thus replacing an optional where the object has const |
786 | // members is technically UB and would require wrapping each access |
787 | // to the storage with std::launder, but this prevents useful |
788 | // optimizations. |
789 | // |
790 | // Implementations of std::optional in both libstdc++ and libc++ are |
791 | // subject to the same UB. It is then reasonable to believe that |
792 | // major compilers don't rely on the constness assumption. |
793 | Optional<WithConstMember> o(1); |
794 | int sum = 0; |
795 | sum += o->x; |
796 | replaceWith2(o); |
797 | sum += o->x; |
798 | EXPECT_EQ(sum, 3); |
799 | } |
800 | |
801 | TEST(Optional, NoneMatchesNullopt) { |
802 | auto op = make_optional<int>(10); |
803 | op = {}; |
804 | EXPECT_FALSE(op.has_value()); |
805 | |
806 | op = make_optional<int>(20); |
807 | op = none; |
808 | EXPECT_FALSE(op.has_value()); |
809 | } |
810 | |
811 | } // namespace folly |
812 | |