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