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 is_nullable_of = is_nullable_v<N> && std::same_as<typename N::value_type, T>;
65
66 template <class N, class T>
67 concept is_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 template <class T>
218 }
219
279 template <class T, mpl::boolean_like B = bool>
281 {
282 public:
283
297
298 template <std::default_initializable U = T, std::default_initializable BB = B>
299 constexpr nullable() noexcept
300 : m_value()
301 , m_null_flag(false)
302 {
303 }
304
305 template <std::default_initializable U = T, std::default_initializable BB = B>
306 constexpr nullable(nullval_t) noexcept
307 : m_value()
308 , m_null_flag(false)
309 {
310 }
311
312 template <class U>
313 requires(not std::same_as<self_type, std::decay_t<U>> and std::constructible_from<T, U &&>)
314 explicit(not std::convertible_to<U&&, T>) constexpr nullable(U&& value) noexcept(
315 noexcept(T(std::declval<U>()))
316 )
317 : m_value(std::forward<U>(value))
318 , m_null_flag(true)
319 {
320 }
321
322 constexpr nullable(const self_type&) = default;
323
324 template <class TO, mpl::boolean_like BO>
329 )
330 : m_value(rhs.get())
331 , m_null_flag(rhs.null_flag())
332 {
333 }
334
335#ifdef __clang__
336 template <class TO, mpl::boolean_like BO>
337 requires(impl::both_constructible_from_cref<T, TO, B, BO> and std::same_as<std::decay_t<T>, bool>)
339 const nullable<TO, BO>& rhs
340 )
341 : m_value(rhs.get())
342 , m_null_flag(rhs.null_flag())
343 {
344 }
345#endif
346
347 constexpr nullable(self_type&&) noexcept = default;
348
349 template <class TO, mpl::boolean_like BO>
350 requires(impl::both_constructible_from_cond_ref<T, TO, B, BO>
351 and not impl::initializable_from_refs<T, nullable<TO, BO>>)
352 explicit(not impl::both_convertible_from_cond_ref<T, TO, B, BO>) SPARROW_CONSTEXPR nullable(
353 nullable<TO, BO>&& rhs
354 )
355 : m_value(std::move(rhs).get())
356 , m_null_flag(std::move(rhs).null_flag())
357 {
358 }
359
360#ifdef __clang__
361 template <class TO, mpl::boolean_like BO>
363 and std::same_as<std::decay_t<T>, bool>)
366 )
367 : m_value(std::move(rhs).get())
368 , m_null_flag(std::move(rhs).null_flag())
369 {
370 }
371#endif
372
374 : m_value(std::move(value))
375 , m_null_flag(std::move(null_flag))
376 {
377 }
378
379 constexpr nullable(std::add_lvalue_reference_t<T> value, std::add_lvalue_reference_t<B> null_flag)
380 : m_value(value)
381 , m_null_flag(null_flag)
382 {
383 }
384
385 constexpr nullable(value_type&& value, std::add_lvalue_reference_t<B> null_flag)
386 : m_value(std::move(value))
387 , m_null_flag(null_flag)
388 {
389 }
390
391 constexpr nullable(std::add_lvalue_reference_t<T> value, flag_type&& null_flag)
392 : m_value(value)
393 , m_null_flag(std::move(null_flag))
394 {
395 }
396
397 constexpr self_type& operator=(nullval_t) noexcept
398 {
399 m_null_flag = false;
400 return *this;
401 }
402
403 template <class TO>
404 requires(not std::same_as<self_type, TO> and std::assignable_from<std::add_lvalue_reference_t<T>, TO>)
405 constexpr self_type& operator=(TO&& rhs) noexcept
406 {
407 m_value = std::forward<TO>(rhs);
408 m_null_flag = true;
409 return *this;
410 }
411
412 constexpr self_type& operator=(const self_type& rhs) noexcept
413 {
414 m_value = rhs.get();
415 m_null_flag = rhs.null_flag();
416 return *this;
417 }
418
419 template <class TO, mpl::boolean_like BO>
420 requires(
424 )
425 constexpr self_type& operator=(const nullable<TO, BO>& rhs) noexcept
426 {
427 m_value = rhs.get();
428 m_null_flag = rhs.null_flag();
429 return *this;
430 }
431
432 constexpr self_type& operator=(self_type&& rhs) noexcept
433 {
434 m_value = std::move(rhs).get();
435 m_null_flag = std::move(rhs).null_flag();
436 return *this;
437 }
438
439 template <class TO, mpl::boolean_like BO>
440 requires(
444 )
445 constexpr self_type& operator=(nullable<TO, BO>&& rhs) noexcept
446 {
447 m_value = std::move(rhs).get();
448 m_null_flag = std::move(rhs).null_flag();
449 return *this;
450 }
451
452 constexpr explicit operator bool() const noexcept;
453 [[nodiscard]] constexpr bool has_value() const noexcept;
454
455 [[nodiscard]] constexpr flag_reference null_flag() & noexcept;
456 [[nodiscard]] constexpr flag_const_reference null_flag() const& noexcept;
457 [[nodiscard]] constexpr flag_rvalue_reference null_flag() && noexcept;
458 [[nodiscard]] constexpr flag_const_rvalue_reference null_flag() const&& noexcept;
459
460 [[nodiscard]] constexpr reference get() & noexcept;
461 [[nodiscard]] constexpr const_reference get() const& noexcept;
462 [[nodiscard]] constexpr rvalue_reference get() && noexcept;
463 [[nodiscard]] constexpr const_rvalue_reference get() const&& noexcept;
464
465 [[nodiscard]] constexpr reference value() &;
466 [[nodiscard]] constexpr const_reference value() const&;
467 [[nodiscard]] constexpr rvalue_reference value() &&;
468 [[nodiscard]] constexpr const_rvalue_reference value() const&&;
469
470 template <class U>
471 [[nodiscard]] constexpr value_type value_or(U&& default_value) const&;
472
473 template <class U>
474 [[nodiscard]] constexpr value_type value_or(U&& default_value) &&;
475
476 void swap(self_type& other) noexcept;
477 void reset() noexcept;
478
479 private:
480
481 void throw_if_null() const;
482
483 T m_value;
484 B m_null_flag;
485
486 template <class TO, mpl::boolean_like BO>
487 friend class nullable;
488 };
489
490 template <class T, class B>
491 constexpr void swap(nullable<T, B>& lhs, nullable<T, B>& rhs) noexcept;
492
493 template <class T, class B>
494 constexpr bool operator==(const nullable<T, B>& lhs, nullval_t) noexcept;
495
496 template <class T, mpl::boolean_like B>
497 constexpr std::strong_ordering operator<=>(const nullable<T, B>& lhs, nullval_t) noexcept;
498
499 template <class T, class B, class U>
500 constexpr bool operator==(const nullable<T, B>& lhs, const U& rhs) noexcept;
501
502 template <class T, class B, class U>
503 requires(!impl::is_nullable_v<U> && std::three_way_comparable_with<U, T>)
504 constexpr std::compare_three_way_result_t<T, U>
505 operator<=>(const nullable<T, B>& lhs, const U& rhs) noexcept;
506
507 template <class T, class B, class U, class UB>
508 constexpr bool operator==(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept;
509
510 template <class T, class B, std::three_way_comparable_with<T> U, class UB>
511 constexpr std::compare_three_way_result_t<T, U>
512 operator<=>(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept;
513
514 // Even if we have CTAD in C++20, some constructors add lvalue reference
515 // to their argument, making the deduction impossible.
516 template <class T, mpl::boolean_like B = bool>
517 constexpr nullable<T, B> make_nullable(T&& value, B&& flag = true);
518
519 template <std::ranges::range R, typename T = typename std::ranges::range_value_t<R>::value_type>
520 requires(
521 mpl::is_type_instance_of_v<std::ranges::range_value_t<R>, nullable>
522 && std::is_same_v<typename std::ranges::range_value_t<R>::value_type, T>
523 )
524 constexpr void zero_null_values(R& range, const T& default_value = T{});
525
531 template <class... T>
532 requires(sizeof...(T) > 0 && (is_nullable_v<T> && ...))
533 class nullable_variant : public std::variant<T...>
534 {
535 public:
536
537 using base_type = std::variant<T...>;
538 using base_type::base_type;
539
540 constexpr nullable_variant(const nullable_variant&) = default;
541 constexpr nullable_variant(nullable_variant&&) noexcept = default;
542
543 constexpr nullable_variant& operator=(const nullable_variant&);
544 constexpr nullable_variant& operator=(nullable_variant&&) noexcept;
545
546 constexpr explicit operator bool() const;
547 constexpr bool has_value() const;
548 };
549}
550
551namespace std
552{
553 namespace mpl = sparrow::mpl;
554
555 // Specialization of basic_common_reference for nullable proxies so
556 // we can use ranges algorithm on iterators returning nullable
557 template <class T, mpl::boolean_like TB, class U, mpl::boolean_like UB, template <class> class TQual, template <class> class UQual>
558 struct basic_common_reference<sparrow::nullable<T, TB>, sparrow::nullable<U, UB>, TQual, UQual>
559 {
560 using type = sparrow::
561 nullable<std::common_reference_t<TQual<T>, UQual<U>>, std::common_reference_t<TQual<TB>, UQual<UB>>>;
562 };
563}
564
565namespace sparrow
566{
567 /***************************
568 * nullable implementation *
569 ***************************/
570
571 template <class T, mpl::boolean_like B>
572 constexpr nullable<T, B>::operator bool() const noexcept
573 {
574 return m_null_flag;
575 }
576
577 template <class T, mpl::boolean_like B>
578 constexpr bool nullable<T, B>::has_value() const noexcept
579 {
580 return m_null_flag;
581 }
582
583 template <class T, mpl::boolean_like B>
584 constexpr auto nullable<T, B>::null_flag() & noexcept -> flag_reference
585 {
586 return m_null_flag;
587 }
588
589 template <class T, mpl::boolean_like B>
590 constexpr auto nullable<T, B>::null_flag() const& noexcept -> flag_const_reference
591 {
592 return m_null_flag;
593 }
594
595 template <class T, mpl::boolean_like B>
596 constexpr auto nullable<T, B>::null_flag() && noexcept -> flag_rvalue_reference
597 {
598 if constexpr (std::is_reference_v<B>)
599 {
600 return m_null_flag;
601 }
602 else
603 {
604 return flag_rvalue_reference(m_null_flag);
605 }
606 }
607
608 template <class T, mpl::boolean_like B>
609 constexpr auto nullable<T, B>::null_flag() const&& noexcept -> flag_const_rvalue_reference
610 {
611 if constexpr (std::is_reference_v<B>)
612 {
613 return m_null_flag;
614 }
615 else
616 {
617 return flag_const_rvalue_reference(m_null_flag);
618 }
619 }
620
621 template <class T, mpl::boolean_like B>
622 constexpr auto nullable<T, B>::get() & noexcept -> reference
623 {
624 return m_value;
625 }
626
627 template <class T, mpl::boolean_like B>
628 constexpr auto nullable<T, B>::get() const& noexcept -> const_reference
629 {
630 return m_value;
631 }
632
633 template <class T, mpl::boolean_like B>
634 constexpr auto nullable<T, B>::get() && noexcept -> rvalue_reference
635 {
636 if constexpr (std::is_reference_v<T>)
637 {
638 return m_value;
639 }
640 else
641 {
642 return rvalue_reference(m_value);
643 }
644 }
645
646 template <class T, mpl::boolean_like B>
647 constexpr auto nullable<T, B>::get() const&& noexcept -> const_rvalue_reference
648 {
649 if constexpr (std::is_reference_v<T>)
650 {
651 return m_value;
652 }
653 else
654 {
655 return const_rvalue_reference(m_value);
656 }
657 }
658
659 template <class T, mpl::boolean_like B>
660 constexpr auto nullable<T, B>::value() & -> reference
661 {
662 throw_if_null();
663 return get();
664 }
665
666 template <class T, mpl::boolean_like B>
667 constexpr auto nullable<T, B>::value() const& -> const_reference
668 {
669 throw_if_null();
670 return get();
671 }
672
673 template <class T, mpl::boolean_like B>
675 {
676 throw_if_null();
677 return std::move(*this).get();
678 }
679
680 template <class T, mpl::boolean_like B>
682 {
683 throw_if_null();
684 return std::move(*this).get();
685 }
686
687 template <class T, mpl::boolean_like B>
688 template <class U>
689 constexpr auto nullable<T, B>::value_or(U&& default_value) const& -> value_type
690 {
691 return *this ? get() : value_type(std::forward<U>(default_value));
692 }
693
694 template <class T, mpl::boolean_like B>
695 template <class U>
696 constexpr auto nullable<T, B>::value_or(U&& default_value) && -> value_type
697 {
698 return *this ? get() : value_type(std::forward<U>(default_value));
699 }
700
701 template <class T, mpl::boolean_like B>
702 void nullable<T, B>::swap(self_type& other) noexcept
703 {
704 using std::swap;
705 swap(m_value, other.m_value);
706 swap(m_null_flag, other.m_null_flag);
707 }
708
709 template <class T, mpl::boolean_like B>
710 void nullable<T, B>::reset() noexcept
711 {
712 m_null_flag = false;
713 }
714
715 template <class T, mpl::boolean_like B>
716 void nullable<T, B>::throw_if_null() const
717 {
718 if (!m_null_flag)
719 {
720 throw bad_nullable_access{};
721 }
722 }
723
724 template <class T, class B>
725 constexpr void swap(nullable<T, B>& lhs, nullable<T, B>& rhs) noexcept
726 {
727 lhs.swap(rhs);
728 }
729
730 template <class T, class B>
731 constexpr bool operator==(const nullable<T, B>& lhs, nullval_t) noexcept
732 {
733 return !lhs;
734 }
735
736 template <class T, class B>
737 constexpr std::strong_ordering operator<=>(const nullable<T, B>& lhs, nullval_t) noexcept
738 {
739 return lhs <=> false;
740 }
741
742 template <class T, class B, class U>
743 constexpr bool operator==(const nullable<T, B>& lhs, const U& rhs) noexcept
744 {
745 return lhs && (lhs.get() == rhs);
746 }
747
748 template <class T, class B, class U>
749 requires(!impl::is_nullable_v<U> && std::three_way_comparable_with<U, T>)
750 constexpr std::compare_three_way_result_t<T, U> operator<=>(const nullable<T, B>& lhs, const U& rhs) noexcept
751 {
752 return lhs ? lhs.get() <=> rhs : std::strong_ordering::less;
753 }
754
755 template <class T, class B, class U, class UB>
756 constexpr bool operator==(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept
757 {
758 return rhs ? lhs == rhs.get() : !lhs;
759 }
760
761 template <class T, class B, std::three_way_comparable_with<T> U, class UB>
762 constexpr std::compare_three_way_result_t<T, U>
763 operator<=>(const nullable<T, B>& lhs, const nullable<U, UB>& rhs) noexcept
764 {
765 return (lhs && rhs) ? lhs.get() <=> rhs.get() : bool(lhs) <=> bool(rhs);
766 }
767
768 template <class T, mpl::boolean_like B>
769 constexpr nullable<T, B> make_nullable(T&& value, B&& flag)
770 {
771 return nullable<T, B>(std::forward<T>(value), std::forward<B>(flag));
772 }
773
774 template <std::ranges::range R, typename T>
775 requires(
777 && std::is_same_v<typename std::ranges::range_value_t<R>::value_type, T>
778 )
779 constexpr void zero_null_values(R& range, const T& default_value)
780 {
781 for (auto nullable_value : range)
782 {
783 if (!nullable_value.has_value())
784 {
785 nullable_value.get() = default_value;
786 }
787 }
788 }
789
790 /***********************************
791 * nullable_variant implementation *
792 ***********************************/
793
794 template <class... T>
795 requires(sizeof...(T) > 0 && (is_nullable_v<T> && ...))
796 constexpr nullable_variant<T...>& nullable_variant<T...>::operator=(const nullable_variant& rhs)
797 {
798 base_type::operator=(rhs);
799 return *this;
800 }
801
802 template <class... T>
803 requires(sizeof...(T) > 0 && (is_nullable_v<T> && ...))
804 constexpr nullable_variant<T...>& nullable_variant<T...>::operator=(nullable_variant&& rhs) noexcept
805 {
806 base_type::operator=(std::move(rhs));
807 return *this;
808 }
809
810 template <class... T>
811 requires(sizeof...(T) > 0 && (is_nullable_v<T> && ...))
812 constexpr nullable_variant<T...>::operator bool() const
813 {
814 return has_value();
815 }
816
817 template <class... T>
818 requires(sizeof...(T) > 0 && (is_nullable_v<T> && ...))
819 constexpr bool nullable_variant<T...>::has_value() const
820 {
821 return std::visit(
822 [](const auto& v)
823 {
824 return v.has_value();
825 },
826#if SPARROW_GCC_11_2_WORKAROUND
827 static_cast<const base_type&>(*this)
828#else
829 *this
830#endif
831 );
832 }
833}
834
835#if defined(__cpp_lib_format)
836
837template <typename T, sparrow::mpl::boolean_like B>
838struct std::formatter<sparrow::nullable<T, B>>
839{
840 constexpr auto parse(format_parse_context& ctx)
841 {
842 auto pos = ctx.begin();
843 while (pos != ctx.end() && *pos != '}')
844 {
845 m_format_string.push_back(*pos);
846 ++pos;
847 }
848 m_format_string.push_back('}');
849 return pos;
850 }
851
852 auto format(const sparrow::nullable<T, B>& n, std::format_context& ctx) const
853 {
854 if (n.has_value())
855 {
856 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(n.get()));
857 }
858 else
859 {
860 return std::format_to(ctx.out(), "{}", "null");
861 }
862 }
863
864 std::string m_format_string = "{:";
865};
866
867template <typename T, sparrow::mpl::boolean_like B>
868std::ostream& operator<<(std::ostream& os, const sparrow::nullable<T, B>& value)
869{
870 os << std::format("{}", value);
871 return os;
872}
873
874template <class... T>
875struct std::formatter<sparrow::nullable_variant<T...>>
876{
877 constexpr auto parse(format_parse_context& ctx)
878 {
879 auto pos = ctx.begin();
880 while (pos != ctx.end() && *pos != '}')
881 {
882 m_format_string.push_back(*pos);
883 ++pos;
884 }
885 m_format_string.push_back('}');
886 return pos;
887 }
888
889 auto format(const sparrow::nullable_variant<T...>& variant, std::format_context& ctx) const
890 {
891 if (variant.has_value())
892 {
893 return std::visit(
894 [&](const auto& value)
895 {
896 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(value));
897 },
898 variant
899 );
900 }
901 else
902 {
903 return std::format_to(ctx.out(), "{}", "null");
904 }
905 }
906
907 std::string m_format_string = "{:";
908};
909
910template <class... T>
911std::ostream& operator<<(std::ostream& os, const sparrow::nullable_variant<T...>& value)
912{
913 os << std::format("{}", value);
914 return os;
915}
916
917template <>
918struct std::formatter<sparrow::nullval_t>
919{
920 constexpr auto parse(format_parse_context& ctx)
921 {
922 return ctx.begin(); // Simple implementation
923 }
924
925 auto format(const sparrow::nullval_t&, std::format_context& ctx) const
926 {
927 return std::format_to(ctx.out(), "nullval");
928 }
929};
930
931#endif
932
933inline std::ostream& operator<<(std::ostream& os, const sparrow::nullval_t&)
934{
935 constexpr std::string_view nullval_str = "nullval";
936 os << nullval_str;
937 return os;
938}
939
940#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:819
std::variant< T... > base_type
Definition nullable.hpp:537
The nullable class models a value or a reference that can be "null", or missing, like values traditio...
Definition nullable.hpp:281
nullable_traits< bool > flag_traits
Definition nullable.hpp:291
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:314
constexpr nullable(std::add_lvalue_reference_t< T > value, flag_type &&null_flag)
Definition nullable.hpp:391
typename value_traits::const_reference const_reference
Definition nullable.hpp:288
constexpr nullable(value_type &&value, flag_type &&null_flag)
Definition nullable.hpp:373
constexpr nullable() noexcept
Definition nullable.hpp:299
friend class nullable
Definition nullable.hpp:487
typename flag_traits::reference flag_reference
Definition nullable.hpp:293
constexpr nullable(nullval_t) noexcept
Definition nullable.hpp:306
typename flag_traits::const_reference flag_const_reference
Definition nullable.hpp:294
typename flag_traits::value_type flag_type
Definition nullable.hpp:292
nullable< inner_value_type, bool > self_type
Definition nullable.hpp:284
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:289
typename value_traits::value_type value_type
Definition nullable.hpp:286
constexpr nullable(value_type &&value, std::add_lvalue_reference_t< B > null_flag)
Definition nullable.hpp:385
constexpr self_type & operator=(const self_type &rhs) noexcept
Definition nullable.hpp:412
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:296
constexpr self_type & operator=(nullval_t) noexcept
Definition nullable.hpp:397
constexpr flag_reference null_flag() &noexcept
typename flag_traits::rvalue_reference flag_rvalue_reference
Definition nullable.hpp:295
constexpr self_type & operator=(self_type &&rhs) noexcept
Definition nullable.hpp:432
typename value_traits::reference reference
Definition nullable.hpp:287
constexpr nullable(std::add_lvalue_reference_t< T > value, std::add_lvalue_reference_t< B > null_flag)
Definition nullable.hpp:379
nullable_traits< inner_value_type > value_traits
Definition nullable.hpp:285
constexpr nullable(self_type &&) noexcept=default
typename value_traits::const_rvalue_reference const_rvalue_reference
Definition nullable.hpp:290
Concepts used to disambiguate the nullable class constructors.
Definition nullable.hpp:161
static constexpr bool is_nullable_v
Definition nullable.hpp:217
std::conditional_t< std::is_reference_v< T >, const std::decay_t< U > &, std::decay_t< U > && > conditional_ref_t
Definition nullable.hpp:190
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:474
constexpr void zero_null_values(R &range, const T &default_value=T{})
Definition nullable.hpp:779
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:769
std::ostream & operator<<(std::ostream &os, const sparrow::nullval_t &)
Definition nullable.hpp:933
#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:560