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