src/zserio/OptionalHolder.h
Line | Count | Source |
1 | | #ifndef ZSERIO_OPTIONAL_HOLDER_H_INC |
2 | | #define ZSERIO_OPTIONAL_HOLDER_H_INC |
3 | | |
4 | | #include <cstddef> |
5 | | #include <type_traits> |
6 | | |
7 | | #include "zserio/CppRuntimeException.h" |
8 | | #include "zserio/NoInit.h" |
9 | | #include "zserio/Types.h" |
10 | | #include "zserio/UniquePtr.h" |
11 | | |
12 | | namespace zserio |
13 | | { |
14 | | |
15 | | /** |
16 | | * Helper type for specification of an unset optional holder. |
17 | | */ |
18 | | struct NullOptType |
19 | | { |
20 | | /** |
21 | | * Explicit constructor from int. |
22 | | * |
23 | | * \see https://en.cppreference.com/w/cpp/utility/optional/nullopt_t |
24 | | */ |
25 | 2 | constexpr explicit NullOptType(int) {} |
26 | | }; |
27 | | |
28 | | /** |
29 | | * Constant used to convenient specification of an unset optional holder. |
30 | | */ |
31 | | constexpr NullOptType NullOpt{int()}; |
32 | | |
33 | | /** |
34 | | * Helper type for specification of in-place construction. |
35 | | */ |
36 | | struct InPlaceT |
37 | | { |
38 | | constexpr explicit InPlaceT() = default; |
39 | | }; |
40 | | |
41 | | /** |
42 | | * Constant used as a marker for in-place construction. |
43 | | */ |
44 | | constexpr InPlaceT InPlace{}; |
45 | | |
46 | | namespace detail |
47 | | { |
48 | | |
49 | | /** |
50 | | * Base class for optional holders. Provides common interface for various types of optional holders. |
51 | | */ |
52 | | template <typename T, typename Derived> |
53 | | class optional_holder_base |
54 | | { |
55 | | public: |
56 | | /** |
57 | | * Operator equality. |
58 | | * |
59 | | * \param other Other holder to compare. |
60 | | * |
61 | | * \return True when the other holder has same value as this. False otherwise. |
62 | | */ |
63 | | bool operator==(const optional_holder_base& other) const |
64 | 31 | { |
65 | 31 | if (this == &other) |
66 | 5 | return true; |
67 | | |
68 | 26 | if (getDerived()->hasValue() != other.getDerived()->hasValue()) |
69 | 5 | return false; |
70 | | |
71 | 21 | if (getDerived()->hasValue()) |
72 | 14 | return get() == other.get(); |
73 | | |
74 | 7 | return true; |
75 | 21 | } |
76 | | |
77 | | /** |
78 | | * Operator less than. |
79 | | * |
80 | | * \param other Other holder to compare. |
81 | | * |
82 | | * \return True when the other holder has less value than this. False otherwise. |
83 | | */ |
84 | | bool operator<(const optional_holder_base& other) const |
85 | 10 | { |
86 | 10 | if (getDerived()->hasValue() && other.getDerived()->hasValue()6 ) |
87 | 4 | return get() < other.get(); |
88 | | |
89 | 6 | return !getDerived()->hasValue() && other.getDerived()->hasValue()4 ; |
90 | 10 | } |
91 | | |
92 | | /** |
93 | | * Bool operator. |
94 | | * |
95 | | * Evaluates to true when this holder has assigned any value. |
96 | | */ |
97 | | explicit operator bool() const noexcept |
98 | 501 | { |
99 | 501 | return getDerived()->hasValue(); |
100 | 501 | } |
101 | | |
102 | | /** |
103 | | * Dereference operator ->. |
104 | | * |
105 | | * \return Const reference to the assigned value. |
106 | | * |
107 | | * \throw CppRuntimeException when the holder is unset. |
108 | | */ |
109 | | const T* operator->() const |
110 | 4 | { |
111 | 4 | return std::addressof(get()); |
112 | 4 | } |
113 | | |
114 | | /** |
115 | | * Dereference operator ->. |
116 | | * |
117 | | * \return Reference to the assigned value. |
118 | | * |
119 | | * \throw CppRuntimeException when the holder is unset. |
120 | | */ |
121 | | T* operator->() |
122 | 426 | { |
123 | 426 | return std::addressof(get()); |
124 | 426 | } |
125 | | |
126 | | /** |
127 | | * Gets held value. |
128 | | * |
129 | | * \return Const reference to the assigned value. |
130 | | * |
131 | | * \throw CppRuntimeException when the holder is unset. |
132 | | */ |
133 | | const T& value() const |
134 | 48.7k | { |
135 | 48.7k | return get(); |
136 | 48.7k | } |
137 | | |
138 | | /** |
139 | | * Gets held value. |
140 | | * |
141 | | * \return Reference to the assigned value. |
142 | | * |
143 | | * \throw CppRuntimeException when the holder is unset. |
144 | | */ |
145 | | T& value() |
146 | 859 | { |
147 | 859 | return get(); |
148 | 859 | } |
149 | | |
150 | | protected: |
151 | | void checkHasValue() const |
152 | 50.1k | { |
153 | 50.1k | if (!getDerived()->hasValue()) |
154 | 14 | throwNonPresentException(); |
155 | 50.1k | } |
156 | | |
157 | | private: |
158 | | T& get() |
159 | 1.28k | { |
160 | 1.28k | return getDerived()->operator*(); |
161 | 1.28k | } |
162 | | |
163 | | const T& get() const |
164 | 48.7k | { |
165 | 48.7k | return getDerived()->operator*(); |
166 | 48.7k | } |
167 | | |
168 | | Derived* getDerived() |
169 | 1.28k | { |
170 | 1.28k | return static_cast<Derived*>(this); |
171 | 1.28k | } |
172 | | |
173 | | const Derived* getDerived() const |
174 | 99.5k | { |
175 | 99.5k | return static_cast<const Derived*>(this); |
176 | 99.5k | } |
177 | | |
178 | | /** Optimization which increases chances to inline checkHasValue(). */ |
179 | | void throwNonPresentException() const |
180 | 14 | { |
181 | 14 | throw CppRuntimeException("Trying to access value of non-present optional field!"); |
182 | 14 | } |
183 | | }; |
184 | | |
185 | | /** |
186 | | * Optional holder implementation that stores the object on heap. |
187 | | */ |
188 | | template <typename T, typename ALLOC> |
189 | | class heap_optional_holder : public optional_holder_base<T, heap_optional_holder<T, ALLOC>> |
190 | | { |
191 | | public: |
192 | | using allocator_type = ALLOC; |
193 | | using allocator_traits = std::allocator_traits<allocator_type>; |
194 | | using value_type = T; |
195 | | using storage_type = zserio::unique_ptr<T, allocator_type>; |
196 | | |
197 | | /** |
198 | | * Getter for the allocator. |
199 | | * |
200 | | * \return Allocator that is used for the dynamic memory allocat |
201 | | */ |
202 | | allocator_type get_allocator() const |
203 | 98 | { |
204 | 98 | return m_storage.get_deleter().get_allocator(); |
205 | 98 | } |
206 | | |
207 | | /** |
208 | | * Empty constructor which creates an unset holder. |
209 | | * |
210 | | * \param allocator Allocator to be used to perform dynamic memory allocations. |
211 | | */ |
212 | | explicit constexpr heap_optional_holder(const allocator_type& allocator = allocator_type()) noexcept : |
213 | | m_storage(nullptr, allocator) |
214 | 45 | {} |
215 | | |
216 | | /** |
217 | | * Constructor from zserio::NullOpt constant to create an unset holder. |
218 | | * |
219 | | * \param allocator Allocator to be used to perform dynamic memory allocations. |
220 | | */ |
221 | | constexpr heap_optional_holder(NullOptType, |
222 | | const allocator_type& allocator = allocator_type()) noexcept : m_storage(nullptr, allocator) |
223 | 2 | {} |
224 | | |
225 | | /** |
226 | | * Constructor from a given value. |
227 | | * |
228 | | * \param value Value to store in the holder. |
229 | | * \param allocator Allocator to be used to perform dynamic memory allocations. |
230 | | */ |
231 | | heap_optional_holder(const T& value, const allocator_type& allocator = allocator_type()) : |
232 | | m_storage(zserio::allocate_unique<T, allocator_type>(allocator, value)) |
233 | 2 | {} |
234 | | |
235 | | /** |
236 | | * Constructor from a given value passed by rvalue reference. |
237 | | * |
238 | | * \param value Value to store in the holder. |
239 | | * \param allocator Allocator to be used to perform dynamic memory allocations. |
240 | | */ |
241 | | heap_optional_holder(T&& value, const allocator_type& allocator = allocator_type()) : |
242 | | m_storage(zserio::allocate_unique<T, allocator_type>(allocator, std::move(value))) |
243 | 27 | {} |
244 | | |
245 | | // called from allocatorPropagatingCopy |
246 | | /** |
247 | | * Constructor from a given value passed by rvalue reference which prevents initialization. |
248 | | * |
249 | | * \param value Value to store in the holder. |
250 | | * \param allocator Allocator to be used to perform dynamic memory allocations. |
251 | | */ |
252 | | template <typename U = T, |
253 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
254 | | heap_optional_holder(NoInitT, T&& value, const allocator_type& allocator = allocator_type()) : |
255 | | m_storage(zserio::allocate_unique<T, allocator_type>(allocator, NoInit, std::move(value))) |
256 | 2 | {} |
257 | | |
258 | | /** |
259 | | * Constructor that initializes the value inplace by forwarding the arguments to the object's constructor. |
260 | | * |
261 | | * \param allocator Allocator to be used for dynamic memory allocations. |
262 | | * \param u Parameters for object's constructor. |
263 | | */ |
264 | | template <typename ...U> |
265 | | explicit heap_optional_holder(const allocator_type& allocator, U&& ...u) : |
266 | | m_storage(zserio::allocate_unique<T, allocator_type>(allocator, std::forward<U>(u)...)) |
267 | 2 | {} |
268 | | |
269 | | /** |
270 | | * Destructor. |
271 | | */ |
272 | 98 | ~heap_optional_holder() = default; |
273 | | |
274 | | /** |
275 | | * Copy constructor. |
276 | | * |
277 | | * \param other Other holder to copy. |
278 | | */ |
279 | | heap_optional_holder(const heap_optional_holder& other) : |
280 | | m_storage(copy_initialize(other, allocator_traits::select_on_container_copy_construction( |
281 | | other.get_allocator()))) |
282 | 6 | {} |
283 | | |
284 | | /** |
285 | | * Copy constructor which prevents initialization. |
286 | | * |
287 | | * \param other Other holder to copy. |
288 | | */ |
289 | | template <typename U = T, |
290 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
291 | | heap_optional_holder(NoInitT, const heap_optional_holder& other) : |
292 | | m_storage(copy_initialize(NoInit, other, |
293 | | allocator_traits::select_on_container_copy_construction( |
294 | | other.get_allocator()))) |
295 | 2 | {} |
296 | | |
297 | | /** |
298 | | * Allocator-extended copy constructor. |
299 | | * |
300 | | * \param other Other holder to copy. |
301 | | * \param allocator Allocator to be used for dynamic memory allocations. |
302 | | */ |
303 | | heap_optional_holder(const heap_optional_holder& other, const allocator_type& allocator) : |
304 | | m_storage(copy_initialize(other, allocator)) |
305 | 2 | {} |
306 | | |
307 | | /** |
308 | | * Move constructor. |
309 | | * |
310 | | * \param other Other holder to move. |
311 | | */ |
312 | 1 | heap_optional_holder(heap_optional_holder&& other) noexcept = default; |
313 | | |
314 | | /** |
315 | | * Move constructor which prevents initialization. |
316 | | * |
317 | | * \param other Other holder to move. |
318 | | */ |
319 | | template <typename U = T, |
320 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
321 | | heap_optional_holder(NoInitT, heap_optional_holder&& other) : |
322 | | m_storage(move_initialize(NoInit, std::move(other), other.get_allocator())) |
323 | 4 | { |
324 | 4 | } |
325 | | |
326 | | /** |
327 | | * Allocator-extended move constructor. |
328 | | * |
329 | | * \param other Other holder to move. |
330 | | * \param allocator Allocator to be used for dynamic memory allocations. |
331 | | */ |
332 | | heap_optional_holder(heap_optional_holder&& other, const allocator_type& allocator) : |
333 | | m_storage(move_initialize(std::move(other), allocator)) |
334 | 3 | { |
335 | 3 | } |
336 | | |
337 | | /** |
338 | | * Assignment operator. |
339 | | * |
340 | | * \param other Other holder to copy-assign. |
341 | | * |
342 | | * \return Reference to the current holder. |
343 | | */ |
344 | | heap_optional_holder& operator=(const heap_optional_holder& other) |
345 | 5 | { |
346 | 5 | if (this == &other) |
347 | 2 | return *this; |
348 | | |
349 | 3 | m_storage = copy_initialize(other, select_allocator(other.get_allocator(), |
350 | 3 | typename allocator_traits::propagate_on_container_copy_assignment())); |
351 | | |
352 | 3 | return *this; |
353 | 5 | } |
354 | | |
355 | | /** |
356 | | * Assignment operator which prevents initialization. |
357 | | * |
358 | | * \param other Other holder to copy-assign. |
359 | | * |
360 | | * \return Reference to the current holder. |
361 | | */ |
362 | | template <typename U = T, |
363 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
364 | | heap_optional_holder& assign(NoInitT, const heap_optional_holder& other) |
365 | 3 | { |
366 | 3 | if (this == &other) |
367 | 1 | return *this; |
368 | | |
369 | 2 | m_storage = copy_initialize(NoInit, other, select_allocator(other.get_allocator(), |
370 | 2 | typename allocator_traits::propagate_on_container_copy_assignment())); |
371 | | |
372 | 2 | return *this; |
373 | 3 | } |
374 | | |
375 | | /** |
376 | | * Move assignment operator. |
377 | | * |
378 | | * \param other Other holder to move-assign. |
379 | | * |
380 | | * \return Reference to the current holder. |
381 | | */ |
382 | | heap_optional_holder& operator=(heap_optional_holder&& other) |
383 | 4 | { |
384 | 4 | if (this == &other) |
385 | 2 | return *this; |
386 | | |
387 | 2 | m_storage = move_initialize(std::move(other), select_allocator(other.get_allocator(), |
388 | 2 | typename allocator_traits::propagate_on_container_move_assignment())); |
389 | | |
390 | 2 | return *this; |
391 | 4 | } |
392 | | |
393 | | /** |
394 | | * Move assignment operator which prevents initialization. |
395 | | * |
396 | | * \param other Other holder to move-assign. |
397 | | * |
398 | | * \return Reference to the current holder. |
399 | | */ |
400 | | template <typename U = T, |
401 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
402 | | heap_optional_holder& assign(NoInitT, heap_optional_holder&& other) |
403 | 5 | { |
404 | 5 | if (this == &other) |
405 | 2 | return *this; |
406 | | |
407 | 3 | m_storage = move_initialize(NoInit, std::move(other), |
408 | 3 | select_allocator(other.get_allocator(), |
409 | 3 | typename allocator_traits::propagate_on_container_move_assignment())); |
410 | | |
411 | 3 | return *this; |
412 | 5 | } |
413 | | |
414 | | /** |
415 | | * Assignment operator from value. |
416 | | * |
417 | | * \param value Value to assign. |
418 | | * |
419 | | * \return Reference to the current holder. |
420 | | */ |
421 | | heap_optional_holder& operator=(const T& value) |
422 | 8 | { |
423 | 8 | set(value); |
424 | | |
425 | 8 | return *this; |
426 | 8 | } |
427 | | |
428 | | /** |
429 | | * Assignment operator from rvalue reference to value. |
430 | | * |
431 | | * \param value Value to move-assign. |
432 | | * |
433 | | * \return Reference to the current holder. |
434 | | */ |
435 | | heap_optional_holder& operator=(T&& value) |
436 | 7 | { |
437 | 7 | set(std::move(value)); |
438 | | |
439 | 7 | return *this; |
440 | 7 | } |
441 | | |
442 | | /** |
443 | | * Resets the current holder (switch to the unset state). |
444 | | */ |
445 | | void reset() noexcept |
446 | 17 | { |
447 | 17 | m_storage.reset(); |
448 | 17 | } |
449 | | |
450 | | /** |
451 | | * Gets whether the holder has any value. |
452 | | * |
453 | | * \return True when this holder has assigned any value. False otherwise. |
454 | | */ |
455 | | bool hasValue() const noexcept |
456 | 208 | { |
457 | 208 | return static_cast<bool>(m_storage); |
458 | 208 | } |
459 | | |
460 | | /** |
461 | | * Dereference operator *. |
462 | | * |
463 | | * \return Const reference to the assigned value. |
464 | | * |
465 | | * \throw CppRuntimeException when the holder is unset. |
466 | | */ |
467 | | const T& operator*() const |
468 | 34 | { |
469 | 34 | this->checkHasValue(); |
470 | 34 | return *m_storage.get(); |
471 | 34 | } |
472 | | |
473 | | /** |
474 | | * Dereference operator *. |
475 | | * |
476 | | * \return Reference to the assigned value. |
477 | | * |
478 | | * \throw CppRuntimeException when the holder is unset. |
479 | | */ |
480 | | T& operator*() |
481 | 76 | { |
482 | 76 | this->checkHasValue(); |
483 | 76 | return *m_storage.get(); |
484 | 76 | } |
485 | | |
486 | | private: |
487 | | allocator_type select_allocator(allocator_type other_allocator, std::true_type) |
488 | 7 | { |
489 | 7 | return other_allocator; |
490 | 7 | } |
491 | | |
492 | | allocator_type select_allocator(allocator_type, std::false_type) |
493 | 3 | { |
494 | 3 | return get_allocator(); // current allocator |
495 | 3 | } |
496 | | |
497 | | template <typename U = T> |
498 | | void set(U&& value) |
499 | 15 | { |
500 | 15 | reset(); |
501 | 15 | m_storage = zserio::allocate_unique<T, allocator_type>(get_allocator(), std::forward<U>(value)); |
502 | 15 | } |
503 | | |
504 | | static storage_type copy_initialize(const heap_optional_holder& other, const allocator_type& allocator) |
505 | 11 | { |
506 | 11 | if (other.hasValue()) |
507 | 9 | return zserio::allocate_unique<T, allocator_type>(allocator, *other); |
508 | 2 | else |
509 | 2 | return storage_type(nullptr, allocator); |
510 | 11 | } |
511 | | |
512 | | static storage_type copy_initialize(NoInitT, const heap_optional_holder& other, |
513 | | const allocator_type& allocator) |
514 | 4 | { |
515 | 4 | if (other.hasValue()) |
516 | 2 | return zserio::allocate_unique<T, allocator_type>(allocator, NoInit, *other); |
517 | 2 | else |
518 | 2 | return storage_type(nullptr, allocator); |
519 | 4 | } |
520 | | |
521 | | static storage_type move_initialize(heap_optional_holder&& other, const allocator_type& allocator) |
522 | 5 | { |
523 | 5 | if (other.hasValue()) |
524 | 4 | { |
525 | 4 | if (allocator == other.get_allocator()) |
526 | 2 | return std::move(other.m_storage); |
527 | | |
528 | 2 | return zserio::allocate_unique<T, allocator_type>(allocator, std::move(*other)); |
529 | 4 | } |
530 | 1 | else |
531 | 1 | { |
532 | 1 | return storage_type(nullptr, allocator); |
533 | 1 | } |
534 | 5 | } |
535 | | |
536 | | static storage_type move_initialize(NoInitT, heap_optional_holder&& other, |
537 | | const allocator_type& allocator) |
538 | 7 | { |
539 | 7 | if (other.hasValue()) |
540 | 4 | { |
541 | 4 | if (allocator == other.get_allocator()) |
542 | 3 | return std::move(other.m_storage); |
543 | | |
544 | 1 | return zserio::allocate_unique<T, allocator_type>(allocator, NoInit, |
545 | 1 | std::move(*other)); |
546 | 4 | } |
547 | 3 | else |
548 | 3 | { |
549 | 3 | return storage_type(nullptr, allocator); |
550 | 3 | } |
551 | 7 | } |
552 | | |
553 | | storage_type m_storage; |
554 | | }; |
555 | | |
556 | | /** |
557 | | * In place storage for optional holder. |
558 | | */ |
559 | | template <typename T> |
560 | | class in_place_storage |
561 | | { |
562 | | public: |
563 | | /** |
564 | | * Constructor. |
565 | | */ |
566 | | in_place_storage() = default; |
567 | | |
568 | | /** |
569 | | * Destructor. |
570 | | */ |
571 | | ~in_place_storage() = default; |
572 | | |
573 | | /** Copying is disabled. */ |
574 | | /** \{ */ |
575 | | in_place_storage(const in_place_storage&) = delete; |
576 | | in_place_storage& operator=(const in_place_storage&) = delete; |
577 | | /** \} */ |
578 | | |
579 | | /** Move constructor is disabled. */ |
580 | | in_place_storage(in_place_storage&& other) = delete; |
581 | | |
582 | | /** |
583 | | * Move assignment operator. |
584 | | * |
585 | | * \param other Other storage to move. |
586 | | * |
587 | | * \return Reference to the current storage. |
588 | | */ |
589 | | in_place_storage& operator=(in_place_storage&& other) |
590 | 16.2k | { |
591 | 16.2k | new (&m_inPlace) T(std::move(*other.getObject())); |
592 | 16.2k | other.getObject()->~T(); // ensure that destructor of object in original storage is called |
593 | | |
594 | 16.2k | return *this; |
595 | 16.2k | } |
596 | | |
597 | | /** |
598 | | * Move assignment operator which prevents initialization. |
599 | | * |
600 | | * \param other Other storage to move. |
601 | | * |
602 | | * \return Reference to the current storage. |
603 | | */ |
604 | | in_place_storage& assign(NoInitT, in_place_storage&& other) |
605 | 10 | { |
606 | 10 | new (&m_inPlace) T(NoInit, std::move(*other.getObject())); |
607 | 10 | other.getObject()->~T(); // ensure that destructor of object in original storage is called |
608 | | |
609 | 10 | return *this; |
610 | 10 | } |
611 | | |
612 | | /** |
613 | | * Gets pointer to the underlying in-place memory. |
614 | | * |
615 | | * \return Pointer to the storage. |
616 | | */ |
617 | | void* getStorage() |
618 | 50.4k | { |
619 | 50.4k | return &m_inPlace; |
620 | 50.4k | } |
621 | | |
622 | | /** |
623 | | * Gets pointer to the stored object. |
624 | | * |
625 | | * \return Pointer to the object. |
626 | | */ |
627 | | T* getObject() |
628 | 84.3k | { |
629 | 84.3k | return reinterpret_cast<T*>(&m_inPlace); |
630 | 84.3k | } |
631 | | |
632 | | /** |
633 | | * Gets pointer to the stored object. |
634 | | * |
635 | | * \return Const pointer to the object. |
636 | | */ |
637 | | const T* getObject() const |
638 | 48.8k | { |
639 | 48.8k | return reinterpret_cast<const T*>(&m_inPlace); |
640 | 48.8k | } |
641 | | |
642 | | private: |
643 | | // Caution! |
644 | | // We use static constants as a WORKAROUND for a GCC 8/9 bug observed when cross-compiling with -m32 flag. |
645 | | // The compilers somehow spoils the aligned storage when alignof(T) is used directly as the template |
646 | | // argument which leads to "*** stack smashing detected ***" error (as observed in some language tests). |
647 | | static constexpr size_t SIZEOF_T = sizeof(T); |
648 | | static constexpr size_t ALIGNOF_T = alignof(T); |
649 | | using AlignedStorage = typename std::aligned_storage<SIZEOF_T, ALIGNOF_T>::type; |
650 | | AlignedStorage m_inPlace; |
651 | | }; |
652 | | |
653 | | /** |
654 | | * Optional holder implementation for Zserio which allows usage of in place storage. |
655 | | */ |
656 | | template <typename T> |
657 | | class inplace_optional_holder : public optional_holder_base<T, inplace_optional_holder<T>> |
658 | | { |
659 | | public: |
660 | | using value_type = T; |
661 | | |
662 | | /** |
663 | | * Empty constructor which creates an unset holder. |
664 | | */ |
665 | 66.4k | constexpr inplace_optional_holder() noexcept = default; |
666 | | |
667 | | /** |
668 | | * Constructor from zserio::NullOpt constant to create an unset holder. |
669 | | */ |
670 | | constexpr inplace_optional_holder(NullOptType) noexcept |
671 | 293 | {} |
672 | | |
673 | | /** |
674 | | * Constructor from a given value. |
675 | | * |
676 | | * \param value Value to store in the holder. |
677 | | */ |
678 | | inplace_optional_holder(const T& value) |
679 | 42 | { |
680 | 42 | new (m_storage.getStorage()) T(value); |
681 | 42 | m_hasValue = true; |
682 | 42 | } |
683 | | |
684 | | /** |
685 | | * Constructor from a given value passed by rvalue reference. |
686 | | * |
687 | | * \param value Value to store in the holder. |
688 | | */ |
689 | | inplace_optional_holder(T&& value) |
690 | 36 | { |
691 | 36 | new (m_storage.getStorage()) T(std::move(value)); |
692 | 36 | m_hasValue = true; |
693 | 36 | } |
694 | | |
695 | | // called from allocatorPropagatingCopy |
696 | | /** |
697 | | * Constructor from a given value passed by rvalue reference which prevents initialization. |
698 | | * |
699 | | * \param value Value to store in the holder. |
700 | | */ |
701 | | template <typename U = T, |
702 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
703 | | inplace_optional_holder(NoInitT, T&& value) |
704 | 6 | { |
705 | 6 | new (m_storage.getStorage()) T(NoInit, std::move(value)); |
706 | 6 | m_hasValue = true; |
707 | 6 | } |
708 | | |
709 | | /** |
710 | | * Copy constructor. |
711 | | * |
712 | | * \param other Other holder to copy. |
713 | | */ |
714 | | inplace_optional_holder(const inplace_optional_holder& other) |
715 | 72 | { |
716 | 72 | if (other.hasValue()) |
717 | 44 | { |
718 | 44 | new (m_storage.getStorage()) T(*other.m_storage.getObject()); |
719 | 44 | m_hasValue = true; |
720 | 44 | } |
721 | 72 | } |
722 | | |
723 | | /** |
724 | | * Copy constructor which prevents initialization. |
725 | | * |
726 | | * \param other Other holder to copy. |
727 | | */ |
728 | | template <typename U = T, |
729 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
730 | | inplace_optional_holder(NoInitT, const inplace_optional_holder& other) |
731 | 2 | { |
732 | 2 | if (other.hasValue()) |
733 | 1 | { |
734 | 1 | new (m_storage.getStorage()) T(NoInit, *other.m_storage.getObject()); |
735 | 1 | m_hasValue = true; |
736 | 1 | } |
737 | 2 | } |
738 | | |
739 | | /** |
740 | | * Move constructor. |
741 | | * |
742 | | * \param other Other holder to move. |
743 | | */ |
744 | | inplace_optional_holder(inplace_optional_holder&& other) |
745 | | noexcept(std::is_nothrow_move_constructible<in_place_storage<T>>::value) |
746 | 4 | { |
747 | 4 | if (other.hasValue()) |
748 | 2 | { |
749 | 2 | m_storage = std::move(other.m_storage); |
750 | 2 | other.m_hasValue = false; |
751 | 2 | m_hasValue = true; |
752 | 2 | } |
753 | 4 | } |
754 | | |
755 | | /** |
756 | | * Move constructor which prevents initialization. |
757 | | * |
758 | | * \param other Other holder to move. |
759 | | */ |
760 | | template <typename U = T, |
761 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
762 | | inplace_optional_holder(NoInitT, inplace_optional_holder&& other) |
763 | | noexcept(std::is_nothrow_move_constructible<in_place_storage<T>>::value) |
764 | 4 | { |
765 | 4 | if (other.hasValue()) |
766 | 2 | { |
767 | 2 | m_storage.assign(NoInit, std::move(other.m_storage)); |
768 | 2 | other.m_hasValue = false; |
769 | 2 | m_hasValue = true; |
770 | 2 | } |
771 | 4 | } |
772 | | |
773 | | /** |
774 | | * Constructor that initializes the value inplace by forwarding the arguments to the object's constructor. |
775 | | * |
776 | | * \param u Parameters for object's constructor. |
777 | | */ |
778 | | template <typename ...U> |
779 | | explicit inplace_optional_holder(InPlaceT, U&& ...u) |
780 | 3 | { |
781 | 3 | new (m_storage.getStorage()) T(std::forward<U>(u)...); |
782 | 3 | m_hasValue = true; |
783 | 3 | } |
784 | | |
785 | | /** |
786 | | * Destructor. |
787 | | */ |
788 | | ~inplace_optional_holder() |
789 | 66.8k | { |
790 | 66.8k | reset(); |
791 | 66.8k | } |
792 | | |
793 | | /** |
794 | | * Assignment operator. |
795 | | * |
796 | | * \param other Other holder to copy-assign. |
797 | | * |
798 | | * \return Reference to the current holder. |
799 | | */ |
800 | | inplace_optional_holder& operator=(const inplace_optional_holder& other) |
801 | 61 | { |
802 | 61 | if (this != &other) |
803 | 60 | { |
804 | 60 | reset(); |
805 | 60 | if (other.hasValue()) |
806 | 59 | { |
807 | 59 | new (m_storage.getStorage()) T(*other.m_storage.getObject()); |
808 | 59 | m_hasValue = true; |
809 | 59 | } |
810 | 60 | } |
811 | | |
812 | 61 | return *this; |
813 | 61 | } |
814 | | |
815 | | /** |
816 | | * Assignment operator which prevents initialization. |
817 | | * |
818 | | * \param other Other holder to copy-assign. |
819 | | * |
820 | | * \return Reference to the current holder. |
821 | | */ |
822 | | template <typename U = T, |
823 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
824 | | inplace_optional_holder& assign(NoInitT, const inplace_optional_holder& other) |
825 | 9 | { |
826 | 9 | if (this != &other) |
827 | 8 | { |
828 | 8 | reset(); |
829 | 8 | if (other.hasValue()) |
830 | 7 | { |
831 | 7 | new (m_storage.getStorage()) T(NoInit, *other.m_storage.getObject()); |
832 | 7 | m_hasValue = true; |
833 | 7 | } |
834 | 8 | } |
835 | | |
836 | 9 | return *this; |
837 | 9 | } |
838 | | |
839 | | /** |
840 | | * Move assignment operator. |
841 | | * |
842 | | * \param other Other holder to move-assign. |
843 | | * |
844 | | * \return Reference to the current holder. |
845 | | */ |
846 | | inplace_optional_holder& operator=(inplace_optional_holder&& other) |
847 | 16.3k | { |
848 | 16.3k | if (this != &other) |
849 | 16.3k | { |
850 | 16.3k | reset(); |
851 | 16.3k | if (other.hasValue()) |
852 | 16.2k | { |
853 | 16.2k | m_storage = std::move(other.m_storage); |
854 | 16.2k | other.m_hasValue = false; |
855 | 16.2k | m_hasValue = true; |
856 | 16.2k | } |
857 | 16.3k | } |
858 | | |
859 | 16.3k | return *this; |
860 | 16.3k | } |
861 | | |
862 | | /** |
863 | | * Move assignment operator which prevents initialization. |
864 | | * |
865 | | * \param other Other holder to move-assign. |
866 | | * |
867 | | * \return Reference to the current holder. |
868 | | */ |
869 | | template <typename U = T, |
870 | | typename std::enable_if<std::is_constructible<U, NoInitT, U>::value, int>::type = 0> |
871 | | inplace_optional_holder& assign(NoInitT, inplace_optional_holder&& other) |
872 | 10 | { |
873 | 10 | if (this != &other) |
874 | 9 | { |
875 | 9 | reset(); |
876 | 9 | if (other.hasValue()) |
877 | 8 | { |
878 | 8 | m_storage.assign(NoInit, std::move(other.m_storage)); |
879 | 8 | other.m_hasValue = false; |
880 | 8 | m_hasValue = true; |
881 | 8 | } |
882 | 9 | } |
883 | | |
884 | 10 | return *this; |
885 | 10 | } |
886 | | |
887 | | /** |
888 | | * Assignment operator from value. |
889 | | * |
890 | | * \param value Value to assign. |
891 | | * |
892 | | * \return Reference to the current holder. |
893 | | */ |
894 | | inplace_optional_holder& operator=(const T& value) |
895 | 33.3k | { |
896 | 33.3k | set(value); |
897 | | |
898 | 33.3k | return *this; |
899 | 33.3k | } |
900 | | |
901 | | /** |
902 | | * Assignment operator from rvalue reference to value. |
903 | | * |
904 | | * \param value Value to move-assign. |
905 | | * |
906 | | * \return Reference to the current holder. |
907 | | */ |
908 | | inplace_optional_holder& operator=(T&& value) |
909 | 16.8k | { |
910 | 16.8k | set(std::move(value)); |
911 | | |
912 | 16.8k | return *this; |
913 | 16.8k | } |
914 | | |
915 | | /** |
916 | | * Resets the current holder (switch to the unset state). |
917 | | */ |
918 | | void reset() noexcept |
919 | 133k | { |
920 | 133k | if (hasValue()) |
921 | 50.4k | { |
922 | 50.4k | m_storage.getObject()->~T(); |
923 | 50.4k | m_hasValue = false; |
924 | 50.4k | } |
925 | 133k | } |
926 | | |
927 | | /** |
928 | | * Gets whether the holder has any value. |
929 | | * |
930 | | * \return True when this holder has assigned any value. False otherwise. |
931 | | */ |
932 | | bool hasValue() const noexcept |
933 | 201k | { |
934 | 201k | return m_hasValue; |
935 | 201k | } |
936 | | |
937 | | /** |
938 | | * Dereference operator *. |
939 | | * |
940 | | * \return Const reference to the assigned value. |
941 | | * |
942 | | * \throw CppRuntimeException when the holder is unset. |
943 | | */ |
944 | | const T& operator*() const |
945 | 48.7k | { |
946 | 48.7k | this->checkHasValue(); |
947 | 48.7k | return *m_storage.getObject(); |
948 | 48.7k | } |
949 | | |
950 | | /** |
951 | | * Dereference operator *. |
952 | | * |
953 | | * \return Reference to the assigned value. |
954 | | * |
955 | | * \throw CppRuntimeException when the holder is unset. |
956 | | */ |
957 | | T& operator*() |
958 | 1.30k | { |
959 | 1.30k | this->checkHasValue(); |
960 | 1.30k | return *m_storage.getObject(); |
961 | 1.30k | } |
962 | | |
963 | | private: |
964 | | template <typename U = T> |
965 | | void set(U&& value) |
966 | 50.2k | { |
967 | 50.2k | reset(); |
968 | 50.2k | new (m_storage.getStorage()) T(std::forward<U>(value)); |
969 | 50.2k | m_hasValue = true; |
970 | 50.2k | } |
971 | | |
972 | | in_place_storage<T> m_storage; |
973 | | bool m_hasValue = false; |
974 | | }; |
975 | | |
976 | | } // namespace detail |
977 | | |
978 | | // Be aware that if Inplace/HeapOptionalHolder is defined by typename, C++ compiler will have problem with |
979 | | // template function overload, see HashCodeUtil.h (overloads for objects and for Inplace/HeapOptionalHolder). |
980 | | /** |
981 | | * Optional holder which uses in place storage. |
982 | | */ |
983 | | template <typename T> |
984 | | using InplaceOptionalHolder = detail::inplace_optional_holder<T>; |
985 | | |
986 | | /** |
987 | | * Optional holder which uses heap storage. |
988 | | */ |
989 | | template <typename T, typename ALLOC = std::allocator<T>> |
990 | | using HeapOptionalHolder = detail::heap_optional_holder<T, ALLOC>; |
991 | | |
992 | | } // namespace zserio |
993 | | |
994 | | #endif // ifndef ZSERIO_OPTIONAL_HOLDER_H_INC |