1 #include <iostream>
2 #include <type_traits>
3 #include <vector>
4 
5 #include <cm/optional>
6 #include <cm/utility>
7 
8 class EventLogger;
9 
10 class Event
11 {
12 public:
13   enum EventType
14   {
15     DEFAULT_CONSTRUCT,
16     COPY_CONSTRUCT,
17     MOVE_CONSTRUCT,
18     VALUE_CONSTRUCT,
19 
20     DESTRUCT,
21 
22     COPY_ASSIGN,
23     MOVE_ASSIGN,
24     VALUE_ASSIGN,
25 
26     REFERENCE,
27     CONST_REFERENCE,
28     RVALUE_REFERENCE,
29     CONST_RVALUE_REFERENCE,
30 
31     SWAP,
32 
33     COMPARE_EE_EQ,
34     COMPARE_EE_NE,
35     COMPARE_EE_LT,
36     COMPARE_EE_LE,
37     COMPARE_EE_GT,
38     COMPARE_EE_GE,
39   };
40 
41   EventType Type;
42   const EventLogger* Logger1;
43   const EventLogger* Logger2;
44   int Value;
45 
46   bool operator==(const Event& other) const;
47   bool operator!=(const Event& other) const;
48 };
49 
operator ==(const Event & other) const50 bool Event::operator==(const Event& other) const
51 {
52   return this->Type == other.Type && this->Logger1 == other.Logger1 &&
53     this->Logger2 == other.Logger2 && this->Value == other.Value;
54 }
55 
operator !=(const Event & other) const56 bool Event::operator!=(const Event& other) const
57 {
58   return !(*this == other);
59 }
60 
61 static std::vector<Event> events;
62 
63 class EventLogger
64 {
65 public:
66   EventLogger();
67   EventLogger(const EventLogger& other);
68   EventLogger(EventLogger&& other);
69   EventLogger(int value);
70 
71   ~EventLogger();
72 
73   EventLogger& operator=(const EventLogger& other);
74   EventLogger& operator=(EventLogger&& other);
75   EventLogger& operator=(int value);
76 
77   void Reference() &;
78   void Reference() const&;
79   void Reference() &&;
80   void Reference() const&&;
81 
82   int Value = 0;
83 };
84 
85 class NoMoveAssignEventLogger : public EventLogger
86 {
87 public:
88   using EventLogger::EventLogger;
89 
90   NoMoveAssignEventLogger(const NoMoveAssignEventLogger&) = default;
91   NoMoveAssignEventLogger(NoMoveAssignEventLogger&&) = default;
92 
93   NoMoveAssignEventLogger& operator=(const NoMoveAssignEventLogger&) = default;
94   NoMoveAssignEventLogger& operator=(NoMoveAssignEventLogger&&) = delete;
95 };
96 
97 #define ASSERT_TRUE(x)                                                        \
98   do {                                                                        \
99     if (!(x)) {                                                               \
100       std::cout << "ASSERT_TRUE(" #x ") failed on line " << __LINE__ << "\n"; \
101       return false;                                                           \
102     }                                                                         \
103   } while (false)
104 
105 // Certain builds of GCC generate false -Wmaybe-uninitialized warnings when
106 // doing a release build with the system version of std::optional. These
107 // warnings do not manifest when using our own cm::optional implementation.
108 // Silence these false warnings.
109 #if defined(__GNUC__) && !defined(__clang__)
110 #  define BEGIN_IGNORE_UNINITIALIZED                                          \
111     _Pragma("GCC diagnostic push")                                            \
112       _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
113 #  define END_IGNORE_UNINITIALIZED _Pragma("GCC diagnostic pop")
114 #else
115 #  define BEGIN_IGNORE_UNINITIALIZED
116 #  define END_IGNORE_UNINITIALIZED
117 #endif
118 
swap(EventLogger & e1,EventLogger & e2)119 void swap(EventLogger& e1, EventLogger& e2)
120 {
121   BEGIN_IGNORE_UNINITIALIZED
122   events.push_back({ Event::SWAP, &e1, &e2, e2.Value });
123   END_IGNORE_UNINITIALIZED
124   auto tmp = e1.Value;
125   e1.Value = e2.Value;
126   e2.Value = tmp;
127 }
128 
EventLogger()129 EventLogger::EventLogger()
130   : Value(0)
131 {
132   events.push_back({ Event::DEFAULT_CONSTRUCT, this, nullptr, 0 });
133 }
134 
EventLogger(const EventLogger & other)135 EventLogger::EventLogger(const EventLogger& other)
136   : Value(other.Value)
137 {
138   events.push_back({ Event::COPY_CONSTRUCT, this, &other, other.Value });
139 }
140 
141 BEGIN_IGNORE_UNINITIALIZED
EventLogger(EventLogger && other)142 EventLogger::EventLogger(EventLogger&& other)
143   : Value(other.Value)
144 {
145   events.push_back({ Event::MOVE_CONSTRUCT, this, &other, other.Value });
146 }
147 END_IGNORE_UNINITIALIZED
148 
EventLogger(int value)149 EventLogger::EventLogger(int value)
150   : Value(value)
151 {
152   events.push_back({ Event::VALUE_CONSTRUCT, this, nullptr, value });
153 }
154 
~EventLogger()155 EventLogger::~EventLogger()
156 {
157   BEGIN_IGNORE_UNINITIALIZED
158   events.push_back({ Event::DESTRUCT, this, nullptr, this->Value });
159   END_IGNORE_UNINITIALIZED
160 }
161 
operator =(const EventLogger & other)162 EventLogger& EventLogger::operator=(const EventLogger& other)
163 {
164   events.push_back({ Event::COPY_ASSIGN, this, &other, other.Value });
165   this->Value = other.Value;
166   return *this;
167 }
168 
operator =(EventLogger && other)169 EventLogger& EventLogger::operator=(EventLogger&& other)
170 {
171   events.push_back({ Event::MOVE_ASSIGN, this, &other, other.Value });
172   this->Value = other.Value;
173   return *this;
174 }
175 
operator =(int value)176 EventLogger& EventLogger::operator=(int value)
177 {
178   events.push_back({ Event::VALUE_ASSIGN, this, nullptr, value });
179   this->Value = value;
180   return *this;
181 }
182 
operator ==(const EventLogger & lhs,const EventLogger & rhs)183 bool operator==(const EventLogger& lhs, const EventLogger& rhs)
184 {
185   events.push_back({ Event::COMPARE_EE_EQ, &lhs, &rhs, lhs.Value });
186   return lhs.Value == rhs.Value;
187 }
188 
operator !=(const EventLogger & lhs,const EventLogger & rhs)189 bool operator!=(const EventLogger& lhs, const EventLogger& rhs)
190 {
191   events.push_back({ Event::COMPARE_EE_NE, &lhs, &rhs, lhs.Value });
192   return lhs.Value != rhs.Value;
193 }
194 
operator <(const EventLogger & lhs,const EventLogger & rhs)195 bool operator<(const EventLogger& lhs, const EventLogger& rhs)
196 {
197   events.push_back({ Event::COMPARE_EE_LT, &lhs, &rhs, lhs.Value });
198   return lhs.Value < rhs.Value;
199 }
200 
operator <=(const EventLogger & lhs,const EventLogger & rhs)201 bool operator<=(const EventLogger& lhs, const EventLogger& rhs)
202 {
203   events.push_back({ Event::COMPARE_EE_LE, &lhs, &rhs, lhs.Value });
204   return lhs.Value <= rhs.Value;
205 }
206 
operator >(const EventLogger & lhs,const EventLogger & rhs)207 bool operator>(const EventLogger& lhs, const EventLogger& rhs)
208 {
209   events.push_back({ Event::COMPARE_EE_GT, &lhs, &rhs, lhs.Value });
210   return lhs.Value > rhs.Value;
211 }
212 
operator >=(const EventLogger & lhs,const EventLogger & rhs)213 bool operator>=(const EventLogger& lhs, const EventLogger& rhs)
214 {
215   events.push_back({ Event::COMPARE_EE_GE, &lhs, &rhs, lhs.Value });
216   return lhs.Value >= rhs.Value;
217 }
218 
Reference()219 void EventLogger::Reference() &
220 {
221   events.push_back({ Event::REFERENCE, this, nullptr, this->Value });
222 }
223 
Reference() const224 void EventLogger::Reference() const&
225 {
226   events.push_back({ Event::CONST_REFERENCE, this, nullptr, this->Value });
227 }
228 
Reference()229 void EventLogger::Reference() &&
230 {
231   events.push_back({ Event::RVALUE_REFERENCE, this, nullptr, this->Value });
232 }
233 
Reference() const234 void EventLogger::Reference() const&&
235 {
236   events.push_back(
237     { Event::CONST_RVALUE_REFERENCE, this, nullptr, this->Value });
238 }
239 
testDefaultConstruct(std::vector<Event> & expected)240 static bool testDefaultConstruct(std::vector<Event>& expected)
241 {
242   const cm::optional<EventLogger> o{};
243 
244   expected = {};
245   return true;
246 }
247 
testNulloptConstruct(std::vector<Event> & expected)248 static bool testNulloptConstruct(std::vector<Event>& expected)
249 {
250   const cm::optional<EventLogger> o{ cm::nullopt };
251 
252   expected = {};
253   return true;
254 }
255 
testValueConstruct(std::vector<Event> & expected)256 static bool testValueConstruct(std::vector<Event>& expected)
257 {
258   const cm::optional<EventLogger> o{ 4 };
259 
260   expected = {
261     { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
262     { Event::DESTRUCT, &*o, nullptr, 4 },
263   };
264   return true;
265 }
266 
testInPlaceConstruct(std::vector<Event> & expected)267 static bool testInPlaceConstruct(std::vector<Event>& expected)
268 {
269   const cm::optional<EventLogger> o1{ cm::in_place, 4 };
270   const cm::optional<EventLogger> o2{ cm::in_place_t{}, 4 };
271 
272   expected = {
273     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
274     { Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
275     { Event::DESTRUCT, &*o2, nullptr, 4 },
276     { Event::DESTRUCT, &*o1, nullptr, 4 },
277   };
278   return true;
279 }
280 
testCopyConstruct(std::vector<Event> & expected)281 static bool testCopyConstruct(std::vector<Event>& expected)
282 {
283   const cm::optional<EventLogger> o1{ 4 };
284   const cm::optional<EventLogger> o2{ o1 };
285   const cm::optional<EventLogger> o3{};
286   const cm::optional<EventLogger> o4{ o3 };
287 
288   expected = {
289     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
290     { Event::COPY_CONSTRUCT, &*o2, &o1.value(), 4 },
291     { Event::DESTRUCT, &*o2, nullptr, 4 },
292     { Event::DESTRUCT, &*o1, nullptr, 4 },
293   };
294   return true;
295 }
296 
testMoveConstruct(std::vector<Event> & expected)297 static bool testMoveConstruct(std::vector<Event>& expected)
298 {
299   cm::optional<EventLogger> o1{ 4 };
300   const cm::optional<EventLogger> o2{ std::move(o1) };
301   cm::optional<EventLogger> o3{};
302   const cm::optional<EventLogger> o4{ std::move(o3) };
303 
304 #ifndef __clang_analyzer__ /* cplusplus.Move */
305   expected = {
306     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
307     { Event::MOVE_CONSTRUCT, &*o2, &*o1, 4 },
308     { Event::DESTRUCT, &*o2, nullptr, 4 },
309     { Event::DESTRUCT, &*o1, nullptr, 4 },
310   };
311 #endif
312   return true;
313 }
314 
testNulloptAssign(std::vector<Event> & expected)315 static bool testNulloptAssign(std::vector<Event>& expected)
316 {
317   cm::optional<EventLogger> o1{ 4 };
318   auto const* v1 = &*o1;
319   o1 = cm::nullopt;
320   cm::optional<EventLogger> o2{};
321   o2 = cm::nullopt;
322 
323   expected = {
324     { Event::VALUE_CONSTRUCT, v1, nullptr, 4 },
325     { Event::DESTRUCT, v1, nullptr, 4 },
326   };
327   return true;
328 }
329 
testCopyAssign(std::vector<Event> & expected)330 static bool testCopyAssign(std::vector<Event>& expected)
331 {
332   cm::optional<EventLogger> o1{};
333   const cm::optional<EventLogger> o2{ 4 };
334   auto const* v2 = &*o2;
335   o1 = o2;
336   auto const* v1 = &*o1;
337   const cm::optional<EventLogger> o3{ 5 };
338   auto const* v3 = &*o3;
339   o1 = o3;
340   const cm::optional<EventLogger> o4{};
341   o1 = o4;
342   o1 = o4; // Intentionally duplicated to test assigning an empty optional to
343   // an empty optional
344 
345   cm::optional<NoMoveAssignEventLogger> o5{ 1 };
346   auto const* v5 = &*o5;
347   const cm::optional<NoMoveAssignEventLogger> o6{ 2 };
348   auto const* v6 = &*o6;
349   o5 = std::move(o6);
350   const NoMoveAssignEventLogger e7{ 3 };
351   o5 = std::move(e7);
352 
353   expected = {
354     { Event::VALUE_CONSTRUCT, v2, nullptr, 4 },
355     { Event::COPY_CONSTRUCT, v1, v2, 4 },
356     { Event::VALUE_CONSTRUCT, v3, nullptr, 5 },
357     { Event::COPY_ASSIGN, v1, v3, 5 },
358     { Event::DESTRUCT, v1, nullptr, 5 },
359     { Event::VALUE_CONSTRUCT, v5, nullptr, 1 },
360     { Event::VALUE_CONSTRUCT, v6, nullptr, 2 },
361     { Event::COPY_ASSIGN, v5, v6, 2 },
362     { Event::VALUE_CONSTRUCT, &e7, nullptr, 3 },
363     { Event::COPY_ASSIGN, v5, &e7, 3 },
364     { Event::DESTRUCT, &e7, nullptr, 3 },
365     { Event::DESTRUCT, v6, nullptr, 2 },
366     { Event::DESTRUCT, v5, nullptr, 3 },
367     { Event::DESTRUCT, v3, nullptr, 5 },
368     { Event::DESTRUCT, v2, nullptr, 4 },
369   };
370   return true;
371 }
372 
testMoveAssign(std::vector<Event> & expected)373 static bool testMoveAssign(std::vector<Event>& expected)
374 {
375   cm::optional<EventLogger> o1{};
376   cm::optional<EventLogger> o2{ 4 };
377   auto const* v2 = &*o2;
378   o1 = std::move(o2);
379   auto const* v1 = &*o1;
380   cm::optional<EventLogger> o3{ 5 };
381   auto const* v3 = &*o3;
382   o1 = std::move(o3);
383   cm::optional<EventLogger> o4{};
384   o1 = std::move(o4);
385 
386   expected = {
387     { Event::VALUE_CONSTRUCT, v2, nullptr, 4 },
388     { Event::MOVE_CONSTRUCT, v1, v2, 4 },
389     { Event::VALUE_CONSTRUCT, v3, nullptr, 5 },
390     { Event::MOVE_ASSIGN, v1, v3, 5 },
391     { Event::DESTRUCT, v1, nullptr, 5 },
392     { Event::DESTRUCT, v3, nullptr, 5 },
393     { Event::DESTRUCT, v2, nullptr, 4 },
394   };
395   return true;
396 }
397 
testPointer(std::vector<Event> & expected)398 static bool testPointer(std::vector<Event>& expected)
399 {
400   cm::optional<EventLogger> o1{ 4 };
401   const cm::optional<EventLogger> o2{ 5 };
402 
403   o1->Reference();
404   o2->Reference();
405 
406   expected = {
407     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
408     { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
409     { Event::REFERENCE, &*o1, nullptr, 4 },
410     { Event::CONST_REFERENCE, &*o2, nullptr, 5 },
411     { Event::DESTRUCT, &*o2, nullptr, 5 },
412     { Event::DESTRUCT, &*o1, nullptr, 4 },
413   };
414   return true;
415 }
416 
417 #if !__GNUC__ || __GNUC__ > 4
418 #  define ALLOW_CONST_RVALUE
419 #endif
420 
testDereference(std::vector<Event> & expected)421 static bool testDereference(std::vector<Event>& expected)
422 {
423   cm::optional<EventLogger> o1{ 4 };
424   auto const* v1 = &*o1;
425   const cm::optional<EventLogger> o2{ 5 };
426   auto const* v2 = &*o2;
427 
428   (*o1).Reference();
429   (*o2).Reference();
430   (*std::move(o1)).Reference();
431 #ifdef ALLOW_CONST_RVALUE
432   (*std::move(o2)).Reference(); // Broken in GCC 4.9.0. Sigh...
433 #endif
434 
435   expected = {
436     { Event::VALUE_CONSTRUCT, v1, nullptr, 4 },
437     { Event::VALUE_CONSTRUCT, v2, nullptr, 5 },
438     { Event::REFERENCE, v1, nullptr, 4 },
439     { Event::CONST_REFERENCE, v2, nullptr, 5 },
440     { Event::RVALUE_REFERENCE, v1, nullptr, 4 },
441 #ifdef ALLOW_CONST_RVALUE
442     { Event::CONST_RVALUE_REFERENCE, v2, nullptr, 5 },
443 #endif
444     { Event::DESTRUCT, v2, nullptr, 5 },
445     { Event::DESTRUCT, v1, nullptr, 4 },
446   };
447   return true;
448 }
449 
testHasValue(std::vector<Event> & expected)450 static bool testHasValue(std::vector<Event>& expected)
451 {
452   const cm::optional<EventLogger> o1{ 4 };
453   const cm::optional<EventLogger> o2{};
454 
455   ASSERT_TRUE(o1.has_value());
456   ASSERT_TRUE(o1);
457   ASSERT_TRUE(!o2.has_value());
458   ASSERT_TRUE(!o2);
459 
460   expected = {
461     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
462     { Event::DESTRUCT, &*o1, nullptr, 4 },
463   };
464   return true;
465 }
466 
testValue(std::vector<Event> & expected)467 static bool testValue(std::vector<Event>& expected)
468 {
469   cm::optional<EventLogger> o1{ 4 };
470   const cm::optional<EventLogger> o2{ 5 };
471   cm::optional<EventLogger> o3{};
472   const cm::optional<EventLogger> o4{};
473 
474   o1.value().Reference();
475   o2.value().Reference();
476 
477   bool thrown = false;
478   try {
479     (void)o3.value();
480   } catch (cm::bad_optional_access&) {
481     thrown = true;
482   }
483   ASSERT_TRUE(thrown);
484 
485   thrown = false;
486   try {
487     (void)o4.value();
488   } catch (cm::bad_optional_access&) {
489     thrown = true;
490   }
491   ASSERT_TRUE(thrown);
492 
493   expected = {
494     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
495     { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
496     { Event::REFERENCE, &*o1, nullptr, 4 },
497     { Event::CONST_REFERENCE, &*o2, nullptr, 5 },
498     { Event::DESTRUCT, &*o2, nullptr, 5 },
499     { Event::DESTRUCT, &*o1, nullptr, 4 },
500   };
501   return true;
502 }
503 
testValueOr()504 static bool testValueOr()
505 {
506   const cm::optional<EventLogger> o1{ 4 };
507   cm::optional<EventLogger> o2{ 5 };
508   const cm::optional<EventLogger> o3{};
509   cm::optional<EventLogger> o4{};
510 
511   EventLogger e1{ 6 };
512   EventLogger e2{ 7 };
513   EventLogger e3{ 8 };
514   EventLogger e4{ 9 };
515 
516   EventLogger r1 = o1.value_or(e1);
517   ASSERT_TRUE(r1.Value == 4);
518   EventLogger r2 = std::move(o2).value_or(e2);
519   ASSERT_TRUE(r2.Value == 5);
520   EventLogger r3 = o3.value_or(e3);
521   ASSERT_TRUE(r3.Value == 8);
522   EventLogger r4 = std::move(o4).value_or(e4);
523   ASSERT_TRUE(r4.Value == 9);
524 
525   return true;
526 }
527 
testComparison(std::vector<Event> & expected)528 static bool testComparison(std::vector<Event>& expected)
529 {
530   const cm::optional<EventLogger> o1{ 1 };
531   const cm::optional<EventLogger> o2{ 2 };
532   const cm::optional<EventLogger> o3{ 2 };
533   const cm::optional<EventLogger> o4{};
534   const cm::optional<EventLogger> o5{};
535   const EventLogger e1{ 2 };
536 
537   ASSERT_TRUE(!(o1 == o2) && o1 != o2);
538   ASSERT_TRUE(o1 < o2 && !(o1 >= o2));
539   ASSERT_TRUE(!(o1 > o2) && o1 <= o2);
540 
541   ASSERT_TRUE(o2 == o3 && !(o2 != o3));
542   ASSERT_TRUE(!(o2 < o3) && o2 >= o3);
543   ASSERT_TRUE(!(o2 > o3) && o2 <= o3);
544 
545   ASSERT_TRUE(!(o3 == o4) && o3 != o4);
546   ASSERT_TRUE(!(o3 < o4) && o3 >= o4);
547   ASSERT_TRUE(o3 > o4 && !(o3 <= o4));
548 
549   ASSERT_TRUE(o4 == o5 && !(o4 != o5));
550   ASSERT_TRUE(!(o4 < o5) && o4 >= o5);
551   ASSERT_TRUE(!(o4 > o5) && o4 <= o5);
552 
553   ASSERT_TRUE(!(o1 == cm::nullopt) && o1 != cm::nullopt);
554   ASSERT_TRUE(!(o1 < cm::nullopt) && o1 >= cm::nullopt);
555   ASSERT_TRUE(o1 > cm::nullopt && !(o1 <= cm::nullopt));
556 
557   ASSERT_TRUE(!(cm::nullopt == o1) && cm::nullopt != o1);
558   ASSERT_TRUE(cm::nullopt < o1 && !(cm::nullopt >= o1));
559   ASSERT_TRUE(!(cm::nullopt > o1) && cm::nullopt <= o1);
560 
561   ASSERT_TRUE(o4 == cm::nullopt && !(o4 != cm::nullopt));
562   ASSERT_TRUE(!(o4 < cm::nullopt) && o4 >= cm::nullopt);
563   ASSERT_TRUE(!(o4 > cm::nullopt) && o4 <= cm::nullopt);
564 
565   ASSERT_TRUE(cm::nullopt == o4 && !(cm::nullopt != o4));
566   ASSERT_TRUE(!(cm::nullopt < o4) && cm::nullopt >= o4);
567   ASSERT_TRUE(!(cm::nullopt > o4) && cm::nullopt <= o4);
568 
569   ASSERT_TRUE(!(o1 == e1) && o1 != e1);
570   ASSERT_TRUE(o1 < e1 && !(o1 >= e1));
571   ASSERT_TRUE(!(o1 > e1) && o1 <= e1);
572 
573   ASSERT_TRUE(o2 == e1 && !(o2 != e1));
574   ASSERT_TRUE(!(o2 < e1) && o2 >= e1);
575   ASSERT_TRUE(!(o2 > e1) && o2 <= e1);
576 
577   ASSERT_TRUE(!(o4 == e1) && o4 != e1);
578   ASSERT_TRUE(o4 < e1 && !(o4 >= e1));
579   ASSERT_TRUE(!(o4 > e1) && o4 <= e1);
580 
581   ASSERT_TRUE(!(e1 == o1) && e1 != o1);
582   ASSERT_TRUE(!(e1 < o1) && e1 >= o1);
583   ASSERT_TRUE(e1 > o1 && !(e1 <= o1));
584 
585   ASSERT_TRUE(e1 == o2 && !(e1 != o2));
586   ASSERT_TRUE(!(e1 < o2) && e1 >= o2);
587   ASSERT_TRUE(!(e1 > o2) && e1 <= o2);
588 
589   ASSERT_TRUE(!(e1 == o4) && e1 != o4);
590   ASSERT_TRUE(!(e1 < o4) && e1 >= o4);
591   ASSERT_TRUE(e1 > o4 && !(e1 <= o4));
592 
593   expected = {
594     { Event::VALUE_CONSTRUCT, &*o1, nullptr, 1 },
595     { Event::VALUE_CONSTRUCT, &*o2, nullptr, 2 },
596     { Event::VALUE_CONSTRUCT, &*o3, nullptr, 2 },
597     { Event::VALUE_CONSTRUCT, &e1, nullptr, 2 },
598     { Event::COMPARE_EE_EQ, &*o1, &*o2, 1 },
599     { Event::COMPARE_EE_NE, &*o1, &*o2, 1 },
600     { Event::COMPARE_EE_LT, &*o1, &*o2, 1 },
601     { Event::COMPARE_EE_GE, &*o1, &*o2, 1 },
602     { Event::COMPARE_EE_GT, &*o1, &*o2, 1 },
603     { Event::COMPARE_EE_LE, &*o1, &*o2, 1 },
604     { Event::COMPARE_EE_EQ, &*o2, &*o3, 2 },
605     { Event::COMPARE_EE_NE, &*o2, &*o3, 2 },
606     { Event::COMPARE_EE_LT, &*o2, &*o3, 2 },
607     { Event::COMPARE_EE_GE, &*o2, &*o3, 2 },
608     { Event::COMPARE_EE_GT, &*o2, &*o3, 2 },
609     { Event::COMPARE_EE_LE, &*o2, &*o3, 2 },
610     { Event::COMPARE_EE_EQ, &*o1, &e1, 1 },
611     { Event::COMPARE_EE_NE, &*o1, &e1, 1 },
612     { Event::COMPARE_EE_LT, &*o1, &e1, 1 },
613     { Event::COMPARE_EE_GE, &*o1, &e1, 1 },
614     { Event::COMPARE_EE_GT, &*o1, &e1, 1 },
615     { Event::COMPARE_EE_LE, &*o1, &e1, 1 },
616     { Event::COMPARE_EE_EQ, &*o2, &e1, 2 },
617     { Event::COMPARE_EE_NE, &*o2, &e1, 2 },
618     { Event::COMPARE_EE_LT, &*o2, &e1, 2 },
619     { Event::COMPARE_EE_GE, &*o2, &e1, 2 },
620     { Event::COMPARE_EE_GT, &*o2, &e1, 2 },
621     { Event::COMPARE_EE_LE, &*o2, &e1, 2 },
622     { Event::COMPARE_EE_EQ, &e1, &*o1, 2 },
623     { Event::COMPARE_EE_NE, &e1, &*o1, 2 },
624     { Event::COMPARE_EE_LT, &e1, &*o1, 2 },
625     { Event::COMPARE_EE_GE, &e1, &*o1, 2 },
626     { Event::COMPARE_EE_GT, &e1, &*o1, 2 },
627     { Event::COMPARE_EE_LE, &e1, &*o1, 2 },
628     { Event::COMPARE_EE_EQ, &e1, &*o2, 2 },
629     { Event::COMPARE_EE_NE, &e1, &*o2, 2 },
630     { Event::COMPARE_EE_LT, &e1, &*o2, 2 },
631     { Event::COMPARE_EE_GE, &e1, &*o2, 2 },
632     { Event::COMPARE_EE_GT, &e1, &*o2, 2 },
633     { Event::COMPARE_EE_LE, &e1, &*o2, 2 },
634     { Event::DESTRUCT, &e1, nullptr, 2 },
635     { Event::DESTRUCT, &*o3, nullptr, 2 },
636     { Event::DESTRUCT, &*o2, nullptr, 2 },
637     { Event::DESTRUCT, &*o1, nullptr, 1 },
638   };
639   return true;
640 }
641 
testSwap(std::vector<Event> & expected)642 static bool testSwap(std::vector<Event>& expected)
643 {
644   cm::optional<EventLogger> o1{ 4 };
645   auto const* v1 = &*o1;
646   cm::optional<EventLogger> o2{};
647 
648   o1.swap(o2);
649   auto const* v2 = &*o2;
650 
651   ASSERT_TRUE(!o1.has_value());
652   ASSERT_TRUE(o2.has_value());
653   ASSERT_TRUE(o2.value().Value == 4);
654 
655   o1.swap(o2);
656 
657   ASSERT_TRUE(o1.has_value());
658   ASSERT_TRUE(o1.value().Value == 4);
659   ASSERT_TRUE(!o2.has_value());
660 
661   o2.emplace(5);
662   o1.swap(o2);
663 
664   ASSERT_TRUE(o1.has_value());
665   ASSERT_TRUE(o1.value().Value == 5);
666   ASSERT_TRUE(o2.has_value());
667   ASSERT_TRUE(o2.value().Value == 4);
668 
669   o1.reset();
670   o2.reset();
671   o1.swap(o2);
672 
673   ASSERT_TRUE(!o1.has_value());
674   ASSERT_TRUE(!o2.has_value());
675 
676   expected = {
677     { Event::VALUE_CONSTRUCT, v1, nullptr, 4 },
678     { Event::MOVE_CONSTRUCT, v2, v1, 4 },
679     { Event::DESTRUCT, v1, nullptr, 4 },
680     { Event::MOVE_CONSTRUCT, v1, v2, 4 },
681     { Event::DESTRUCT, v2, nullptr, 4 },
682     { Event::VALUE_CONSTRUCT, v2, nullptr, 5 },
683     { Event::SWAP, v1, v2, 5 },
684     { Event::DESTRUCT, v1, nullptr, 5 },
685     { Event::DESTRUCT, v2, nullptr, 4 },
686   };
687   return true;
688 }
689 
testReset(std::vector<Event> & expected)690 static bool testReset(std::vector<Event>& expected)
691 {
692   cm::optional<EventLogger> o{ 4 };
693   auto const* v = &*o;
694 
695   o.reset();
696 
697   ASSERT_TRUE(!o.has_value());
698 
699   o.reset();
700 
701   expected = {
702     { Event::VALUE_CONSTRUCT, v, nullptr, 4 },
703     { Event::DESTRUCT, v, nullptr, 4 },
704   };
705   return true;
706 }
707 
testEmplace(std::vector<Event> & expected)708 static bool testEmplace(std::vector<Event>& expected)
709 {
710   cm::optional<EventLogger> o{ 4 };
711 
712   o.emplace(5);
713   o.reset();
714   o.emplace();
715 
716   expected = {
717     { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
718     { Event::DESTRUCT, &*o, nullptr, 4 },
719     { Event::VALUE_CONSTRUCT, &*o, nullptr, 5 },
720     { Event::DESTRUCT, &*o, nullptr, 5 },
721     { Event::DEFAULT_CONSTRUCT, &*o, nullptr, 0 },
722     { Event::DESTRUCT, &*o, nullptr, 0 },
723   };
724   return true;
725 }
726 
testMakeOptional(std::vector<Event> & expected)727 static bool testMakeOptional(std::vector<Event>& expected)
728 {
729   EventLogger e{ 4 };
730   cm::optional<EventLogger> o1 = cm::make_optional<EventLogger>(e);
731   cm::optional<EventLogger> o2 = cm::make_optional<EventLogger>(5);
732 
733   expected = {
734     { Event::VALUE_CONSTRUCT, &e, nullptr, 4 },
735     { Event::COPY_CONSTRUCT, &*o1, &e, 4 },
736     { Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
737     { Event::DESTRUCT, &*o2, nullptr, 5 },
738     { Event::DESTRUCT, &*o1, nullptr, 4 },
739     { Event::DESTRUCT, &e, nullptr, 4 },
740   };
741   return true;
742 }
743 
testMemoryRange(std::vector<Event> & expected)744 static bool testMemoryRange(std::vector<Event>& expected)
745 {
746   cm::optional<EventLogger> o{ 4 };
747 
748   auto* ostart = &o;
749   auto* oend = ostart + 1;
750   auto* estart = &o.value();
751   auto* eend = estart + 1;
752 
753   ASSERT_TRUE(static_cast<void*>(estart) >= static_cast<void*>(ostart) &&
754               static_cast<void*>(eend) <= static_cast<void*>(oend));
755 
756   expected = {
757     { Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
758     { Event::DESTRUCT, &*o, nullptr, 4 },
759   };
760   return true;
761 }
762 
testOptional(int,char * [])763 int testOptional(int /*unused*/, char* /*unused*/ [])
764 {
765   int retval = 0;
766 
767 #define DO_EVENT_TEST(name)                                                   \
768   do {                                                                        \
769     events.clear();                                                           \
770     std::vector<Event> expected;                                              \
771     if (!name(expected)) {                                                    \
772       std::cout << "in " #name << std::endl;                                  \
773       retval = 1;                                                             \
774     } else if (expected != events) {                                          \
775       std::cout << #name " did not produce expected events" << std::endl;     \
776       retval = 1;                                                             \
777     }                                                                         \
778   } while (0)
779 
780 #define DO_TEST(name)                                                         \
781   do {                                                                        \
782     if (!name()) {                                                            \
783       std::cout << "in " #name << std::endl;                                  \
784       retval = 1;                                                             \
785     }                                                                         \
786   } while (0)
787 
788   DO_EVENT_TEST(testDefaultConstruct);
789   DO_EVENT_TEST(testNulloptConstruct);
790   DO_EVENT_TEST(testValueConstruct);
791   DO_EVENT_TEST(testInPlaceConstruct);
792   DO_EVENT_TEST(testCopyConstruct);
793   DO_EVENT_TEST(testMoveConstruct);
794   DO_EVENT_TEST(testNulloptAssign);
795   DO_EVENT_TEST(testCopyAssign);
796   DO_EVENT_TEST(testMoveAssign);
797   DO_EVENT_TEST(testPointer);
798   DO_EVENT_TEST(testDereference);
799   DO_EVENT_TEST(testHasValue);
800   DO_EVENT_TEST(testValue);
801   DO_TEST(testValueOr);
802   DO_EVENT_TEST(testComparison);
803   DO_EVENT_TEST(testSwap);
804   DO_EVENT_TEST(testReset);
805   DO_EVENT_TEST(testEmplace);
806   DO_EVENT_TEST(testMakeOptional);
807   DO_EVENT_TEST(testMemoryRange);
808 
809   return retval;
810 }
811