sparrow 0.6.0
Loading...
Searching...
No Matches
nullable.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <compare>
18#include <concepts>
19#include <exception>
20#if defined(__cpp_lib_format)
21# include <format>
22# include <ostream>
23#endif
24#include <type_traits>
25#include <variant>
26
28
29
30#if defined(SPARROW_CONSTEXPR)
31# error "SPARROW_CONSTEXPR already defined"
32#endif
33
34// clang workaround: clang instantiates the constructor in SFINAE context,
35// which is incompatible with the implementation of standard libraries which
36// are not libc++. This leads to wrong compilation errors. Making the constructor
37// not constexpr prevents the compiler from instantiating it.
38#if defined(__clang__) && not defined(_LIBCPP_VERSION)
39# define SPARROW_CONSTEXPR
40#else
41# define SPARROW_CONSTEXPR constexpr
42#endif
43
44namespace sparrow
45{
46 template <class T, mpl::boolean_like B>
47 class nullable;
48
49 template <class T>
50 struct is_nullable : std::false_type
51 {
52 };
53
54 template <class T, mpl::boolean_like B>
55 struct is_nullable<nullable<T, B>> : std::true_type
56 {
57 };
58
59 template <class T>
60 inline constexpr bool is_nullable_v = is_nullable<T>::value;
61
62 template <class N, class T>
63 concept is_nullable_of = is_nullable_v<N> && std::same_as<typename N::value_type, T>;
64
65 template <class N, class T>
66 concept is_nullable_of_convertible_to = is_nullable_v<N> && std::convertible_to<typename N::value_type, T>;
67
68 /*
69 * Matches a range of nullables objects.
70 *
71 * A range is considered a range of nullables if it is a range and its value type is a nullable.
72 *
73 * @tparam RangeOfNullables The range to check.
74 */
75 template <class RangeOfNullables>
76 concept range_of_nullables = std::ranges::range<RangeOfNullables>
78
79 /*
80 * Default traits for the nullable class. These traits should be specialized
81 * for proxy classes whose reference and const_reference types are not
82 * defined as usual. For instance:
83 *
84 * @code{.cpp}
85 * struct nullable_traits<string_proxy>
86 * {
87 * using value_type = std::string;
88 * using reference = string_proxy;
89 * using const_reference = std::string_view;
90 * using rvalue_reverence = std::string&&;
91 * using const_rvalue_reference = const std::string&&;
92 * };
93 * @endcode
94 */
95 template <class T>
97 {
98 using value_type = T;
99 using reference = std::add_lvalue_reference_t<value_type>;
100 using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
103 };
104
105 template <class T>
107 {
108 using value_type = T;
109 using reference = std::add_lvalue_reference_t<value_type>;
110 using const_reference = std::add_lvalue_reference_t<std::add_const_t<value_type>>;
113 };
114
115 /*
116 * Defines a type of object to be thrown by nullable::value when accessing
117 * a nullable object whose value is null.
118 */
119 class bad_nullable_access : public std::exception
120 {
121 public:
122
123 bad_nullable_access() noexcept = default;
124 bad_nullable_access(const bad_nullable_access&) noexcept = default;
125 bad_nullable_access& operator=(const bad_nullable_access&) noexcept = default;
126
127 [[nodiscard]] const char* what() const noexcept override
128 {
129 return message;
130 }
131
132 private:
133
134 static constexpr const char* message = "Invalid access to nullable underlying value";
135 };
136
141 {
142 // This is required to disable the generation of
143 // the default constructor. Otherwise, a = {} where
144 // a is a nullable would lead to an ambiguous call
145 // where both operator=(nullable&&) and operator=(nullval_t)
146 // are valid.
147 constexpr explicit nullval_t(int)
148 {
149 }
150 };
151
152 inline constexpr nullval_t nullval(0);
153
154 namespace impl
155 {
159 template <class T, class TArgs, class U, class UArgs>
160 concept both_constructible_from_cref = std::constructible_from<T, mpl::add_const_lvalue_reference_t<TArgs>>
161 and std::constructible_from<U, mpl::add_const_lvalue_reference_t<UArgs>>;
162
163 template <class To1, class From1, class To2, class From2>
164 concept both_convertible_from_cref = std::convertible_to<mpl::add_const_lvalue_reference_t<From1>, To1>
165 and std::convertible_to<mpl::add_const_lvalue_reference_t<From2>, To2>;
166
167 template <class T, class... Args>
168 concept constructible_from_one = (std::constructible_from<T, Args> || ...);
169
170 template <class T, class... Args>
171 concept convertible_from_one = (std::convertible_to<Args, T> || ...);
172
173 template <class T, class... Args>
175
176 template <class T, class Arg>
178
179 // We prefer std::is_assignable_v to std::assignable_from<To, From> because
180 // std::assignable_from requires the existence of an implicit conversion
181 // from From to To
182 template <class To, class... Args>
183 concept assignable_from_one = (std::is_assignable_v<std::add_lvalue_reference<To>, Args> && ...);
184
185 template <class T, class Arg>
187
188 template <class T, class U>
189 using conditional_ref_t = std::conditional_t<std::is_reference_v<T>, const std::decay_t<U>&, std::decay_t<U>&&>;
190
191 template <class T, class Targs, class U, class UArgs>
192 concept both_constructible_from_cond_ref = std::constructible_from<T, conditional_ref_t<T, Targs>>
193 and std::constructible_from<U, conditional_ref_t<U, UArgs>>;
194
195 template <class To1, class From1, class To2, class From2>
196 concept both_convertible_from_cond_ref = std::convertible_to<conditional_ref_t<To1, From1>, To1>
197 and std::convertible_to<conditional_ref_t<To2, From2>, To2>;
198
199 template <class To1, class From1, class To2, class From2>
200 concept both_assignable_from_cref = std::is_assignable_v<
201 std::add_lvalue_reference_t<To1>,
203 and std::is_assignable_v<
204 std::add_lvalue_reference_t<To2>,
206
207 template <class To1, class From1, class To2, class From2>
208 concept both_assignable_from_cond_ref = std::is_assignable_v<
209 std::add_lvalue_reference_t<To1>,
211 and std::is_assignable_v<
212 std::add_lvalue_reference_t<To2>,
214
215 template <class T>
217 }
218
278 template <class T, mpl::boolean_like B = bool>
280 {
281 public:
282
296
297 template <std::default_initializable U = T, std::default_initializable BB = B>
298 constexpr nullable() noexcept
299 : m_value()
300 , m_null_flag(false)
301 {
302 }
303
304 template <std::default_initializable U = T, std::default_initializable BB = B>
305 constexpr nullable(nullval_t) noexcept
306 : m_value()
307 , m_null_flag(false)
308 {
309 }
310
311 template <class U>
312 requires(not std::same_as<self_type, std::decay_t<U>> and std::constructible_from<T, U &&>)
313 explicit(not std::convertible_to<U&&, T>
314 ) constexpr nullable(U&& value) noexcept(noexcept(T(std::declval<U>())))
315 : m_value(std::forward<U>(value))
316 , m_null_flag(true)
317 {
318 }
319
320 constexpr nullable(const self_type&) = default;
321
322 template <class TO, mpl::boolean_like BO>
327 : m_value(rhs.get())
328 , m_null_flag(rhs.null_flag())
329 {
330 }
331
332#ifdef __clang__
333 template <class TO, mpl::boolean_like BO>
334 requires(impl::both_constructible_from_cref<T, TO, B, BO> and std::same_as<std::decay_t<T>, bool>)
337 : m_value(rhs.get())
338 , m_null_flag(rhs.null_flag())
339 {
340 }
341#endif
342
343 constexpr nullable(self_type&&) noexcept = default;
344
345 template <class TO, mpl::boolean_like BO>
346 requires(impl::both_constructible_from_cond_ref<T, TO, B, BO>
347 and not impl::initializable_from_refs<T, nullable<TO, BO>>)
348 explicit(not impl::both_convertible_from_cond_ref<T, TO, B, BO>
350 : m_value(std::move(rhs).get())
351 , m_null_flag(std::move(rhs).null_flag())
352 {
353 }
354
355#ifdef __clang__
356 template <class TO, mpl::boolean_like BO>
358 and std::same_as<std::decay_t<T>, bool>)
361 : m_value(std::move(rhs).get())
362 , m_null_flag(std::move(rhs).null_flag())
363 {
364 }
365#endif
366
368 : m_value(std::move(value))
369 , m_null_flag(std::move(null_flag))
370 {
371 }
372
373 constexpr nullable(std::add_lvalue_reference_t<T> value, std::add_lvalue_reference_t<B> null_flag)
374 : m_value(value)
375 , m_null_flag(null_flag)
376 {
377 }
378
379 constexpr nullable(value_type&& value, std::add_lvalue_reference_t<B> null_flag)
380 : m_value(std::move(value))
381 , m_null_flag(null_flag)
382 {
383 }
384
385 constexpr nullable(std::add_lvalue_reference_t<T> value, flag_type&& null_flag)
386 : m_value(value)
387 , m_null_flag(std::move(null_flag))
388 {
389 }
390
392 {
393 m_null_flag = false;
394 return *this;
395 }
396
397 template <class TO>
398 requires(not std::same_as<self_type, TO> and std::assignable_from<std::add_lvalue_reference_t<T>, TO>)
399 constexpr self_type& operator=(TO&& rhs)
400 {
401 m_value = std::forward<TO>(rhs);
402 m_null_flag = true;
403 return *this;
404 }
405
406 constexpr self_type& operator=(const self_type& rhs)
407 {
408 m_value = rhs.get();
409 m_null_flag = rhs.null_flag();
410 return *this;
411 }
412
413 template <class TO, mpl::boolean_like BO>
414 requires(
418 )
419 constexpr self_type& operator=(const nullable<TO, BO>& rhs)
420 {
421 m_value = rhs.get();
422 m_null_flag = rhs.null_flag();
423 return *this;
424 }
425
427 {
428 m_value = std::move(rhs).get();
429 m_null_flag = std::move(rhs).null_flag();
430 return *this;
431 }
432
433 template <class TO, mpl::boolean_like BO>
434 requires(
438 )
439 constexpr self_type& operator=(nullable<TO, BO>&& rhs)
440 {
441 m_value = std::move(rhs).get();
442 m_null_flag = std::move(rhs).null_flag();
443 return *this;
444 }
445
446 constexpr explicit operator bool() const noexcept;
447 [[nodiscard]] constexpr bool has_value() const noexcept;
448
449 [[nodiscard]] constexpr flag_reference null_flag() & noexcept;
450 [[nodiscard]] constexpr flag_const_reference null_flag() const& noexcept;
451 [[nodiscard]] constexpr flag_rvalue_reference null_flag() && noexcept;
452 [[nodiscard]] constexpr flag_const_rvalue_reference null_flag() const&& noexcept;
453
454 [[nodiscard]] constexpr reference get() & noexcept;
455 [[nodiscard]] constexpr const_reference get() const& noexcept;
456 [[nodiscard]] constexpr rvalue_reference get() && noexcept;
457 [[nodiscard]] constexpr const_rvalue_reference get() const&& noexcept;
458
459 [[nodiscard]] constexpr reference value() &;
460 [[nodiscard]] constexpr const_reference value() const&;
461 [[nodiscard]] constexpr rvalue_reference value() &&;
462 [[nodiscard]] constexpr const_rvalue_reference value() const&&;
463
464 template <class U>
465 [[nodiscard]] constexpr value_type value_or(U&& default_value) const&;
466
467 template <class U>
468 [[nodiscard]] constexpr value_type value_or(U&& default_value) &&;
469
470 void swap(self_type& other) noexcept;
471 void reset() noexcept;
472
473 private:
474
475 void throw_if_null() const;
476
477 T m_value;
478 B m_null_flag;
479
480 template <class TO, mpl::boolean_like BO>
481 friend class nullable;
482 };
483
484 template <class T, class B>
485 constexpr void swap(nullable<T, B>& lhs, nullable<T, B>& rhs) noexcept;
486
487 template <class T, class B>
488 constexpr bool operator==(const nullable<T, B>& lhs, nullval_t) noexcept;
489
490 template <class T, mpl::boolean_like B>
491 constexpr std::strong_ordering operator<=>(const nullable<T, B>& lhs, nullval_t) noexcept;
492
493 template <class T, class B, class U>
494 constexpr bool operator==(const nullable<T, B>& lhs, const U& rhs) noexcept;
495
496 template <class T, class B, class U>
497 requires(!impl::is_nullable_v<U> && std::three_way_comparable_with<U, T>)
498 constexpr std::compare_three_way_result_t<T, U>
499 operator<=>(const nullable<T, B>& lhs, const U& rhs) noexcept;
500
501 template <class T, class B, class U, class UB>
502 constexpr bool operator==(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept;
503
504 template <class T, class B, std::three_way_comparable_with<T> U, class UB>
505 constexpr std::compare_three_way_result_t<T, U>
506 operator<=>(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept;
507
508 // Even if we have CTAD in C++20, some constructors add lvalue reference
509 // to their argument, making the deduction impossible.
510 template <class T, mpl::boolean_like B = bool>
511 constexpr nullable<T, B> make_nullable(T&& value, B&& flag = true);
512
518 template <class... T>
519 requires(is_nullable_v<T> && ...)
520 class nullable_variant : public std::variant<T...>
521 {
522 public:
523
524 using base_type = std::variant<T...>;
525 using base_type::base_type;
526
527 constexpr nullable_variant(const nullable_variant&) = default;
528 constexpr nullable_variant(nullable_variant&&) noexcept = default;
529
530 constexpr nullable_variant& operator=(const nullable_variant&);
531 constexpr nullable_variant& operator=(nullable_variant&&);
532
533 constexpr explicit operator bool() const;
534 constexpr bool has_value() const;
535 };
536}
537
538namespace std
539{
540 namespace mpl = sparrow::mpl;
541
542 // Specialization of basic_common_reference for nullable proxies so
543 // we can use ranges algorithm on iterators returning nullable
544 template <class T, mpl::boolean_like TB, class U, mpl::boolean_like UB, template <class> class TQual, template <class> class UQual>
545 struct basic_common_reference<sparrow::nullable<T, TB>, sparrow::nullable<U, UB>, TQual, UQual>
546 {
547 using type = sparrow::
548 nullable<std::common_reference_t<TQual<T>, UQual<U>>, std::common_reference_t<TQual<TB>, UQual<UB>>>;
549 };
550}
551
552namespace sparrow
553{
554 /***************************
555 * nullable implementation *
556 ***************************/
557
558 template <class T, mpl::boolean_like B>
559 constexpr nullable<T, B>::operator bool() const noexcept
560 {
561 return m_null_flag;
562 }
563
564 template <class T, mpl::boolean_like B>
565 constexpr bool nullable<T, B>::has_value() const noexcept
566 {
567 return m_null_flag;
568 }
569
570 template <class T, mpl::boolean_like B>
571 constexpr auto nullable<T, B>::null_flag() & noexcept -> flag_reference
572 {
573 return m_null_flag;
574 }
575
576 template <class T, mpl::boolean_like B>
577 constexpr auto nullable<T, B>::null_flag() const& noexcept -> flag_const_reference
578 {
579 return m_null_flag;
580 }
581
582 template <class T, mpl::boolean_like B>
583 constexpr auto nullable<T, B>::null_flag() && noexcept -> flag_rvalue_reference
584 {
585 if constexpr (std::is_reference_v<B>)
586 {
587 return m_null_flag;
588 }
589 else
590 {
591 return flag_rvalue_reference(m_null_flag);
592 }
593 }
594
595 template <class T, mpl::boolean_like B>
596 constexpr auto nullable<T, B>::null_flag() const&& noexcept -> flag_const_rvalue_reference
597 {
598 if constexpr (std::is_reference_v<B>)
599 {
600 return m_null_flag;
601 }
602 else
603 {
604 return flag_const_rvalue_reference(m_null_flag);
605 }
606 }
607
608 template <class T, mpl::boolean_like B>
609 constexpr auto nullable<T, B>::get() & noexcept -> reference
610 {
611 return m_value;
612 }
613
614 template <class T, mpl::boolean_like B>
615 constexpr auto nullable<T, B>::get() const& noexcept -> const_reference
616 {
617 return m_value;
618 }
619
620 template <class T, mpl::boolean_like B>
621 constexpr auto nullable<T, B>::get() && noexcept -> rvalue_reference
622 {
623 if constexpr (std::is_reference_v<T>)
624 {
625 return m_value;
626 }
627 else
628 {
629 return rvalue_reference(m_value);
630 }
631 }
632
633 template <class T, mpl::boolean_like B>
634 constexpr auto nullable<T, B>::get() const&& noexcept -> const_rvalue_reference
635 {
636 if constexpr (std::is_reference_v<T>)
637 {
638 return m_value;
639 }
640 else
641 {
642 return const_rvalue_reference(m_value);
643 }
644 }
645
646 template <class T, mpl::boolean_like B>
647 constexpr auto nullable<T, B>::value() & -> reference
648 {
649 throw_if_null();
650 return get();
651 }
652
653 template <class T, mpl::boolean_like B>
654 constexpr auto nullable<T, B>::value() const& -> const_reference
655 {
656 throw_if_null();
657 return get();
658 }
659
660 template <class T, mpl::boolean_like B>
662 {
663 throw_if_null();
664 return std::move(*this).get();
665 }
666
667 template <class T, mpl::boolean_like B>
669 {
670 throw_if_null();
671 return std::move(*this).get();
672 }
673
674 template <class T, mpl::boolean_like B>
675 template <class U>
676 constexpr auto nullable<T, B>::value_or(U&& default_value) const& -> value_type
677 {
678 return *this ? get() : value_type(std::forward<U>(default_value));
679 }
680
681 template <class T, mpl::boolean_like B>
682 template <class U>
683 constexpr auto nullable<T, B>::value_or(U&& default_value) && -> value_type
684 {
685 return *this ? get() : value_type(std::forward<U>(default_value));
686 }
687
688 template <class T, mpl::boolean_like B>
689 void nullable<T, B>::swap(self_type& other) noexcept
690 {
691 using std::swap;
692 swap(m_value, other.m_value);
693 swap(m_null_flag, other.m_null_flag);
694 }
695
696 template <class T, mpl::boolean_like B>
697 void nullable<T, B>::reset() noexcept
698 {
699 m_null_flag = false;
700 }
701
702 template <class T, mpl::boolean_like B>
703 void nullable<T, B>::throw_if_null() const
704 {
705 if (!m_null_flag)
706 {
707 throw bad_nullable_access{};
708 }
709 }
710
711 template <class T, class B>
712 constexpr void swap(nullable<T, B>& lhs, nullable<T, B>& rhs) noexcept
713 {
714 lhs.swap(rhs);
715 }
716
717 template <class T, class B>
718 constexpr bool operator==(const nullable<T, B>& lhs, nullval_t) noexcept
719 {
720 return !lhs;
721 }
722
723 template <class T, class B>
724 constexpr std::strong_ordering operator<=>(const nullable<T, B>& lhs, nullval_t) noexcept
725 {
726 return lhs <=> false;
727 }
728
729 template <class T, class B, class U>
730 constexpr bool operator==(const nullable<T, B>& lhs, const U& rhs) noexcept
731 {
732 return lhs && (lhs.get() == rhs);
733 }
734
735 template <class T, class B, class U>
736 requires(!impl::is_nullable_v<U> && std::three_way_comparable_with<U, T>)
737 constexpr std::compare_three_way_result_t<T, U> operator<=>(const nullable<T, B>& lhs, const U& rhs) noexcept
738 {
739 return lhs ? lhs.get() <=> rhs : std::strong_ordering::less;
740 }
741
742 template <class T, class B, class U, class UB>
743 constexpr bool operator==(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept
744 {
745 return rhs ? lhs == rhs.get() : !lhs;
746 }
747
748 template <class T, class B, std::three_way_comparable_with<T> U, class UB>
749 constexpr std::compare_three_way_result_t<T, U>
750 operator<=>(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept
751 {
752 return (lhs && rhs) ? lhs.get() <=> rhs.get() : bool(lhs) <=> bool(rhs);
753 }
754
755 template <class T, mpl::boolean_like B>
756 constexpr nullable<T, B> make_nullable(T&& value, B&& flag)
757 {
758 return nullable<T, B>(std::forward<T>(value), std::forward<B>(flag));
759 }
760
761 /***********************************
762 * nullable_variant implementation *
763 ***********************************/
764
765 template <class... T>
766 requires(is_nullable_v<T> && ...)
767 constexpr nullable_variant<T...>& nullable_variant<T...>::operator=(const nullable_variant& rhs)
768 {
769 base_type::operator=(rhs);
770 return *this;
771 }
772
773 template <class... T>
774 requires(is_nullable_v<T> && ...)
775 constexpr nullable_variant<T...>& nullable_variant<T...>::operator=(nullable_variant&& rhs)
776 {
777 base_type::operator=(std::move(rhs));
778 return *this;
779 }
780
781 template <class... T>
782 requires(is_nullable_v<T> && ...)
783 constexpr nullable_variant<T...>::operator bool() const
784 {
785 return has_value();
786 }
787
788 template <class... T>
789 requires(is_nullable_v<T> && ...)
790 constexpr bool nullable_variant<T...>::has_value() const
791 {
792 return std::visit(
793 [](const auto& v)
794 {
795 return v.has_value();
796 },
797 *this
798 );
799 }
800}
801
802#if defined(__cpp_lib_format)
803
804template <typename T, sparrow::mpl::boolean_like B>
805struct std::formatter<sparrow::nullable<T, B>>
806{
807 constexpr auto parse(format_parse_context& ctx)
808 {
809 auto pos = ctx.begin();
810 while (pos != ctx.end() && *pos != '}')
811 {
812 m_format_string.push_back(*pos);
813 ++pos;
814 }
815 m_format_string.push_back('}');
816 return pos;
817 }
818
819 auto format(const sparrow::nullable<T, B>& n, std::format_context& ctx) const
820 {
821 if (n.has_value())
822 {
823 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(n.get()));
824 }
825 else
826 {
827 return std::format_to(ctx.out(), "{}", "null");
828 }
829 }
830
831 std::string m_format_string = "{:";
832};
833
834template <typename T, sparrow::mpl::boolean_like B>
835std::ostream& operator<<(std::ostream& os, const sparrow::nullable<T, B>& value)
836{
837 os << std::format("{}", value);
838 return os;
839}
840
841template <class... T>
842struct std::formatter<sparrow::nullable_variant<T...>>
843{
844 constexpr auto parse(format_parse_context& ctx)
845 {
846 auto pos = ctx.begin();
847 while (pos != ctx.end() && *pos != '}')
848 {
849 m_format_string.push_back(*pos);
850 ++pos;
851 }
852 m_format_string.push_back('}');
853 return pos;
854 }
855
856 auto format(const sparrow::nullable_variant<T...>& variant, std::format_context& ctx) const
857 {
858 if (variant.has_value())
859 {
860 return std::visit(
861 [&](const auto& value)
862 {
863 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(value));
864 },
865 variant
866 );
867 }
868 else
869 {
870 return std::format_to(ctx.out(), "{}", "null");
871 }
872 }
873
874 std::string m_format_string = "{:";
875};
876
877template <class... T>
878std::ostream& operator<<(std::ostream& os, const sparrow::nullable_variant<T...>& value)
879{
880 os << std::format("{}", value);
881 return os;
882}
883
884template <>
885struct std::formatter<sparrow::nullval_t>
886{
887 constexpr auto parse(format_parse_context& ctx)
888 {
889 return ctx.begin(); // Simple implementation
890 }
891
892 auto format(const sparrow::nullval_t&, std::format_context& ctx) const
893 {
894 return std::format_to(ctx.out(), "nullval");
895 }
896};
897
898#endif
899
900inline std::ostream& operator<<(std::ostream& os, const sparrow::nullval_t&)
901{
902 constexpr std::string_view nullval_str = "nullval";
903 os << nullval_str;
904 return os;
905}
906
907#undef SPARROW_CONSTEXPR
const char * what() const noexcept override
Definition nullable.hpp:127
bad_nullable_access() noexcept=default
constexpr nullable_variant(nullable_variant &&) noexcept=default
constexpr nullable_variant(const nullable_variant &)=default
constexpr bool has_value() const
Definition nullable.hpp:790
std::variant< T... > base_type
Definition nullable.hpp:524
The nullable class models a value or a reference that can be "null", or missing, like values traditio...
Definition nullable.hpp:280
nullable_traits< bool > flag_traits
Definition nullable.hpp:290
explicit(not std::convertible_to< U &&, inner_value_type >) const expr nullable(U &&value) noexcept(noexcept(inner_value_type(std::declval< U >())))
Definition nullable.hpp:313
constexpr nullable(std::add_lvalue_reference_t< T > value, flag_type &&null_flag)
Definition nullable.hpp:385
typename value_traits::const_reference const_reference
Definition nullable.hpp:287
constexpr nullable(value_type &&value, flag_type &&null_flag)
Definition nullable.hpp:367
constexpr nullable() noexcept
Definition nullable.hpp:298
friend class nullable
Definition nullable.hpp:481
typename flag_traits::reference flag_reference
Definition nullable.hpp:292
constexpr nullable(nullval_t) noexcept
Definition nullable.hpp:305
typename flag_traits::const_reference flag_const_reference
Definition nullable.hpp:293
constexpr self_type & operator=(nullval_t)
Definition nullable.hpp:391
typename flag_traits::value_type flag_type
Definition nullable.hpp:291
nullable< inner_value_type, bool > self_type
Definition nullable.hpp:283
constexpr bool has_value() const noexcept
void swap(self_type &other) noexcept
constexpr reference get() &noexcept
typename value_traits::rvalue_reference rvalue_reference
Definition nullable.hpp:288
typename value_traits::value_type value_type
Definition nullable.hpp:285
constexpr nullable(value_type &&value, std::add_lvalue_reference_t< B > null_flag)
Definition nullable.hpp:379
constexpr value_type value_or(U &&default_value) const &
constexpr nullable(const self_type &)=default
typename flag_traits::const_rvalue_reference flag_const_rvalue_reference
Definition nullable.hpp:295
constexpr self_type & operator=(self_type &&rhs)
Definition nullable.hpp:426
constexpr flag_reference null_flag() &noexcept
typename flag_traits::rvalue_reference flag_rvalue_reference
Definition nullable.hpp:294
typename value_traits::reference reference
Definition nullable.hpp:286
constexpr nullable(std::add_lvalue_reference_t< T > value, std::add_lvalue_reference_t< B > null_flag)
Definition nullable.hpp:373
nullable_traits< inner_value_type > value_traits
Definition nullable.hpp:284
constexpr nullable(self_type &&) noexcept=default
typename value_traits::const_rvalue_reference const_rvalue_reference
Definition nullable.hpp:289
constexpr self_type & operator=(const self_type &rhs)
Definition nullable.hpp:406
Concepts used to disambiguate the nullable class constructors.
Definition nullable.hpp:160
static constexpr bool is_nullable_v
Definition nullable.hpp:216
std::conditional_t< std::is_reference_v< T >, const std::decay_t< U > &, std::decay_t< U > && > conditional_ref_t
Definition nullable.hpp:189
constexpr bool is_type_instance_of_v
true if T is a concrete type template instanciation of U which is a type template.
Definition mp_utils.hpp:50
typename add_const_lvalue_reference< T >::type add_const_lvalue_reference_t
Definition mp_utils.hpp:373
constexpr std::compare_three_way_result_t< typename cloning_ptr< T1 >::pointer, typename cloning_ptr< T2 >::pointer > operator<=>(const cloning_ptr< T1 > &lhs, const cloning_ptr< T2 > &rhs) noexcept
Definition memory.hpp:475
SPARROW_API bool operator==(const array &lhs, const array &rhs)
Compares the content of two arrays.
SPARROW_API void swap(ArrowArray &lhs, ArrowArray &rhs)
Swaps the contents of the two ArrowArray objects.
constexpr nullval_t nullval(0)
constexpr bool is_nullable_v
Definition nullable.hpp:60
constexpr nullable< T, B > make_nullable(T &&value, B &&flag=true)
Definition nullable.hpp:756
std::ostream & operator<<(std::ostream &os, const sparrow::nullval_t &)
Definition nullable.hpp:900
#define SPARROW_CONSTEXPR
Definition nullable.hpp:41
const_reference const_rvalue_reference
Definition nullable.hpp:112
std::add_lvalue_reference_t< value_type > reference
Definition nullable.hpp:109
std::add_lvalue_reference_t< std::add_const_t< value_type > > const_reference
Definition nullable.hpp:110
const value_type && const_rvalue_reference
Definition nullable.hpp:102
std::add_lvalue_reference_t< value_type > reference
Definition nullable.hpp:99
value_type && rvalue_reference
Definition nullable.hpp:101
std::add_lvalue_reference_t< std::add_const_t< value_type > > const_reference
Definition nullable.hpp:100
nullval_t is an empty class used to indicate that a nullable is null.
Definition nullable.hpp:141
constexpr nullval_t(int)
Definition nullable.hpp:147
sparrow:: nullable< std::common_reference_t< TQual< T >, UQual< U > >, std::common_reference_t< TQual< TB >, UQual< UB > > > type
Definition nullable.hpp:547