21#if __has_include(<format>)
32#if defined(SPARROW_CONSTEXPR)
33# error "SPARROW_CONSTEXPR already defined"
40#if defined(__clang__) && not defined(_LIBCPP_VERSION)
41# define SPARROW_CONSTEXPR
43# define SPARROW_CONSTEXPR constexpr
48 template <
class T, mpl::
boolean_like B>
56 template <
class T, mpl::
boolean_like B>
64 template <
class N,
class T>
67 template <
class N,
class T>
69 && std::convertible_to<typename N::stored_value_type, T>;
78 template <
class RangeOfNullables>
88 constexpr decltype(
auto)
operator()(N&& nullable_value)
const
90 if constexpr (std::is_lvalue_reference_v<N&&>)
92 return std::forward<N>(nullable_value).get();
96 using value_type = std::remove_cvref_t<decltype(std::forward<N>(nullable_value).get())>;
97 return value_type(std::forward<N>(nullable_value).get());
124 using reference = std::add_lvalue_reference_t<value_type>;
134 using reference = std::add_lvalue_reference_t<value_type>;
161 [[nodiscard]] const
char*
what() const noexcept
override
168 static constexpr const char* message =
"Invalid access to nullable underlying value";
207 template <
class T,
class TArgs,
class U,
class UArgs>
209 and std::constructible_from<U, mpl::add_const_lvalue_reference_t<UArgs>>;
211 template <
class To1,
class From1,
class To2,
class From2>
213 and std::convertible_to<mpl::add_const_lvalue_reference_t<From2>, To2>;
215 template <
class T,
class... Args>
218 template <
class T,
class Arg>
224 template <
class To,
class... Args>
227 template <
class T,
class Arg>
230 template <
class T,
class U>
231 using conditional_ref_t = std::conditional_t<std::is_reference_v<T>,
const std::decay_t<U>&, std::decay_t<U>&&>;
233 template <
class T,
class Targs,
class U,
class UArgs>
235 and std::constructible_from<U, conditional_ref_t<U, UArgs>>;
237 template <
class To1,
class From1,
class To2,
class From2>
239 and std::convertible_to<conditional_ref_t<To2, From2>, To2>;
241 template <
class To1,
class From1,
class To2,
class From2>
243 std::add_lvalue_reference_t<To1>,
245 and std::is_assignable_v<
246 std::add_lvalue_reference_t<To2>,
249 template <
class To1,
class From1,
class To2,
class From2>
251 std::add_lvalue_reference_t<To1>,
253 and std::is_assignable_v<
254 std::add_lvalue_reference_t<To2>,
268 using type = std::remove_cvref_t<T>;
285 template <
class X,
class... Ts>
294 template <
class Source,
class Dest>
300 template <
class... U,
class... T>
302 : std::bool_constant<(unique_alternative<materialized_nullable_t<U>, T...> and ...)>
313 template <
class Source,
class Dest>
319 template <
class Dest,
class Source>
323 []<
class N>(
const N& value) -> Dest
326 if constexpr (std::constructible_from<destination_nullable, const N&>)
328 return Dest(std::in_place_type<destination_nullable>, value);
334 std::in_place_type<destination_nullable>,
335 typename destination_nullable::value_type(
343 static_cast<const typename Source::base_type&
>(source)
394 template <
class T, mpl::
boolean_like B =
bool>
425 template <std::default_initializable U = T, std::default_initializable BB = B>
443 template <std::default_initializable U = T, std::default_initializable BB = B>
463 requires(not std::same_as<self_type, std::decay_t<U>> and std::constructible_from<T, U &&>)
464 explicit(not std::convertible_to<U&&, T>)
constexpr nullable(U&&
value)
noexcept(
465 noexcept(T(std::declval<U>()))
498 template <
class TO, mpl::
boolean_like BO>
530 template <
class TO, mpl::
boolean_like BO>
567 template <
class TO, mpl::
boolean_like BO>
600 template <
class TO, mpl::
boolean_like BO>
602 and std::same_as<std::decay_t<T>,
bool>)
683 template <
class U,
class V>
684 requires(std::same_as<std::remove_cvref_t<U>, T> && std::same_as<std::remove_cvref_t<V>, B>
685 && std::is_const_v<std::remove_reference_t<U>>
686 && not std::is_const_v<std::remove_reference_t<V>> && not std::is_reference_v<T>
687 && not std::is_reference_v<B> && std::is_lvalue_reference_v<U &&>
688 && std::is_lvalue_reference_v<V &&>)
708 template <
class U,
class V>
709 requires(std::same_as<std::remove_cvref_t<U>, T> && std::same_as<std::remove_cvref_t<V>, B>
710 && not std::is_reference_v<T> && not std::is_reference_v<B>
711 && (std::is_const_v<std::remove_reference_t<U>>
712 || std::is_const_v<std::remove_reference_t<V>>) )
749 requires(not std::same_as<self_type, TO> and std::assignable_from<std::add_lvalue_reference_t<T>, TO>)
751 std::is_nothrow_assignable_v<std::add_lvalue_reference_t<T>, TO>
752 && std::is_nothrow_assignable_v<std::add_lvalue_reference_t<B>,
bool>
755 m_value = std::forward<TO>(
rhs);
772 std::is_nothrow_assignable_v<std::add_lvalue_reference_t<T>,
const_reference>
777 m_null_flag =
rhs.null_flag();
796 template <
class TO, mpl::
boolean_like BO>
824 std::is_nothrow_assignable_v<std::add_lvalue_reference_t<T>,
rvalue_reference>
828 m_value = std::move(
rhs).get();
829 m_null_flag = std::move(
rhs).null_flag();
848 template <
class TO, mpl::
boolean_like BO>
859 m_value = std::move(
rhs).get();
860 m_null_flag = std::move(
rhs).null_flag();
872 constexpr explicit operator bool() const noexcept;
882 [[nodiscard]] constexpr
bool has_value() const noexcept;
1067 void throw_if_null() const;
1072 template <class TO,
mpl::boolean_like BO>
1087 template <class T, class B>
1101 template <class T, class B>
1116 template <class T,
mpl::boolean_like B>
1117 constexpr
std::strong_ordering operator<=>(const
nullable<T, B>& lhs,
nullval_t dummy) noexcept;
1132 template <class T, class B, class U>
1134 constexpr
bool operator==(const
nullable<T, B>& lhs, const U&
rhs) noexcept;
1150 template <class T, class B, class U>
1152 constexpr
std::compare_three_way_result_t<T, U>
1153 operator<=>(const
nullable<T, B>& lhs, const U&
rhs) noexcept;
1170 template <class T, class B, class U, class UB>
1171 requires(
mpl::weakly_equality_comparable_with<T, U>)
1172 constexpr
bool operator==(const
nullable<T, B>& lhs, const
nullable<U, UB>&
rhs) noexcept;
1190 template <class T, class B,
std::three_way_comparable_with<T> U, class UB>
1191 constexpr
std::compare_three_way_result_t<T, U>
1206 template <class T,
mpl::boolean_like B =
bool>
1239 template <
class... T>
1246 using base_type::base_type;
1248 template <
class U, mpl::
boolean_like B>
1249 requires((std::same_as<nullable<U, B>, T> || ...))
1268 template <
class... U>
1287 template <
class... U>
1326 constexpr explicit operator
bool() const;
1343 template <
class... T>
1358 template <
class T, mpl::
boolean_like TB,
class U, mpl::
boolean_like UB,
template <
class>
class TQual,
template <
class>
class UQual>
1359 requires std::common_reference_with<T, U> && std::common_reference_with<TB, UB>
1363 nullable<std::common_reference_t<TQual<T>, UQual<U>>, std::common_reference_t<TQual<TB>, UQual<UB>>>;
1373 template <
class T, mpl::
boolean_like B>
1379 template <
class T, mpl::
boolean_like B>
1385 template <
class T, mpl::
boolean_like B>
1391 template <
class T, mpl::
boolean_like B>
1397 template <
class T, mpl::
boolean_like B>
1400 if constexpr (std::is_reference_v<B>)
1410 template <
class T, mpl::
boolean_like B>
1413 if constexpr (std::is_reference_v<B>)
1423 template <
class T, mpl::
boolean_like B>
1429 template <
class T, mpl::
boolean_like B>
1435 template <
class T, mpl::
boolean_like B>
1438 if constexpr (std::is_reference_v<T>)
1448 template <
class T, mpl::
boolean_like B>
1451 if constexpr (std::is_reference_v<T>)
1461 template <
class T, mpl::
boolean_like B>
1468 template <
class T, mpl::
boolean_like B>
1475 template <
class T, mpl::
boolean_like B>
1479 return std::move(*this).get();
1482 template <
class T, mpl::
boolean_like B>
1486 return std::move(*this).get();
1489 template <
class T, mpl::
boolean_like B>
1493 return *
this ?
get() :
value_type(std::forward<U>(default_value));
1496 template <
class T, mpl::
boolean_like B>
1500 return *
this ?
get() :
value_type(std::forward<U>(default_value));
1503 template <
class T, mpl::
boolean_like B>
1507 swap(m_value, other.m_value);
1508 swap(m_null_flag, other.m_null_flag);
1511 template <
class T, mpl::
boolean_like B>
1514 m_null_flag =
false;
1517 template <
class T, mpl::
boolean_like B>
1518 void nullable<T, B>::throw_if_null()
const
1526 template <
class T,
class B>
1532 template <
class T,
class B>
1538 template <
class T,
class B>
1541 return lhs <=>
false;
1544 template <
class T,
class B,
class U>
1548 return lhs && (lhs.
get() == rhs);
1551 template <
class T,
class B,
class U>
1555 return lhs ? lhs.
get() <=> rhs : std::strong_ordering::less;
1558 template <
class T,
class B,
class U,
class UB>
1559 requires(mpl::weakly_equality_comparable_with<T, U>)
1562 return rhs ? lhs == rhs.
get() : !lhs;
1565 template <
class T,
class B, std::three_way_comparable_with<T> U,
class UB>
1566 constexpr std::compare_three_way_result_t<T, U>
1569 return (lhs && rhs) ? lhs.
get() <=> rhs.
get() : bool(lhs) <=> bool(rhs);
1572 template <
class T, mpl::
boolean_like B>
1575 return nullable<T, B>(std::forward<T>(value), std::forward<B>(flag));
1578 template <std::ranges::range R,
typename T>
1579 requires(nullable_of<std::ranges::range_value_t<R>, T>)
1582 for (
auto nullable_value : range)
1584 if (!nullable_value.has_value())
1586 nullable_value.get() = default_value;
1595 template <
class... T>
1599 base_type::operator=(rhs);
1603 template <
class... T>
1607 base_type::operator=(std::move(rhs));
1611 template <
class... T>
1618 template <
class... T>
1625 return v.has_value();
1627#if SPARROW_GCC_11_2_WORKAROUND
1636#if defined(__cpp_lib_format)
1638template <
typename T, sparrow::mpl::
boolean_like B>
1639struct std::formatter<
sparrow::nullable<T, B>>
1641 constexpr auto parse(format_parse_context& ctx)
1643 auto pos = ctx.begin();
1644 while (pos != ctx.end() && *pos !=
'}')
1646 m_format_string.push_back(*pos);
1649 m_format_string.push_back(
'}');
1653 auto format(
const sparrow::nullable<T, B>& n, std::format_context& ctx)
const
1657 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(n.
get()));
1661 return std::format_to(ctx.out(),
"{}",
"null");
1665 std::string m_format_string =
"{:";
1670 template <
typename T, mpl::
boolean_like B>
1671 std::ostream&
operator<<(std::ostream& os,
const nullable<T, B>& value)
1673 os << std::format(
"{}", value);
1678template <
class... T>
1679struct std::formatter<sparrow::nullable_variant<T...>>
1681 constexpr auto parse(format_parse_context& ctx)
1683 auto pos = ctx.begin();
1684 while (pos != ctx.end() && *pos !=
'}')
1686 m_format_string.push_back(*pos);
1689 m_format_string.push_back(
'}');
1693 auto format(
const sparrow::nullable_variant<T...>& variant, std::format_context& ctx)
const
1698 [&](
const auto& value)
1700 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(value));
1707 return std::vformat_to(ctx.out(), m_format_string, std::make_format_args(
"null"));
1711 std::string m_format_string =
"{:";
1716 template <
class... T>
1717 std::ostream&
operator<<(std::ostream& os,
const nullable_variant<T...>& value)
1719 os << std::format(
"{}", value);
1725struct std::formatter<sparrow::nullval_t>
1727 constexpr auto parse(format_parse_context& ctx)
1732 auto format(
const sparrow::nullval_t&, std::format_context& ctx)
const
1734 return std::format_to(ctx.out(),
"nullval");
1744 constexpr std::string_view nullval_str =
"nullval";
1750#undef SPARROW_CONSTEXPR
Exception thrown when accessing a null nullable value.
const char * what() const noexcept override
Gets the descriptive error message.
bad_nullable_access() noexcept=default
Variant of nullable types with has_value() convenience method.
constexpr nullable_variant(const nullable_variant< U... > &rhs)
Converting constructor from another nullable variant.
constexpr nullable_variant(nullable_variant &&) noexcept=default
constexpr nullable_variant & operator=(const nullable_variant< U... > &rhs)
Converting assignment operator from another nullable variant.
constexpr nullable_variant(const nullable_variant &)=default
constexpr bool has_value() const
Checks whether the active alternative contains a valid value.
std::variant< T... > base_type
constexpr nullable_variant(const nullable< U, B > &value)
constexpr nullable(const self_type &rhs)=default
Default copy constructor.
nullable_traits< B > flag_traits
constexpr nullable(std::add_lvalue_reference_t< T > value, flag_type &&null_flag)
Constructor from value reference and moved flag.
constexpr nullable(U &value, V &null_flag)
Constructor from const value and non-const flag reference (for non-reference types only).
typename value_traits::const_reference const_reference
constexpr nullable(value_type &&value, flag_type &&null_flag)
Constructor from value and flag.
constexpr nullable() noexcept
Default constructor creating a null nullable.
typename flag_traits::reference flag_reference
constexpr nullable(nullval_t) noexcept
Constructor from nullval_t creating a null nullable.
typename flag_traits::const_reference flag_const_reference
typename flag_traits::value_type flag_type
constexpr nullable(U &&value, V &&null_flag)
Constructor from two forwarding references (for value types with const qualifiers).
nullable< inner_const_reference, B > self_type
constexpr reference value() &
inner_const_reference stored_value_type
constexpr bool has_value() const noexcept
void swap(self_type &other) noexcept
constexpr reference get() &noexcept
typename value_traits::rvalue_reference rvalue_reference
typename value_traits::value_type value_type
constexpr nullable(value_type &&value, std::add_lvalue_reference_t< B > null_flag)
Constructor from moved value and flag reference.
constexpr self_type & operator=(const self_type &rhs) noexcept(std::is_nothrow_assignable_v< std::add_lvalue_reference_t< T >, const_reference > &&std::is_nothrow_assignable_v< std::add_lvalue_reference_t< B >, flag_const_reference >)
Default copy assignment operator.
constexpr value_type value_or(U &&default_value) const &
constexpr nullable(self_type &&rhs) noexcept=default
Default move constructor.
typename flag_traits::const_rvalue_reference flag_const_rvalue_reference
constexpr self_type & operator=(nullval_t) noexcept
Assignment from nullval_t, setting nullable to null state.
constexpr self_type & operator=(self_type &&rhs) noexcept(std::is_nothrow_assignable_v< std::add_lvalue_reference_t< T >, rvalue_reference > &&std::is_nothrow_assignable_v< std::add_lvalue_reference_t< B >, flag_rvalue_reference >)
Default move assignment operator.
constexpr flag_reference null_flag() &noexcept
typename flag_traits::rvalue_reference flag_rvalue_reference
typename value_traits::reference reference
constexpr nullable(std::add_lvalue_reference_t< T > value, std::add_lvalue_reference_t< B > null_flag)
Constructor from lvalue references (for reference semantics).
nullable_traits< inner_const_reference > value_traits
typename value_traits::const_rvalue_reference const_rvalue_reference
Concepts used to disambiguate the nullable class constructors.
Whether every alternative of Source has exactly one owning counterpart in Dest, so that a value of So...
std::ostream & operator<<(std::ostream &stream, primesum::uint128_t n)
The __int128_t type (GCC/Clang) is not well supported by the C++ standard library (in 2016) so we hav...
nullable< materialized_t< typename N::value_type >, materialized_t< typename N::flag_type > > materialized_nullable_t
Owning counterpart of a nullable: materialized value and flag.
std::conditional_t< std::is_reference_v< T >, const std::decay_t< U > &, std::decay_t< U > && > conditional_ref_t
constexpr Dest materialize_variant(const Source &source)
Converts the active alternative of a nullable variant to its owning counterpart.
constexpr bool unique_alternative
Whether X is the type of exactly one of Ts.
typename materialized< T >::type materialized_t
typename add_const_lvalue_reference< T >::type add_const_lvalue_reference_t
Convenience alias for add_const_lvalue_reference.
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
constexpr void zero_null_values(R &range, const T &default_value=T{})
Sets null values in a range to a default value.
SPARROW_API bool operator==(const array &lhs, const array &rhs)
Compares the content of two arrays.
constexpr nullval_t nullval(0)
constexpr nullable_get_fn nullable_get
constexpr bool is_nullable_variant_v
constexpr bool is_nullable_v
std::ostream & operator<<(std::ostream &os, const nullval_t &)
SPARROW_API void swap(ArrowArray &lhs, ArrowArray &rhs) noexcept
Swaps the contents of the two ArrowArray objects.
constexpr nullable< T, B > make_nullable(T &&value, B &&flag=true)
Creates a nullable object with deduced types.
Extensions to the C++ standard library.
#define SPARROW_CONSTEXPR
Mapping of the alternatives of Source onto their owning counterpart in Dest.
Owning counterpart of the type stored by a nullable.
std::remove_cvref_t< T > type
const_reference const_rvalue_reference
reference rvalue_reference
std::add_lvalue_reference_t< value_type > reference
std::add_lvalue_reference_t< std::add_const_t< value_type > > const_reference
const value_type && const_rvalue_reference
std::add_lvalue_reference_t< value_type > reference
value_type && rvalue_reference
std::add_lvalue_reference_t< std::add_const_t< value_type > > const_reference
Sentinel type to indicate a nullable value is null.
constexpr nullval_t(int)
Private constructor to prevent default construction.
sparrow:: nullable< std::common_reference_t< TQual< T >, UQual< U > >, std::common_reference_t< TQual< TB >, UQual< UB > > > type