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