diff --git a/sus/boxed/box.h b/sus/boxed/box.h index 3109f5cc7..bdd62ef3c 100644 --- a/sus/boxed/box.h +++ b/sus/boxed/box.h @@ -656,6 +656,9 @@ class [[_sus_trivial_abi]] Box final : public __private::BoxBase, T> { return {}; } + // Stream support. + _sus_format_to_stream(Box); + private: enum FromPointer { FROM_POINTER }; constexpr explicit Box(FromPointer, T* t) noexcept : t_(t) {} @@ -708,9 +711,6 @@ struct fmt::formatter<::sus::boxed::Box, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::boxed, Box, T); - // Promote `Box` into the `sus` namespace. namespace sus { using ::sus::boxed::Box; diff --git a/sus/choice/choice.h b/sus/choice/choice.h index cb45dbda6..941af0605 100644 --- a/sus/choice/choice.h +++ b/sus/choice/choice.h @@ -910,6 +910,9 @@ class Choice<__private::TypeList, Tags...> final { const Choice<__private::TypeList, RhsTag, RhsTags...>& r) = delete; + // Stream support + _sus_format_to_stream(Choice); + private: constexpr explicit Choice(IndexType i) noexcept : index_(i) {} @@ -1001,18 +1004,6 @@ struct fmt::formatter< } }; -// Stream support (written out manually due to use of template specialization). -namespace sus::choice_type { -template StreamType> -inline StreamType& operator<<( - StreamType& stream, - const Choice<__private::TypeList, Tags...>& value) { - return ::sus::string::__private::format_to_stream(stream, - fmt::to_string(value)); -} -} // namespace sus::choice_type - // Promote Choice into the `sus` namespace. namespace sus { using ::sus::choice_type::Choice; diff --git a/sus/collections/array.h b/sus/collections/array.h index 7b267c1bc..a0ab9b93c 100644 --- a/sus/collections/array.h +++ b/sus/collections/array.h @@ -63,6 +63,27 @@ struct Storage final {}; } // namespace __private +namespace __private { +template +constexpr bool array_cmp_impl(sus::cmp::Ordering auto& val, const auto& l, + const auto& r) noexcept { + auto cmp = l.get_unchecked(::sus::marker::unsafe_fn, I) <=> + r.get_unchecked(::sus::marker::unsafe_fn, I); + // Allow downgrading from equal to equivalent, but not the inverse. + if (cmp != 0) val = cmp; + // Short circuit by returning true when we find a difference. + return val == 0; +} + +template +constexpr auto array_cmp(sus::cmp::Ordering auto equal, const auto& l, + const auto& r, std::index_sequence) noexcept { + auto val = equal; + (true && ... && (array_cmp_impl(val, l, r))); + return val; +} +} // namespace __private + /// A collection of objects of type `T`, with a fixed size `N`. /// /// An Array can not be larger than [`isize::MAX`]($sus::num::isize::MAX), as @@ -335,10 +356,11 @@ class Array final { /// Satisfies the [`Eq, Array>`]($sus::cmp::Eq) concept. template requires(::sus::cmp::Eq) - constexpr bool operator==(const Array& r) const& noexcept + friend constexpr bool operator==(const Array& l, + const Array& r) noexcept requires(::sus::cmp::Eq) { - return eq_impl(r, std::make_index_sequence()); + return l.eq_impl(r, std::make_index_sequence()); } /// Satisfies the [`Eq, Slice>`]($sus::cmp::Eq) concept. @@ -405,6 +427,115 @@ class Array final { sus::iter::IterRefCounter::empty_for_view(), storage_.data_, N); } + /// Compares two Arrays. + /// + /// Satisfies sus::cmp::StrongOrd> if sus::cmp::StrongOrd. + /// + /// Satisfies sus::cmp::Ord> if sus::cmp::Ord. + /// + /// Satisfies sus::cmp::PartialOrd> if sus::cmp::PartialOrd. + /// #[doc.overloads=array.cmp.array] + template + requires(::sus::cmp::ExclusiveStrongOrd) + constexpr friend std::strong_ordering operator<=>( + const Array& l, const Array& r) noexcept { + return __private::array_cmp(std::strong_ordering::equivalent, l, r, + std::make_index_sequence()); + } + /// #[doc.overloads=array.cmp.array] + template + requires(::sus::cmp::ExclusiveOrd) + constexpr friend std::weak_ordering operator<=>( + const Array& l, const Array& r) noexcept { + return __private::array_cmp(std::weak_ordering::equivalent, l, r, + std::make_index_sequence()); + } + /// #[doc.overloads=array.cmp.array] + template + requires(::sus::cmp::ExclusivePartialOrd) + constexpr friend std::partial_ordering operator<=>( + const Array& l, const Array& r) noexcept { + return __private::array_cmp(std::partial_ordering::equivalent, l, r, + std::make_index_sequence()); + } + + /// Compares an Array and a Slice. + /// + /// Satisfies sus::cmp::StrongOrd, Slice> if + /// sus::cmp::StrongOrd. + /// + /// Satisfies sus::cmp::Ord, Slice> if sus::cmp::Ord. + /// + /// Satisfies sus::cmp::PartialOrd, Slice> if + /// sus::cmp::PartialOrd. + /// #[doc.overloads=array.cmp.slice] + template + requires(::sus::cmp::ExclusiveStrongOrd) + constexpr friend std::strong_ordering operator<=>( + const Array& l, const Slice& r) noexcept { + if (r.len() != N) return r.len() <=> N; + return __private::array_cmp(std::strong_ordering::equivalent, l, r, + std::make_index_sequence()); + } + /// #[doc.overloads=array.cmp.slice] + template + requires(::sus::cmp::ExclusiveOrd) + constexpr friend std::weak_ordering operator<=>(const Array& l, + const Slice& r) noexcept { + if (r.len() != N) return r.len() <=> N; + return __private::array_cmp(std::weak_ordering::equivalent, l, r, + std::make_index_sequence()); + } + /// #[doc.overloads=array.cmp.slice] + template + requires(::sus::cmp::ExclusivePartialOrd) + constexpr friend std::partial_ordering operator<=>( + const Array& l, const Slice& r) noexcept { + if (r.len() != N) return r.len() <=> N; + return __private::array_cmp(std::partial_ordering::equivalent, l, r, + std::make_index_sequence()); + } + + /// Compares an Array and a SliceMut. + /// + /// Satisfies sus::cmp::StrongOrd, SliceMut> if + /// sus::cmp::StrongOrd. + /// + /// Satisfies sus::cmp::Ord, SliceMut> if sus::cmp::Ord. + /// + /// Satisfies sus::cmp::PartialOrd, SliceMut> if + /// sus::cmp::PartialOrd. + /// #[doc.overloads=array.cmp.slicemut] + template + requires(::sus::cmp::ExclusiveStrongOrd) + constexpr friend std::strong_ordering operator<=>( + const Array& l, const SliceMut& r) noexcept { + if (r.len() != N) return r.len() <=> N; + return __private::array_cmp(std::strong_ordering::equivalent, l, r, + std::make_index_sequence()); + } + /// #[doc.overloads=array.cmp.slicemut] + template + requires(::sus::cmp::ExclusiveOrd) + constexpr friend std::weak_ordering operator<=>( + const Array& l, const SliceMut& r) noexcept { + if (r.len() != N) return r.len() <=> N; + return __private::array_cmp(std::weak_ordering::equivalent, l, r, + std::make_index_sequence()); + } + /// #[doc.overloads=array.cmp.slicemut] + template + requires(::sus::cmp::ExclusivePartialOrd) + constexpr friend std::partial_ordering operator<=>( + const Array& l, const SliceMut& r) noexcept { + if (r.len() != N) return r.len() <=> N; + return __private::array_cmp(std::partial_ordering::equivalent, l, r, + std::make_index_sequence()); + } + + // Stream support + _sus_format_to_stream(Array); + private: enum WithDefault { WITH_DEFAULT }; template @@ -467,136 +598,6 @@ template std::same_as, std::remove_cvref_t>)) Array(T&&, Ts&&...) -> Array, 1u + sizeof...(Ts)>; -namespace __private { - -template -constexpr inline bool array_cmp_impl(sus::cmp::Ordering auto& val, - const auto& l, const auto& r) noexcept { - auto cmp = l.get_unchecked(::sus::marker::unsafe_fn, I) <=> - r.get_unchecked(::sus::marker::unsafe_fn, I); - // Allow downgrading from equal to equivalent, but not the inverse. - if (cmp != 0) val = cmp; - // Short circuit by returning true when we find a difference. - return val == 0; -}; - -template -constexpr inline auto array_cmp(sus::cmp::Ordering auto equal, const auto& l, - const auto& r, - std::index_sequence) noexcept { - auto val = equal; - (true && ... && (array_cmp_impl(val, l, r))); - return val; -}; - -} // namespace __private - -/// Compares two Arrays. -/// -/// Satisfies sus::cmp::StrongOrd> if sus::cmp::StrongOrd. -/// -/// Satisfies sus::cmp::Ord> if sus::cmp::Ord. -/// -/// Satisfies sus::cmp::PartialOrd> if sus::cmp::PartialOrd. -/// #[doc.overloads=array.cmp.array] -template - requires(::sus::cmp::ExclusiveStrongOrd) -constexpr inline std::strong_ordering operator<=>( - const Array& l, const Array& r) noexcept { - return __private::array_cmp(std::strong_ordering::equivalent, l, r, - std::make_index_sequence()); -} -/// #[doc.overloads=array.cmp.array] -template - requires(::sus::cmp::ExclusiveOrd) -constexpr inline std::weak_ordering operator<=>(const Array& l, - const Array& r) noexcept { - return __private::array_cmp(std::weak_ordering::equivalent, l, r, - std::make_index_sequence()); -} -/// #[doc.overloads=array.cmp.array] -template - requires(::sus::cmp::ExclusivePartialOrd) -constexpr inline std::partial_ordering operator<=>( - const Array& l, const Array& r) noexcept { - return __private::array_cmp(std::partial_ordering::equivalent, l, r, - std::make_index_sequence()); -} - -/// Compares an Array and a Slice. -/// -/// Satisfies sus::cmp::StrongOrd, Slice> if -/// sus::cmp::StrongOrd. -/// -/// Satisfies sus::cmp::Ord, Slice> if sus::cmp::Ord. -/// -/// Satisfies sus::cmp::PartialOrd, Slice> if -/// sus::cmp::PartialOrd. -/// #[doc.overloads=array.cmp.slice] -template - requires(::sus::cmp::ExclusiveStrongOrd) -constexpr inline std::strong_ordering operator<=>(const Array& l, - const Slice& r) noexcept { - if (r.len() != N) return r.len() <=> N; - return __private::array_cmp(std::strong_ordering::equivalent, l, r, - std::make_index_sequence()); -} -/// #[doc.overloads=array.cmp.slice] -template - requires(::sus::cmp::ExclusiveOrd) -constexpr inline std::weak_ordering operator<=>(const Array& l, - const Slice& r) noexcept { - if (r.len() != N) return r.len() <=> N; - return __private::array_cmp(std::weak_ordering::equivalent, l, r, - std::make_index_sequence()); -} -/// #[doc.overloads=array.cmp.slice] -template - requires(::sus::cmp::ExclusivePartialOrd) -constexpr inline std::partial_ordering operator<=>(const Array& l, - const Slice& r) noexcept { - if (r.len() != N) return r.len() <=> N; - return __private::array_cmp(std::partial_ordering::equivalent, l, r, - std::make_index_sequence()); -} - -/// Compares an Array and a SliceMut. -/// -/// Satisfies sus::cmp::StrongOrd, SliceMut> if -/// sus::cmp::StrongOrd. -/// -/// Satisfies sus::cmp::Ord, SliceMut> if sus::cmp::Ord. -/// -/// Satisfies sus::cmp::PartialOrd, SliceMut> if -/// sus::cmp::PartialOrd. -/// #[doc.overloads=array.cmp.slicemut] -template - requires(::sus::cmp::ExclusiveStrongOrd) -constexpr inline std::strong_ordering operator<=>( - const Array& l, const SliceMut& r) noexcept { - if (r.len() != N) return r.len() <=> N; - return __private::array_cmp(std::strong_ordering::equivalent, l, r, - std::make_index_sequence()); -} -/// #[doc.overloads=array.cmp.slicemut] -template - requires(::sus::cmp::ExclusiveOrd) -constexpr inline std::weak_ordering operator<=>(const Array& l, - const SliceMut& r) noexcept { - if (r.len() != N) return r.len() <=> N; - return __private::array_cmp(std::weak_ordering::equivalent, l, r, - std::make_index_sequence()); -} -/// #[doc.overloads=array.cmp.slicemut] -template - requires(::sus::cmp::ExclusivePartialOrd) -constexpr inline std::partial_ordering operator<=>( - const Array& l, const SliceMut& r) noexcept { - if (r.len() != N) return r.len() <=> N; - return __private::array_cmp(std::partial_ordering::equivalent, l, r, - std::make_index_sequence()); -} - /// Support for using structured bindings with `Array`. /// #[doc.overloads=array.structured.bindings] template @@ -664,17 +665,6 @@ struct fmt::formatter<::sus::collections::Array, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support (written out manually due to size_t template param). -namespace sus::collections { -template StreamType> -inline StreamType& operator<<(StreamType& stream, const Array& value) { - return ::sus::string::__private::format_to_stream(stream, - fmt::to_string(value)); -} - -} // namespace sus::collections - namespace sus::collections { // Documented in vec.h using ::sus::iter::begin; diff --git a/sus/collections/slice.h b/sus/collections/slice.h index 29663192d..3da6943f3 100644 --- a/sus/collections/slice.h +++ b/sus/collections/slice.h @@ -248,6 +248,9 @@ class [[_sus_trivial_abi]] Slice final { iter_refs_ = ::sus::iter::IterRefCounter::empty_for_view(); } + // Stream support. + _sus_format_to_stream(Slice); + #define _ptr_expr data_ #define _len_expr len_ #define _iter_refs_expr iter_refs_.to_iter_from_view() @@ -471,6 +474,9 @@ class [[_sus_trivial_abi]] SliceMut final { slice_.iter_refs_ = ::sus::iter::IterRefCounter::empty_for_view(); } + // Stream support. + _sus_format_to_stream(SliceMut); + #define _ptr_expr slice_.data_ #define _len_expr slice_.len_ #define _iter_refs_expr slice_.iter_refs_.to_iter_from_view() @@ -540,9 +546,6 @@ struct fmt::formatter<::sus::collections::Slice, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::collections, Slice, T); - // fmt support. template struct fmt::formatter<::sus::collections::SliceMut, Char> { @@ -568,9 +571,6 @@ struct fmt::formatter<::sus::collections::SliceMut, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::collections, SliceMut, T); - // Promote Slice into the `sus` namespace. namespace sus { using ::sus::collections::Slice; diff --git a/sus/collections/vec.h b/sus/collections/vec.h index d28e0e3fa..90dd202c0 100644 --- a/sus/collections/vec.h +++ b/sus/collections/vec.h @@ -829,6 +829,9 @@ class Vec final { ::sus::marker::unsafe_fn, iter_refs_.to_view_from_owner(), data_, len_); } + // Stream support. + _sus_format_to_stream(Vec); + #define _ptr_expr data_ #define _len_expr len_ #define _iter_refs_expr iter_refs_.to_iter_from_owner() @@ -1094,9 +1097,6 @@ struct fmt::formatter<::sus::collections::Vec, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::collections, Vec, T); - namespace sus::collections { /// Implicit for-ranged loop iteration for all collections via the `iter` /// method. diff --git a/sus/env/var.h b/sus/env/var.h index fc6a9c87b..193f0df38 100644 --- a/sus/env/var.h +++ b/sus/env/var.h @@ -36,7 +36,8 @@ struct VarError { Reason reason; /// Satisfies the [`Eq`]($sus::cmp::Eq) concept. - constexpr bool operator==(const VarError& rhs) const noexcept = default; + constexpr friend bool operator==(const VarError& lhs, + const VarError& rhs) noexcept = default; }; /// Fetches the environment variable `key` from the current process. diff --git a/sus/iter/generator.h b/sus/iter/generator.h index abc98159b..f267bd718 100644 --- a/sus/iter/generator.h +++ b/sus/iter/generator.h @@ -122,9 +122,10 @@ class [[nodiscard]] [[_sus_trivial_abi]] GeneratorLoop { constexpr GeneratorLoop(Generator& generator sus_lifetimebound) noexcept : generator_(generator) {} - constexpr bool operator==( + constexpr friend bool operator==( + const GeneratorLoop& l, const ::sus::iter::__private::IteratorEnd&) noexcept { - return generator_.co_handle_.done(); + return l.generator_.co_handle_.done(); } constexpr GeneratorLoop& operator++() & noexcept { // UB occurs if this is called after GeneratorLoop == IteratorEnd. This diff --git a/sus/iter/iterator_loop.h b/sus/iter/iterator_loop.h index 2fac60fea..9ff8c3cea 100644 --- a/sus/iter/iterator_loop.h +++ b/sus/iter/iterator_loop.h @@ -31,8 +31,9 @@ class [[nodiscard]] IteratorLoop final { constexpr IteratorLoop(Iter&& iter) noexcept : iter_(::sus::forward(iter)), item_(iter_.next()) {} - constexpr inline bool operator==(__private::IteratorEnd) const noexcept { - return item_.is_none(); + constexpr friend bool operator==(const IteratorLoop& x, + __private::IteratorEnd) noexcept { + return x.item_.is_none(); } constexpr inline void operator++() & noexcept { item_ = iter_.next(); } constexpr inline Item operator*() & noexcept { diff --git a/sus/iter/size_hint.h b/sus/iter/size_hint.h index 859cba1e4..4ce4f1968 100644 --- a/sus/iter/size_hint.h +++ b/sus/iter/size_hint.h @@ -18,6 +18,7 @@ #include "sus/num/unsigned_integer.h" #include "sus/option/option.h" +#include "sus/string/__private/format_to_stream.h" namespace sus::iter { @@ -27,6 +28,22 @@ struct SizeHint { friend constexpr bool operator==(const SizeHint& lhs, const SizeHint& rhs) noexcept = default; + + // Stream support. + _sus_format_to_stream(SizeHint); }; } // namespace sus::iter + +// fmt support. +template +struct fmt::formatter<::sus::iter::SizeHint, Char> { + template + constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + FormatContext::iterator format(const ::sus::iter::SizeHint& t, + FormatContext& ctx) const; +}; diff --git a/sus/iter/size_hint_impl.h b/sus/iter/size_hint_impl.h index d4abd230e..9d6be1ae9 100644 --- a/sus/iter/size_hint_impl.h +++ b/sus/iter/size_hint_impl.h @@ -24,18 +24,8 @@ // fmt support. template -struct fmt::formatter<::sus::iter::SizeHint, Char> { - template - constexpr auto parse(ParseContext& ctx) { - return ctx.begin(); - } - - template - constexpr auto format(const ::sus::iter::SizeHint& t, - FormatContext& ctx) const { - return fmt::format_to(ctx.out(), "SizeHint({}, {})", t.lower, t.upper); - } -}; - -// Stream support. -_sus_format_to_stream(sus::iter, SizeHint); +template +FormatContext::iterator fmt::formatter<::sus::iter::SizeHint, Char>::format( + const ::sus::iter::SizeHint& t, FormatContext& ctx) const { + return fmt::format_to(ctx.out(), "SizeHint({}, {})", t.lower, t.upper); +} diff --git a/sus/marker/empty.h b/sus/marker/empty.h index 3f307231b..89445710c 100644 --- a/sus/marker/empty.h +++ b/sus/marker/empty.h @@ -19,6 +19,25 @@ #include "fmt/core.h" #include "sus/string/__private/format_to_stream.h" +namespace sus::marker { +struct EmptyMarker; +} + +// fmt support. +template +struct fmt::formatter<::sus::marker::EmptyMarker, Char> { + template + constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + constexpr auto format(const ::sus::marker::EmptyMarker&, + FormatContext& ctx) const { + return fmt::format_to(ctx.out(), "empty"); + } +}; + namespace sus::marker { /// A marker that designates emptinesss, for constructing an empty collection. @@ -31,6 +50,9 @@ struct EmptyMarker { /// $sus::marker::empty). /// #[doc.hidden] explicit consteval EmptyMarker() {} + + // Stream support. + _sus_format_to_stream(EmptyMarker); }; /// The global [`EmptyMarker`]($sus::marker::EmptyMarker) which can be passed to @@ -40,24 +62,6 @@ constexpr inline auto empty = EmptyMarker(); } // namespace sus::marker -// fmt support. -template -struct fmt::formatter<::sus::marker::EmptyMarker, Char> { - template - constexpr auto parse(ParseContext& ctx) { - return ctx.begin(); - } - - template - constexpr auto format(const ::sus::marker::EmptyMarker&, - FormatContext& ctx) const { - return fmt::format_to(ctx.out(), "empty"); - } -}; - -// Stream support. -_sus_format_to_stream(sus::marker, EmptyMarker); - // Promote `empty` into the `sus` namespace. namespace sus { using sus::marker::empty; diff --git a/sus/marker/unsafe.h b/sus/marker/unsafe.h index c71ff59f2..9fe452240 100644 --- a/sus/marker/unsafe.h +++ b/sus/marker/unsafe.h @@ -22,9 +22,26 @@ namespace sus { /// Marker types, such as for accessing unsafe APIs, for overload resolution, /// or type elision. -namespace marker {} +namespace marker { +struct UnsafeFnMarker; +} } // namespace sus +// fmt support. +template +struct fmt::formatter<::sus::marker::UnsafeFnMarker, Char> { + template + constexpr auto parse(ParseContext& ctx) { + return ctx.begin(); + } + + template + constexpr auto format(const ::sus::marker::UnsafeFnMarker&, + FormatContext& ctx) const { + return fmt::format_to(ctx.out(), "unsafe_fn"); + } +}; + namespace sus::marker { /// A marker that designates a function as unsafe, or containing Undefined @@ -51,6 +68,9 @@ struct UnsafeFnMarker { /// $sus::marker::unsafe_fn). /// #[doc.hidden] explicit consteval UnsafeFnMarker() {} + + // Stream support. + _sus_format_to_stream(UnsafeFnMarker); }; /// The global [`UnsafeFnMarker`]($sus::marker::UnsafeFnMarker) which can be @@ -59,21 +79,3 @@ struct UnsafeFnMarker { constexpr inline auto unsafe_fn = UnsafeFnMarker(); } // namespace sus::marker - -// fmt support. -template -struct fmt::formatter<::sus::marker::UnsafeFnMarker, Char> { - template - constexpr auto parse(ParseContext& ctx) { - return ctx.begin(); - } - - template - constexpr auto format(const ::sus::marker::UnsafeFnMarker&, - FormatContext& ctx) const { - return fmt::format_to(ctx.out(), "unsafe_fn"); - } -}; - -// Stream support. -_sus_format_to_stream(sus::marker, UnsafeFnMarker); diff --git a/sus/num/__private/float_methods.inc b/sus/num/__private/float_methods.inc index 06e611dfa..33c007d42 100644 --- a/sus/num/__private/float_methods.inc +++ b/sus/num/__private/float_methods.inc @@ -1157,6 +1157,9 @@ _sus_pure static _self from_ne_bytes( const ::sus::collections::Array()>& bytes) noexcept; +// Stream support. +_sus_format_to_stream(_self); + #undef _self #undef _primitive #undef _unsigned diff --git a/sus/num/__private/float_methods_impl.inc b/sus/num/__private/float_methods_impl.inc index 039d81e23..7553d73a6 100644 --- a/sus/num/__private/float_methods_impl.inc +++ b/sus/num/__private/float_methods_impl.inc @@ -95,9 +95,6 @@ struct fmt::formatter<::sus::num::_self, Char> { formatter<_primitive, Char> underlying_; }; -// Stream support. -_sus_format_to_stream(sus::num, _self); - #undef _self #undef _primitive #undef _unsigned diff --git a/sus/num/__private/signed_integer_methods.inc b/sus/num/__private/signed_integer_methods.inc index 8c4a6f506..dc49cf938 100644 --- a/sus/num/__private/signed_integer_methods.inc +++ b/sus/num/__private/signed_integer_methods.inc @@ -2083,6 +2083,145 @@ _sus_pure static constexpr _self from_ne_bytes( const ::sus::collections::Array()>& bytes) noexcept; +/// Satisfies the [`Shl`]($sus::num::Shl) concept for signed integers. +/// +/// This operation supports shifting with primitive signed or unsigned integers +/// that convert to the safe numeric, as well as enums. +/// However enum class is excluded as they require an explicit conversion to an +/// integer. +/// +/// Thus the bound is `std::convertible_to` (implicit conversion) instead of +/// `sus::construct::From` (explicit conversion). +/// +/// # Panics +/// This function will panic when `r` is not less than the number of bits in `l` +/// if overflow checks are enabled (they are by default) and will perform a +/// wrapping shift if overflow checks are disabled (not the default). +/// +/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this +/// behaviour. +/// +/// #[doc.overloads=signedint.<<] +[[nodiscard]] __sus_pure_const constexpr friend _self operator<<( + _self l, std::convertible_to auto r) noexcept { + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = __private::shl_with_overflow(l.primitive_value, + u64(r).primitive_value); + sus_check_with_message(!out.overflow, + "attempt to shift left with overflow"); + return out.value; + } else { + return l.wrapping_shl(u64(r).primitive_value); + } +} + +/// #[doc.overloads=signedint.<<] +template + requires(!std::convertible_to) +constexpr friend _self operator<<(_self l, U r) noexcept = delete; + +/// Satisfies the [`Shr`]($sus::num::Shr) concept for signed integers. +/// +/// Performs sign extension, copying the sign bit to the right if its set. +/// +/// # Panics +/// This function will panic when `r` is not less than the number of bits in `l` +/// if overflow checks are enabled (they are by default) and will perform a +/// wrapping shift if overflow checks are disabled (not the default). +/// +/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this +/// behaviour. +/// +/// #[doc.overloads=signedint.>>] +[[nodiscard]] __sus_pure_const constexpr friend _self operator>>( + _self l, std::convertible_to auto r) noexcept { + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + const auto out = __private::shr_with_overflow(l.primitive_value, + u64(r).primitive_value); + sus_check_with_message(!out.overflow, + "attempt to shift right with overflow"); + return out.value; + } else { + return l.wrapping_shr(u64(r).primitive_value); + } +} + +/// #[doc.overloads=signedint.>>] +template + requires(!std::convertible_to) +constexpr friend _self operator>>(_self l, U r) noexcept = delete; + +/// Satisfies the [`Shl`]($sus::num::Shl) concept for signed primitive integers +/// shifted by [`u64`]($sus::num::u64). +/// #[doc.overloads=signed.prim.< + requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && + std::same_as && std::convertible_to) +[[nodiscard]] __sus_pure_const constexpr friend P operator<<(P l, U r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l << u64(r).primitive_value; +} +/// #[doc.overloads=signed.prim.< + requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && + std::same_as && !std::convertible_to) +constexpr friend P operator<<(P l, U r) noexcept = delete; + +/// Satisfies the [`Shr`]($sus::num::Shr) concept for signed primitive integers +/// shifted by [`u64`]($sus::num::u64). +/// +/// Performs sign extension, copying the sign bit to the right if its set. +/// #[doc.overloads=signed.prim.>>u64] +template + requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && + std::same_as && std::convertible_to) +[[nodiscard]] __sus_pure_const constexpr friend P operator>>(P l, U r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l >> u64(r).primitive_value; +} +/// #[doc.overloads=signed.prim.>>u64] +template + requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && + std::same_as && !std::convertible_to) +constexpr friend P operator>>(P l, U r) noexcept = delete; + +/// #[doc.overloads=unsigned.prim.< + requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && + std::same_as && std::convertible_to) +[[nodiscard]] __sus_pure_const constexpr friend P operator<<(P l, U r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l << u64(r).primitive_value; +} + +/// #[doc.overloads=unsigned.prim.< + requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && + std::same_as && !std::convertible_to) +constexpr friend P operator<<(P l, U r) noexcept = delete; + +/// #[doc.overloads=unsigned.prim.>>u64] +template + requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && + std::same_as && std::convertible_to) +[[nodiscard]] __sus_pure_const constexpr friend P operator>>(P l, U r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l >> u64(r).primitive_value; +} + +/// #[doc.overloads=unsigned.prim.>>u64] +template + requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && + std::same_as && !std::convertible_to) +constexpr friend P operator>>(P l, U r) noexcept = delete; + +// Stream support. +_sus_format_to_stream(_self); + #undef _self #undef _primitive #undef _unsigned diff --git a/sus/num/__private/signed_integer_methods_impl.inc b/sus/num/__private/signed_integer_methods_impl.inc index 2486aea43..5e341af61 100644 --- a/sus/num/__private/signed_integer_methods_impl.inc +++ b/sus/num/__private/signed_integer_methods_impl.inc @@ -265,9 +265,6 @@ struct fmt::formatter<::sus::num::_self, Char> { formatter<_primitive, Char> underlying_; }; -// Stream support. -_sus_format_to_stream(sus::num, _self); - #undef _self #undef _primitive #undef _unsigned diff --git a/sus/num/__private/unsigned_integer_methods.inc b/sus/num/__private/unsigned_integer_methods.inc index 0ee1408d6..6643a74f0 100644 --- a/sus/num/__private/unsigned_integer_methods.inc +++ b/sus/num/__private/unsigned_integer_methods.inc @@ -2114,39 +2114,27 @@ _sus_pure constexpr _self wrapping_sub(U rhs) const& noexcept { /// Returns the number of ones in the binary representation of the current /// value. -_sus_pure constexpr u32 count_ones() const& noexcept { - return __private::count_ones(primitive_value); -} +_sus_pure constexpr u32 count_ones() const& noexcept; /// Returns the number of zeros in the binary representation of the current /// value. -_sus_pure constexpr u32 count_zeros() const& noexcept { - return (~(*this)).count_ones(); -} +_sus_pure constexpr u32 count_zeros() const& noexcept; /// Returns the number of leading ones in the binary representation of the /// current value. -_sus_pure constexpr u32 leading_ones() const& noexcept { - return (~(*this)).leading_zeros(); -} +_sus_pure constexpr u32 leading_ones() const& noexcept; /// Returns the number of leading zeros in the binary representation of the /// current value. -_sus_pure constexpr u32 leading_zeros() const& noexcept { - return __private::leading_zeros(primitive_value); -} +_sus_pure constexpr u32 leading_zeros() const& noexcept; /// Returns the number of trailing ones in the binary representation of the /// current value. -_sus_pure constexpr u32 trailing_ones() const& noexcept { - return (~(*this)).trailing_zeros(); -} +_sus_pure constexpr u32 trailing_ones() const& noexcept; /// Returns the number of trailing zeros in the binary representation of the /// current value. -_sus_pure constexpr u32 trailing_zeros() const& noexcept { - return __private::trailing_zeros(primitive_value); -} +_sus_pure constexpr u32 trailing_zeros() const& noexcept; /// Reverses the order of bits in the integer. The least significant bit becomes /// the most significant bit, second least-significant bit becomes second @@ -2181,15 +2169,7 @@ _sus_pure constexpr _self swap_bytes() const& noexcept { /// /// See [overflow checks]($sus::num#overflow-behaviour) for controlling this /// behaviour. -_sus_pure constexpr _self pow(u32 rhs) const& noexcept { - const auto out = - __private::pow_with_overflow(primitive_value, rhs.primitive_value); - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(!out.overflow, - "attempt to multiply with overflow"); - } - return _self(out.value); -} +_sus_pure constexpr _self pow(u32 rhs) const& noexcept; /// Checked exponentiation. Computes [`pow`]($sus::num::@doc.self::pow), /// returning `None` if overflow occurred. @@ -2205,9 +2185,7 @@ _sus_pure constexpr ::sus::tuple_type::Tuple<_self, bool> overflowing_pow( /// Wrapping (modular) exponentiation. Computes [`pow`]( /// $sus::num::@doc.self::pow), wrapping around at the boundary of the type. -_sus_pure constexpr _self wrapping_pow(u32 exp) const& noexcept { - return _self(__private::wrapping_pow(primitive_value, exp.primitive_value)); -} +_sus_pure constexpr _self wrapping_pow(u32 exp) const& noexcept; /// Returns the base 2 logarithm of the number, rounded down. /// @@ -2273,9 +2251,7 @@ _sus_pure constexpr u32 log(U rhs) const& noexcept; #endif /// Returns `true` if and only if `self == 2^k` for some `k`. -_sus_pure constexpr bool is_power_of_two() const& noexcept { - return count_ones() == _self(_primitive{1u}); -} +_sus_pure constexpr bool is_power_of_two() const& noexcept; /// Calculates the smallest value greater than or equal to itself that is a /// multiple of `rhs`. @@ -2445,6 +2421,121 @@ to_ne_bytes() const& noexcept; const ::sus::collections::Array()>& bytes) noexcept; +// Stream support. +_sus_format_to_stream(_self); + +/// Satisfies the [`Shl`]($sus::num::Shl) concept for unsigned integers. +/// +/// # Panics +/// This function will panic when `r` is not less than the number of bits in `l` +/// if overflow checks are enabled (they are by default) and will perform a +/// wrapping shift if overflow checks are disabled (not the default). +/// +/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this +/// behaviour. +/// +/// #[doc.overloads=unsignedint.<<] +[[nodiscard]] __sus_pure_const constexpr friend _self operator<<( + _self l, std::convertible_to auto r) noexcept { + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + sus_check_with_message(r < _self::BITS, + "attempt to shift left with overflow"); + return _self( + __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); + } else { + return _self(__private::shl_with_overflow(l.primitive_value, + u64(r).primitive_value) + .value); + } +} + +/// #[doc.overloads=unsignedint.<<] +template + requires(!std::convertible_to) +constexpr friend _self operator<<(_self l, U r) noexcept = delete; + +/// Satisfies the [`Shr`]($sus::num::Shr) concept for unsigned integers. +/// +/// # Panics +/// This function will panic when `r` is not less than the number of bits in `l` +/// if overflow checks are enabled (they are by default) and will perform a +/// wrapping shift if overflow checks are disabled (not the default). +/// +/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this +/// behaviour. +/// +/// #[doc.overloads=unsignedint.>>] +[[nodiscard]] __sus_pure_const constexpr friend _self operator>>( + _self l, std::convertible_to auto r) noexcept { + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + sus_check_with_message(r < _self::BITS, + "attempt to shift right with overflow"); + return _self( + __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); + } else { + return _self(__private::shr_with_overflow(l.primitive_value, + u64(r).primitive_value) + .value); + } +} + +/// #[doc.overloads=unsignedint.>>] +template + requires(!std::convertible_to) +constexpr friend _self operator>>(_self l, U r) noexcept = delete; + +/// Satisfies the [`Add`]($sus::num::Add) concept for pointers +/// (`T*`) with [`usize`]($sus::num::usize). +/// +/// Adds a [`usize`]($sus::num::usize) to a pointer, returning the resulting +/// pointer. +/// +/// #[doc.overloads=ptr.add.usize] +template +__sus_pure_const constexpr friend T* operator+(T* t, _self offset) { + return t + size_t{offset}; +} + +/// Satisfies the [`Shl`]($sus::num::Shl) concept for unsigned primitive +/// integers shifted by [`u64`]($sus::num::u64). +/// #[doc.overloads=unsigned.prim.< + requires(UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) +[[nodiscard]] __sus_pure_const constexpr friend P operator<<(P l, _self r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l << u64(r).primitive_value; +} + +/// Satisfies the [`Shr`]($sus::num::Shr) concept for unsigned primitive +/// integers shifted by [`u64`]($sus::num::u64). +/// #[doc.overloads=unsigned.prim.>>u64] +template + requires(UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) +[[nodiscard]] __sus_pure_const constexpr friend P operator>>(P l, _self r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l >> u64(r).primitive_value; +} + +/// #[doc.overloads=signed.prim.< + requires(SignedPrimitiveInteger

|| SignedPrimitiveEnum

) +[[nodiscard]] __sus_pure_const constexpr friend P operator<<(P l, _self r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l << u64(r).primitive_value; +} + +/// #[doc.overloads=signed.prim.>>u64] +template + requires(SignedPrimitiveInteger

|| SignedPrimitiveEnum

) +[[nodiscard]] __sus_pure_const constexpr friend P operator>>(P l, _self r) noexcept { + // No UB checks on primitive types, since there's no promotion to a Subspace + // return type? + return l >> u64(r).primitive_value; +} + #if _pointer /// Returns the address portion of the pointer. diff --git a/sus/num/__private/unsigned_integer_methods_impl.inc b/sus/num/__private/unsigned_integer_methods_impl.inc index 504f916fd..d32c0492e 100644 --- a/sus/num/__private/unsigned_integer_methods_impl.inc +++ b/sus/num/__private/unsigned_integer_methods_impl.inc @@ -748,6 +748,47 @@ _sus_pure constexpr _self _self::from_ne_bytes( return _self(val); } +_sus_pure constexpr u32 _self::count_ones() const& noexcept { + return __private::count_ones(primitive_value); +} + +_sus_pure constexpr u32 _self::count_zeros() const& noexcept { + return (~(*this)).count_ones(); +} + +_sus_pure constexpr u32 _self::leading_ones() const& noexcept { + return (~(*this)).leading_zeros(); +} + +_sus_pure constexpr u32 _self::leading_zeros() const& noexcept { + return __private::leading_zeros(primitive_value); +} + +_sus_pure constexpr u32 _self::trailing_ones() const& noexcept { + return (~(*this)).trailing_zeros(); +} + +_sus_pure constexpr u32 _self::trailing_zeros() const& noexcept { + return __private::trailing_zeros(primitive_value); +} + +_sus_pure constexpr _self _self::pow(u32 rhs) const& noexcept { + const auto out = + __private::pow_with_overflow(primitive_value, rhs.primitive_value); + if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { + sus_check_with_message(!out.overflow, "attempt to multiply with overflow"); + } + return _self(out.value); +} + +_sus_pure constexpr _self _self::wrapping_pow(u32 exp) const& noexcept { + return _self(__private::wrapping_pow(primitive_value, exp.primitive_value)); +} + +_sus_pure constexpr bool _self::is_power_of_two() const& noexcept { + return count_ones() == _self(_primitive{1u}); +} + } // namespace sus::num // std hash support. @@ -782,9 +823,6 @@ struct fmt::formatter<::sus::num::_self, Char> { formatter<_primitive, Char> underlying_; }; -// Stream support. -_sus_format_to_stream(sus::num, _self); - #undef _self #undef _primitive #undef _signed diff --git a/sus/num/signed_integer.h b/sus/num/signed_integer.h index a1cc1343a..1be3d2857 100644 --- a/sus/num/signed_integer.h +++ b/sus/num/signed_integer.h @@ -38,6 +38,7 @@ #include "sus/num/integer_concepts.h" #include "sus/num/try_from_int_error.h" #include "sus/num/unsigned_integer.h" +#include "sus/num/unsigned_integer_consts.h" #include "sus/option/option.h" #include "sus/ptr/copy.h" #include "sus/result/result.h" @@ -121,299 +122,60 @@ struct [[_sus_trivial_abi]] isize final { #define _primitive ::sus::num::__private::addr_type<>::signed_type #define _unsigned usize #include "sus/num/__private/signed_integer_methods.inc" -}; -#define _self isize -#define _primitive ::sus::num::__private::addr_type<>::signed_type -#include "sus/num/__private/signed_integer_consts.inc" -/// Satisfies the [`Add`]($sus::num::Add) concept for pointers -/// (`T*`) with [`isize`]($sus::num::isize). -/// -/// Adds a [`isize`]($sus::num::isize) to a pointer, returning the resulting -/// pointer. -/// -/// #[doc.overloads=ptr.add.isize] -template - requires(std::constructible_from) -__sus_pure_const constexpr inline T* operator+(T* t, S offset) { - return t + ptrdiff_t{offset}; -} - -/// Satisfies the [`AddAssign`]($sus::num::AddAssign) concept for pointers -/// (`T*`) with [`isize`]($sus::num::isize). -/// -/// Adds a [`isize`]($sus::num::isize) to a referenced pointer, and returns -/// the input reference. -/// -/// #[doc.overloads=ptr.add.isize] -template -constexpr inline T*& operator+=(T*& t, isize offset) { - t += ptrdiff_t{offset}; - return t; -} - -/// Satisfies the [`Sub`]($sus::num::Sub) concept for pointers -/// (`T*`) with [`isize`]($sus::num::isize). -/// -/// Subtracts a [`isize`]($sus::num::isize) from a pointer, returning the -/// resulting pointer. -/// -/// #[doc.overloads=ptr.sub.isize] -template -__sus_pure_const constexpr inline T* operator-(T* t, isize offset) { - return t - ptrdiff_t{offset}; -} - -/// Satisfies the [`SubAssign`]($sus::num::SubAssign) concept for pointers -/// (`T*`) with [`isize`]($sus::num::isize). -/// -/// Subtracts a [`isize`]($sus::num::isize) from a referenced pointer, and -/// returns the input reference. -/// -/// #[doc.overloads=ptr.sub.isize] -template -constexpr inline T*& operator-=(T*& t, isize offset) { - t -= ptrdiff_t{offset}; - return t; -} - -/// Satisfies the [`Shl`]($sus::num::Shl) concept for signed primitive integers -/// shifted by [`u64`]($sus::num::u64). -/// #[doc.overloads=signed.prim.< - requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && - std::convertible_to) -[[nodiscard]] __sus_pure_const constexpr inline P operator<<(P l, U r) noexcept { - // No UB checks on primitive types, since there's no promotion to a Subspace - // return type? - return l << u64(r).primitive_value; -} -/// #[doc.overloads=signed.prim.< - requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && - !std::convertible_to) -constexpr inline P operator<<(P l, U r) noexcept = delete; - -/// Satisfies the [`Shr`]($sus::num::Shr) concept for signed primitive integers -/// shifted by [`u64`]($sus::num::u64). -/// -/// Performs sign extension, copying the sign bit to the right if its set. -/// #[doc.overloads=signed.prim.>>u64] -template - requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && - std::convertible_to) -[[nodiscard]] __sus_pure_const constexpr inline P operator>>(P l, U r) noexcept { - // No UB checks on primitive types, since there's no promotion to a Subspace - // return type? - return l >> u64(r).primitive_value; -} -/// #[doc.overloads=signed.prim.>>u64] -template - requires((SignedPrimitiveInteger

|| SignedPrimitiveEnum

) && - !std::convertible_to) -constexpr inline P operator>>(P l, U r) noexcept = delete; - -/// Satisfies the [`Shl`]($sus::num::Shl) concept for signed integers. -/// -/// This operation supports shifting with primitive signed or unsigned integers -/// that convert to the safe numeric, as well as enums. -/// However enum class is excluded as they require an explicit conversion to an -/// integer. -/// -/// Thus the bound is `std::convertible_to` (implicit conversion) instead of -/// `sus::construct::From` (explicit conversion). -/// -/// # Panics -/// This function will panic when `r` is not less than the number of bits in `l` -/// if overflow checks are enabled (they are by default) and will perform a -/// wrapping shift if overflow checks are disabled (not the default). -/// -/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this -/// behaviour. -/// -/// #[doc.overloads=signedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline i8 operator<<( - i8 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift left with overflow"); - return out.value; - } else { - return l.wrapping_shl(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.<<] -template - requires(!std::convertible_to) -constexpr inline i8 operator<<(i8 l, U r) noexcept = delete; -/// Satisfies the [`Shr`]($sus::num::Shr) concept for signed integers. -/// -/// Performs sign extension, copying the sign bit to the right if its set. -/// -/// # Panics -/// This function will panic when `r` is not less than the number of bits in `l` -/// if overflow checks are enabled (they are by default) and will perform a -/// wrapping shift if overflow checks are disabled (not the default). -/// -/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this -/// behaviour. -/// -/// #[doc.overloads=signedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline i8 operator>>( - i8 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift right with overflow"); - return out.value; - } else { - return l.wrapping_shr(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.>>] -template - requires(!std::convertible_to) -constexpr inline i8 operator>>(i8 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline i16 operator<<( - i16 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift left with overflow"); - return out.value; - } else { - return l.wrapping_shl(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.<<] -template - requires(!std::convertible_to) -constexpr inline i16 operator<<(i16 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline i16 operator>>( - i16 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift right with overflow"); - return out.value; - } else { - return l.wrapping_shr(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.>>] -template - requires(!std::convertible_to) -constexpr inline i16 operator>>(i16 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline i32 operator<<( - i32 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift left with overflow"); - return out.value; - } else { - return l.wrapping_shl(u64(r).primitive_value); + /// Satisfies the [`AddAssign`]($sus::num::AddAssign) concept for pointers + /// (`T*`) with [`isize`]($sus::num::isize). + /// + /// Adds a [`isize`]($sus::num::isize) to a referenced pointer, and returns + /// the input reference. + /// + /// #[doc.overloads=ptr.add.isize] + template + constexpr friend T*& operator+=(T*& t, isize offset) { + t += ptrdiff_t{offset}; + return t; } -} -/// #[doc.overloads=signedint.<<] -template - requires(!std::convertible_to) -constexpr inline i32 operator<<(i32 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline i32 operator>>( - i32 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift right with overflow"); - return out.value; - } else { - return l.wrapping_shr(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.>>] -template - requires(!std::convertible_to) -constexpr inline i32 operator>>(i32 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline i64 operator<<( - i64 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift left with overflow"); - return out.value; - } else { - return l.wrapping_shl(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.<<] -template - requires(!std::convertible_to) -constexpr inline i64 operator<<(i64 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline i64 operator>>( - i64 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift right with overflow"); - return out.value; - } else { - return l.wrapping_shr(u64(r).primitive_value); - } -} -/// #[doc.overloads=signedint.>>] -template - requires(!std::convertible_to) -constexpr inline i64 operator>>(i64 l, U r) noexcept = delete; -/// #[doc.overloads=signedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline isize operator<<( - isize l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift left with overflow"); - return out.value; - } else { - return l.wrapping_shl(u64(r).primitive_value); + + /// Satisfies the [`Sub`]($sus::num::Sub) concept for pointers + /// (`T*`) with [`isize`]($sus::num::isize). + /// + /// Subtracts a [`isize`]($sus::num::isize) from a pointer, returning the + /// resulting pointer. + /// + /// #[doc.overloads=ptr.sub.isize] + template + __sus_pure_const constexpr friend T* operator-(T* t, isize offset) { + return t - ptrdiff_t{offset}; } -} -/// #[doc.overloads=signedint.<<] -template - requires(!std::convertible_to) -constexpr inline isize operator<<(isize l, U r) noexcept = delete; -/// #[doc.overloads=signedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline isize operator>>( - isize l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - const auto out = - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value); - sus_check_with_message(!out.overflow, - "attempt to shift right with overflow"); - return out.value; - } else { - return l.wrapping_shr(u64(r).primitive_value); + + /// Satisfies the [`SubAssign`]($sus::num::SubAssign) concept for pointers + /// (`T*`) with [`isize`]($sus::num::isize). + /// + /// Subtracts a [`isize`]($sus::num::isize) from a referenced pointer, and + /// returns the input reference. + /// + /// #[doc.overloads=ptr.sub.isize] + template + constexpr friend T*& operator-=(T*& t, isize offset) { + t -= ptrdiff_t{offset}; + return t; } -} -/// #[doc.overloads=signedint.>>] -template - requires(!std::convertible_to) -constexpr inline isize operator>>(isize l, U r) noexcept = delete; + /// Satisfies the [`Add`]($sus::num::Add) concept for pointers + /// (`T*`) with [`isize`]($sus::num::isize). + /// + /// Adds a [`isize`]($sus::num::isize) to a pointer, returning the resulting + /// pointer. + /// + /// #[doc.overloads=ptr.add.isize] + template + __sus_pure_const constexpr friend T* operator+(T* t, isize offset) { + return t + ptrdiff_t{offset}; + } +}; +#define _self isize +#define _primitive ::sus::num::__private::addr_type<>::signed_type +#include "sus/num/__private/signed_integer_consts.inc" } // namespace sus::num /// For writing [`i8`]($sus::num::i8) literals. diff --git a/sus/num/try_from_int_error.h b/sus/num/try_from_int_error.h index 01f5ae887..c1275c477 100644 --- a/sus/num/try_from_int_error.h +++ b/sus/num/try_from_int_error.h @@ -35,7 +35,10 @@ class TryFromIntError { _sus_pure constexpr Kind kind() const noexcept; /// Satisfies the [`Eq`]($sus::cmp::Eq) concept. - _sus_pure constexpr bool operator==(TryFromIntError rhs) const noexcept; + _sus_pure friend constexpr bool operator==(TryFromIntError lhs, + TryFromIntError rhs) noexcept { + return lhs.kind_ == rhs.kind_; + } private: enum Construct { CONSTRUCT }; diff --git a/sus/num/try_from_int_error_impl.h b/sus/num/try_from_int_error_impl.h index 092aed851..4d162acaa 100644 --- a/sus/num/try_from_int_error_impl.h +++ b/sus/num/try_from_int_error_impl.h @@ -35,11 +35,6 @@ _sus_pure constexpr TryFromIntError::Kind TryFromIntError::kind() return kind_; } -_sus_pure constexpr bool TryFromIntError::operator==( - TryFromIntError rhs) const noexcept { - return kind_ == rhs.kind_; -} - constexpr TryFromIntError::TryFromIntError(Construct, Kind k) noexcept : kind_(k) {} diff --git a/sus/num/types.h b/sus/num/types.h index 3928916b8..849f80551 100644 --- a/sus/num/types.h +++ b/sus/num/types.h @@ -115,4 +115,8 @@ namespace num {} #include "sus/num/try_from_int_error_impl.h" #include "sus/num/unsigned_integer.h" #include "sus/num/unsigned_integer_impl.h" + +// Running an include tracer indicated that placing size_hint_impl.h in this +// header should fix link-time errors when attempting to format `SizeHint`. +#include "sus/iter/size_hint_impl.h" // IWYU pragma: end_exports diff --git a/sus/num/unsigned_integer.h b/sus/num/unsigned_integer.h index 2aedc3721..62b7d6b1e 100644 --- a/sus/num/unsigned_integer.h +++ b/sus/num/unsigned_integer.h @@ -46,6 +46,18 @@ namespace sus::num { // TODO: from_str_radix(). Need Result typ`e and Errors. +/// A 64-bit unsigned integer. +/// +/// See the [namespace level documentation]($sus::num) for more. +struct [[_sus_trivial_abi]] u64 final { +#define _self u64 +#define _pointer false +#define _pointer_sized +#define _primitive uint64_t +#define _signed i64 +#include "sus/num/__private/unsigned_integer_methods.inc" +}; + /// A 32-bit unsigned integer. /// /// See the [namespace level documentation]($sus::num) for more. @@ -57,10 +69,6 @@ struct [[_sus_trivial_abi]] u32 final { #define _signed i32 #include "sus/num/__private/unsigned_integer_methods.inc" }; -#define _self u32 -#define _pointer false -#define _primitive uint32_t -#include "sus/num/__private/unsigned_integer_consts.inc" /// An 8-bit unsigned integer. /// @@ -73,10 +81,6 @@ struct [[_sus_trivial_abi]] u8 final { #define _signed i8 #include "sus/num/__private/unsigned_integer_methods.inc" }; -#define _self u8 -#define _pointer false -#define _primitive uint8_t -#include "sus/num/__private/unsigned_integer_consts.inc" /// A 16-bit unsigned integer. /// @@ -89,26 +93,6 @@ struct [[_sus_trivial_abi]] u16 final { #define _signed i16 #include "sus/num/__private/unsigned_integer_methods.inc" }; -#define _self u16 -#define _pointer false -#define _primitive uint16_t -#include "sus/num/__private/unsigned_integer_consts.inc" - -/// A 64-bit unsigned integer. -/// -/// See the [namespace level documentation]($sus::num) for more. -struct [[_sus_trivial_abi]] u64 final { -#define _self u64 -#define _pointer false -#define _pointer_sized -#define _primitive uint64_t -#define _signed i64 -#include "sus/num/__private/unsigned_integer_methods.inc" -}; -#define _self u64 -#define _pointer false -#define _primitive uint64_t -#include "sus/num/__private/unsigned_integer_consts.inc" /// An address-sized unsigned integer. /// @@ -135,12 +119,46 @@ struct [[_sus_trivial_abi]] usize final { ::sus::num::__private::ptr_type<::sus::mem::size_of()>::unsigned_type #define _signed isize #include "sus/num/__private/unsigned_integer_methods.inc" + + /// Satisfies the [`AddAssign`]($sus::num::AddAssign) concept for pointers + /// (`T*`) with [`usize`]($sus::num::usize). + /// + /// Adds a [`usize`]($sus::num::usize) to a referenced pointer, and returns the + /// input reference. + /// + /// #[doc.overloads=ptr.add.usize] + template + constexpr friend T*& operator+=(T*& t, usize offset) { + t += size_t{offset}; + return t; + } + + /// Satisfies the [`Sub`]($sus::num::Sub) concept for pointers + /// (`T*`) with [`usize`]($sus::num::usize). + /// + /// Subtracts a [`usize`]($sus::num::usize) from a pointer, returning the + /// resulting pointer. + /// + /// #[doc.overloads=ptr.sub.usize] + template + __sus_pure_const constexpr friend T* operator-(T* t, usize offset) { + return t - size_t{offset}; + } + + /// Satisfies the [`SubAssign`]($sus::num::SubAssign) concept for pointers + /// (`T*`) with [`usize`]($sus::num::usize). + /// + /// Subtracts a [`usize`]($sus::num::usize) from a referenced pointer, and + /// returns the input + /// reference. + /// + /// #[doc.overloads=ptr.sub.usize] + template + constexpr friend T*& operator-=(T*& t, usize offset) { + t -= size_t{offset}; + return t; + } }; -#define _self usize -#define _pointer false -#define _primitive \ - ::sus::num::__private::ptr_type<::sus::mem::size_of()>::unsigned_type -#include "sus/num/__private/unsigned_integer_consts.inc" /// A pointer-sized unsigned integer. /// @@ -175,329 +193,6 @@ struct [[_sus_trivial_abi]] uptr final { ::sus::mem::size_of()>::unsigned_type #include "sus/num/__private/unsigned_integer_methods.inc" }; -#define _self uptr -#define _pointer true -#define _primitive \ - ::sus::num::__private::ptr_type< \ - ::sus::mem::size_of()>::unsigned_type -#include "sus/num/__private/unsigned_integer_consts.inc" - -/// Satisfies the [`Add`]($sus::num::Add) concept for pointers -/// (`T*`) with [`usize`]($sus::num::usize). -/// -/// Adds a [`usize`]($sus::num::usize) to a pointer, returning the resulting -/// pointer. -/// -/// #[doc.overloads=ptr.add.usize] -template - requires(std::constructible_from) -__sus_pure_const constexpr inline T* operator+(T* t, U offset) { - return t + size_t{offset}; -} - -/// Satisfies the [`AddAssign`]($sus::num::AddAssign) concept for pointers -/// (`T*`) with [`usize`]($sus::num::usize). -/// -/// Adds a [`usize`]($sus::num::usize) to a referenced pointer, and returns the -/// input reference. -/// -/// #[doc.overloads=ptr.add.usize] -template -constexpr inline T*& operator+=(T*& t, usize offset) { - t += size_t{offset}; - return t; -} - -/// Satisfies the [`Sub`]($sus::num::Sub) concept for pointers -/// (`T*`) with [`usize`]($sus::num::usize). -/// -/// Subtracts a [`usize`]($sus::num::usize) from a pointer, returning the -/// resulting pointer. -/// -/// #[doc.overloads=ptr.sub.usize] -template -__sus_pure_const constexpr inline T* operator-(T* t, usize offset) { - return t - size_t{offset}; -} - -/// Satisfies the [`SubAssign`]($sus::num::SubAssign) concept for pointers -/// (`T*`) with [`usize`]($sus::num::usize). -/// -/// Subtracts a [`usize`]($sus::num::usize) from a referenced pointer, and -/// returns the input -/// reference. -/// -/// #[doc.overloads=ptr.sub.usize] -template -constexpr inline T*& operator-=(T*& t, usize offset) { - t -= size_t{offset}; - return t; -} - -/// Satisfies the [`Shl`]($sus::num::Shl) concept for unsigned primitive -/// integers shifted by [`u64`]($sus::num::u64). -/// #[doc.overloads=unsigned.prim.< - requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && - std::convertible_to) -[[nodiscard]] __sus_pure_const constexpr inline P operator<<(P l, U r) noexcept { - // No UB checks on primitive types, since there's no promotion to a Subspace - // return type? - return l << u64(r).primitive_value; -} -/// #[doc.overloads=unsigned.prim.< - requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && - !std::convertible_to) -constexpr inline P operator<<(P l, U r) noexcept = delete; - -/// Satisfies the [`Shr`]($sus::num::Shr) concept for unsigned primitive -/// integers shifted by [`u64`]($sus::num::u64). -/// #[doc.overloads=unsigned.prim.>>u64] -template - requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && - std::convertible_to) -[[nodiscard]] __sus_pure_const constexpr inline P operator>>(P l, U r) noexcept { - // No UB checks on primitive types, since there's no promotion to a Subspace - // return type? - return l >> u64(r).primitive_value; -} -/// #[doc.overloads=unsigned.prim.>>u64] -template - requires((UnsignedPrimitiveInteger

|| UnsignedPrimitiveEnum

) && - !std::convertible_to) -constexpr inline P operator>>(P l, U r) noexcept = delete; - -/// Satisfies the [`Shl`]($sus::num::Shl) concept for unsigned integers. -/// -/// # Panics -/// This function will panic when `r` is not less than the number of bits in `l` -/// if overflow checks are enabled (they are by default) and will perform a -/// wrapping shift if overflow checks are disabled (not the default). -/// -/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this -/// behaviour. -/// -/// #[doc.overloads=unsignedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline u8 operator<<( - u8 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u8::BITS, - "attempt to shift left with overflow"); - return u8( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); - } else { - return u8( - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.<<] -template - requires(!std::convertible_to) -constexpr inline u8 operator<<(u8 l, U r) noexcept = delete; -/// Satisfies the [`Shr`]($sus::num::Shr) concept for unsigned integers. -/// -/// # Panics -/// This function will panic when `r` is not less than the number of bits in `l` -/// if overflow checks are enabled (they are by default) and will perform a -/// wrapping shift if overflow checks are disabled (not the default). -/// -/// See [overflow checks]($sus::num#overflow-behaviour) for controlling this -/// behaviour. -/// -/// #[doc.overloads=unsignedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline u8 operator>>( - u8 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u8::BITS, - "attempt to shift right with overflow"); - return u8( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); - } else { - return u8( - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.>>] -template - requires(!std::convertible_to) -constexpr inline u8 operator>>(u8 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline u16 operator<<( - u16 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u16::BITS, - "attempt to shift left with overflow"); - return u16( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); - } else { - return u16( - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.<<] -template - requires(!std::convertible_to) -constexpr inline u16 operator<<(u16 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline u16 operator>>( - u16 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u16::BITS, - "attempt to shift right with overflow"); - return u16( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); - } else { - return u16( - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.>>] -template - requires(!std::convertible_to) -constexpr inline u16 operator>>(u16 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline u32 operator<<( - u32 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u32::BITS, "attempt to shift left with overflow"); - return u32( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); - } else { - return u32( - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.<<] -template - requires(!std::convertible_to) -constexpr inline u32 operator<<(u32 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline u32 operator>>( - u32 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u32::BITS, - "attempt to shift right with overflow"); - return u32( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); - } else { - return u32( - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.>>] -template - requires(!std::convertible_to) -constexpr inline u32 operator>>(u32 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline u64 operator<<( - u64 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u64::BITS, "attempt to shift left with overflow"); - return u64( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); - } else { - return u64( - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.<<] -template - requires(!std::convertible_to) -constexpr inline u64 operator<<(u64 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline u64 operator>>( - u64 l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < u64::BITS, - "attempt to shift right with overflow"); - return u64( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); - } else { - return u64( - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.>>] -template - requires(!std::convertible_to) -constexpr inline u64 operator>>(u64 l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline usize operator<<( - usize l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < usize::BITS, "attempt to shift left with overflow"); - return usize( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); - } else { - return usize( - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.<<] -template - requires(!std::convertible_to) -constexpr inline usize operator<<(usize l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline usize operator>>( - usize l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < usize::BITS, "attempt to shift right with overflow"); - return usize( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); - } else { - return usize( - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.>>] -template - requires(!std::convertible_to) -constexpr inline usize operator>>(usize l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.<<] -[[nodiscard]] __sus_pure_const constexpr inline uptr operator<<( - uptr l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < uptr::BITS, "attempt to shift left with overflow"); - return uptr( - __private::unchecked_shl(l.primitive_value, u64(r).primitive_value)); - } else { - return uptr( - __private::shl_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.<<] -template - requires(!std::convertible_to) -constexpr inline uptr operator<<(uptr l, U r) noexcept = delete; -/// #[doc.overloads=unsignedint.>>] -[[nodiscard]] __sus_pure_const constexpr inline uptr operator>>( - uptr l, std::convertible_to auto r) noexcept { - if constexpr (SUS_CHECK_INTEGER_OVERFLOW) { - sus_check_with_message(r < uptr::BITS, "attempt to shift right with overflow"); - return uptr( - __private::unchecked_shr(l.primitive_value, u64(r).primitive_value)); - } else { - return uptr( - __private::shr_with_overflow(l.primitive_value, u64(r).primitive_value) - .value); - } -} -/// #[doc.overloads=unsignedint.>>] -template - requires(!std::convertible_to) -constexpr inline uptr operator>>(uptr l, U r) noexcept = delete; } // namespace sus::num diff --git a/sus/num/unsigned_integer_consts.h b/sus/num/unsigned_integer_consts.h new file mode 100644 index 000000000..f431a3abb --- /dev/null +++ b/sus/num/unsigned_integer_consts.h @@ -0,0 +1,56 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// IWYU pragma: private, include "sus/num/types.h" +// IWYU pragma: friend "sus/.*" +#pragma once + +#include "sus/num/unsigned_integer.h" + +namespace sus::num { + +#define _self u32 +#define _pointer false +#define _primitive uint32_t +#include "sus/num/__private/unsigned_integer_consts.inc" + +#define _self u8 +#define _pointer false +#define _primitive uint8_t +#include "sus/num/__private/unsigned_integer_consts.inc" + +#define _self u16 +#define _pointer false +#define _primitive uint16_t +#include "sus/num/__private/unsigned_integer_consts.inc" + +#define _self u64 +#define _pointer false +#define _primitive uint64_t +#include "sus/num/__private/unsigned_integer_consts.inc" + +#define _self usize +#define _pointer false +#define _primitive \ + ::sus::num::__private::ptr_type<::sus::mem::size_of()>::unsigned_type +#include "sus/num/__private/unsigned_integer_consts.inc" + +#define _self uptr +#define _pointer true +#define _primitive \ + ::sus::num::__private::ptr_type< \ + ::sus::mem::size_of()>::unsigned_type +#include "sus/num/__private/unsigned_integer_consts.inc" + +} // namespace sus::num diff --git a/sus/num/unsigned_integer_impl.h b/sus/num/unsigned_integer_impl.h index bf0d718c7..6c6a38f70 100644 --- a/sus/num/unsigned_integer_impl.h +++ b/sus/num/unsigned_integer_impl.h @@ -60,3 +60,5 @@ #define _pointer 1 #define _primitive ::sus::num::__private::ptr_type<::sus::mem::size_of()>::unsigned_type #include "sus/num/__private/unsigned_integer_methods_impl.inc" +// Comment to prevent clang-format from deleting this line and sorting includes +#include "sus/num/unsigned_integer_consts.h" diff --git a/sus/ops/range.h b/sus/ops/range.h index 5de01c114..d0f25b474 100644 --- a/sus/ops/range.h +++ b/sus/ops/range.h @@ -218,14 +218,17 @@ class Range final : public __private::RangeIter, T> { /// Compares two `Range` for equality, satisfying the [`Eq`]($sus::cmp::Eq) /// concept if `T` satisfies [`Eq`]($sus::cmp::Eq). - constexpr bool operator==(const Range& rhs) const noexcept + constexpr friend bool operator==(const Range& lhs, const Range& rhs) noexcept requires(::sus::cmp::Eq) { - return start == rhs.start && finish == rhs.finish; + return lhs.start == rhs.start && lhs.finish == rhs.finish; } sus_class_trivially_relocatable_if_types(::sus::marker::unsafe_fn, decltype(start), decltype(finish)); + + // Stream support. + _sus_format_to_stream(Range); }; /// A range only bounded inclusively below (`start..`). @@ -299,14 +302,18 @@ class RangeFrom final : public __private::RangeFromIter, T> { } // sus::cmp::Eq trait - constexpr bool operator==(const RangeFrom& rhs) const noexcept + constexpr friend bool operator==(const RangeFrom& lhs, + const RangeFrom& rhs) noexcept requires(::sus::cmp::Eq) { - return start == rhs.start; + return lhs.start == rhs.start; } sus_class_trivially_relocatable_if_types(::sus::marker::unsafe_fn, decltype(start)); + + // Stream support. + _sus_format_to_stream(RangeFrom); }; /// A range only bounded exclusively above (`..end`). @@ -370,14 +377,18 @@ class RangeTo final { constexpr RangeTo end_at(T t) && noexcept { return RangeTo(::sus::move(t)); } // sus::cmp::Eq trait - constexpr bool operator==(const RangeTo& rhs) const noexcept + constexpr friend bool operator==(const RangeTo& lhs, + const RangeTo& rhs) noexcept requires(::sus::cmp::Eq) { - return finish == rhs.finish; + return lhs.finish == rhs.finish; } sus_class_trivially_relocatable_if_types(::sus::marker::unsafe_fn, decltype(finish)); + + // Stream support. + _sus_format_to_stream(RangeTo); }; /// An unbounded range (`..`). @@ -435,13 +446,16 @@ class [[_sus_trivial_abi]] RangeFull final { } // sus::cmp::Eq trait - constexpr bool operator==(const RangeFull& rhs) const noexcept + constexpr friend bool operator==(const RangeFull&, const RangeFull&) noexcept requires(::sus::cmp::Eq) { return true; } sus_class_trivially_relocatable(::sus::marker::unsafe_fn); + + // Stream support. + _sus_format_to_stream(RangeFull); }; /// Return a new Range that starts at `start` and ends at `end`. @@ -487,9 +501,6 @@ struct fmt::formatter<::sus::ops::Range, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::ops, Range, T); - // fmt support. template struct fmt::formatter<::sus::ops::RangeFrom, Char> { @@ -512,9 +523,6 @@ struct fmt::formatter<::sus::ops::RangeFrom, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::ops, RangeFrom, T); - // fmt support. template struct fmt::formatter<::sus::ops::RangeTo, Char> { @@ -537,9 +545,6 @@ struct fmt::formatter<::sus::ops::RangeTo, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::ops, RangeTo, T); - // fmt support. template struct fmt::formatter<::sus::ops::RangeFull, Char> { @@ -560,6 +565,3 @@ struct fmt::formatter<::sus::ops::RangeFull, Char> { private: ::sus::string::__private::AnyFormatter underlying_; }; - -// Stream support. -_sus_format_to_stream(sus::ops, RangeFull, T); diff --git a/sus/option/option.h b/sus/option/option.h index d60201395..259908162 100644 --- a/sus/option/option.h +++ b/sus/option/option.h @@ -365,7 +365,7 @@ namespace sus { /// another [`Option`]($sus::option::Option) as input, and produce an /// [`Option`]($sus::option::Option) as output. /// Only the [`and_that`]($sus::option::Option::and_that) -/// method can produce an [`Option`]($sus::option::Option) value having a +/// method can produce an [`Option`]($sus::option::Option) value having a /// different inner type `U` than [`Option`]($sus::option::Option). /// /// | method | self | input | output | @@ -1933,6 +1933,9 @@ class Option final { } } + // Stream support. + _sus_format_to_stream(Option); + private: template friend class Option; @@ -2097,9 +2100,6 @@ struct fmt::formatter<::sus::option::Option, Char> { ::sus::string::__private::AnyFormatter underlying_; }; -// Stream support. -_sus_format_to_stream(sus::option, Option, T); - // Promote Option and its enum values into the `sus` namespace. namespace sus { using ::sus::option::none; diff --git a/sus/ptr/nonnull.h b/sus/ptr/nonnull.h index 9b2316298..ac4196050 100644 --- a/sus/ptr/nonnull.h +++ b/sus/ptr/nonnull.h @@ -151,6 +151,27 @@ class [[_sus_trivial_abi]] NonNull { static_cast(ptr_)); } + /// Satisfies the [`Eq, NonNull>`]($sus::cmp::Eq) concept if the + /// pointers are comparable and thus satisfy `Eq` as well. + template + requires(::sus::cmp::Eq) + constexpr friend bool operator==(const NonNull& l, + const NonNull& r) noexcept { + return l.as_ptr() == r.as_ptr(); + } + + /// Satisfies the [`StrongOrd>`]($sus::cmp::StrongOrd) concept if the + /// pointers are comparable and thus satisfy `StrongOrd` as well. + template + requires(::sus::cmp::StrongOrd) + constexpr friend std::strong_ordering operator<=>( + const NonNull& l, const NonNull& r) noexcept { + return l.as_ptr() <=> r.as_ptr(); + } + + // Stream support. + _sus_format_to_stream(NonNull); + private: T* ptr_; @@ -166,24 +187,6 @@ class [[_sus_trivial_abi]] NonNull { : ptr_(nullptr) {} }; -/// Satisfies the [`Eq, NonNull>`]($sus::cmp::Eq) concept if the -/// pointers are comparable and thus satisfy `Eq` as well. -template - requires(::sus::cmp::Eq) -constexpr inline bool operator==(const NonNull& l, - const NonNull& r) noexcept { - return l.as_ptr() == r.as_ptr(); -} - -/// Satisfies the [`StrongOrd>`]($sus::cmp::StrongOrd) concept if the -/// pointers are comparable and thus satisfy `StrongOrd` as well. -template - requires(::sus::cmp::StrongOrd) -constexpr inline std::strong_ordering operator<=>( - const NonNull& l, const NonNull& r) noexcept { - return l.as_ptr() <=> r.as_ptr(); -} - } // namespace sus::ptr // fmt support. @@ -203,6 +206,3 @@ struct fmt::formatter<::sus::ptr::NonNull, Char> { private: formatter underlying_; }; - -// Stream support. -_sus_format_to_stream(sus::ptr, NonNull, T); diff --git a/sus/result/result.h b/sus/result/result.h index f120937b5..445ea77fc 100644 --- a/sus/result/result.h +++ b/sus/result/result.h @@ -775,6 +775,7 @@ class [[nodiscard]] Result final { { return eq(l, r); } + template requires(VoidOrEq && ::sus::cmp::Eq) friend constexpr bool operator==(const Result& l, @@ -848,6 +849,9 @@ class [[nodiscard]] Result final { friend constexpr auto operator<=>(const Result& l, const Result& r) noexcept = delete; + // Stream support. + _sus_format_to_stream(Result); + private: template friend class Result; @@ -1141,9 +1145,6 @@ struct fmt::formatter<::sus::result::Result, Char> { ::sus::string::__private::AnyFormatter underlying_err_; }; -// Stream support. -_sus_format_to_stream(sus::result, Result, T, E); - namespace sus { using ::sus::result::Err; using ::sus::result::err; diff --git a/sus/string/__private/format_to_stream.h b/sus/string/__private/format_to_stream.h index 2a2fb3eb7..5dc4b83ce 100644 --- a/sus/string/__private/format_to_stream.h +++ b/sus/string/__private/format_to_stream.h @@ -16,6 +16,7 @@ // IWYU pragma: friend "sus/.*" #pragma once +#include #include #include @@ -23,25 +24,24 @@ #include "sus/macros/compiler.h" #include "sus/macros/for_each.h" -#if SUS_COMPILER_IS_GCC -#include -#endif - namespace sus::string::__private { template concept ConvertibleFrom = std::convertible_to; -template -concept StreamCanReceiveString = requires(T& t, std::basic_string s) { - // Check ConvertibleFrom as std streams return std::basic_ostream&, which the - // input type `T&` is convertible to. The concept ordering means we want - // `ConvertibleFrom<..the output type.., T&>` to be true then. - { t << s } -> ConvertibleFrom; -}; +template +concept StreamCanReceiveString = + /// Ensure that we don't accidentally recursively check `U`. + !std::same_as, std::remove_cvref_t> && + requires(T& t, const std::basic_string s) { + // Check ConvertibleFrom as std streams return std::basic_ostream&, which the + // input type `T&` is convertible to. The concept ordering means we want + // `ConvertibleFrom<..the output type.., T&>` to be true then. + { t << s } -> ConvertibleFrom; + }; /// Consumes the string `s` and streams it to the output stream `os`. -template S> +template S> S& format_to_stream(S& os, const std::basic_string& s) { os << s; return os; @@ -61,55 +61,28 @@ S& format_to_stream(S& os, const std::basic_string& s) { /// /// # Implementation Notes /// -/// The `Type` argument is encoded as a template argument for the GCC compiler -/// because it does not reject the overload when the `Type` does not match -/// otherwise, and then ends up recursively trying to solve -/// `StreamCanReceiveString`. On the first attempt to solve -/// `StreamCanReceiveString`, it tries to call this overload with -/// `std::string` which is *not* `Type` and yet it considers it a valid -/// overload, so it tries to again solve `StreamCanReceiveString` which -/// is now recursive. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99599. -/// -/// Actually it gets worse. If the `Type` has template parameters, we can't use -/// `std::same_as> Sus_ValueType` as the compiler can't -/// infer the `Params...` there, even though `Sus_ValueType` appears in the -/// parameter list. So we _have_ to put the real type in the parameter list. -/// But also in this case GCC does the same thing as the others and does not -/// include the function in the overload set if the non-concept type doesn't -/// match. So we can revert back to the usual incantation then. -// +/// The `U` template type parameter allows `_sus_format_to_stream` to be a +/// hidden friend when `Type` is not a class template. Directly using `Type` +/// means that the function is immediately instantiated, and we'll get a +/// compile-time error unless `fmt::formatter` is specialised before `Type`'s +/// definition, since `fmt::is_formattable` doesn't know it's a formattable type +/// at this point in the code. This isn't a problem for primary class templates, +/// but it is problematic for regular classes and full specialisations. Using a +/// dependent type `U` defers instantiation until `operator<<` is first used. +/// We need to constrain `U` to be the same type as `Type`, otherwise it will +/// be ambiguous as to which overload we want. // clang-format off -#define _sus_format_to_stream(Namespace, Type, ...) \ - namespace Namespace { \ - using namespace ::sus::string::__private; \ - template< \ - _sus_for_each(_sus_format_to_stream_add_class, _sus_for_each_sep_comma, \ - __VA_ARGS__) __VA_OPT__(,) \ - /* Inserts `std::same_as Sus_ValueType` if required for GCC. */ \ - sus_if_gcc( \ - _sus_format_to_stream_parameter_concept##__VA_OPT__(_with_template)( \ - Type __VA_OPT__(<__VA_ARGS__>) \ - ) \ - ) \ - StreamCanReceiveString Sus_StreamType \ - > \ - /** Adaptor from fmt to streams. \ - * #[doc.hidden] */ \ - inline Sus_StreamType& operator<<( \ - Sus_StreamType& stream, \ - /* Uses `Sus_ValueType` as the type if required for GCC, or `Type`. */ \ - sus_if_gcc( \ - const \ - _sus_format_to_stream_parameter##__VA_OPT__(_with_template)( \ - Type __VA_OPT__(<__VA_ARGS__>) \ - ) \ - & value \ - ) \ - /* Uses `Type` unconditionally for non-GCC. */ \ - sus_if_not_gcc(const Type __VA_OPT__(<__VA_ARGS__>)& value) \ - ) { \ - static_assert(fmt::is_formattable)>::value); \ - return format_to_stream(stream, fmt::to_string(value)); \ - } \ - } +#define _sus_format_to_stream(Type) \ + template< \ + sus::string::__private::StreamCanReceiveString Sus_StreamType, \ + std::same_as U = Type \ + > \ + /** Adaptor from fmt to streams. \ + * #[doc.hidden] */ \ + friend Sus_StreamType& operator<<( \ + Sus_StreamType& stream, const U& value) { \ + static_assert(fmt::is_formattable::value); \ + return ::sus::string::__private::format_to_stream(stream, fmt::to_string(value)); \ + } \ + static_assert(true) // clang-format on diff --git a/sus/string/__private/format_to_stream_unittest.cc b/sus/string/__private/format_to_stream_unittest.cc index 279a8c4e8..1765a38a5 100644 --- a/sus/string/__private/format_to_stream_unittest.cc +++ b/sus/string/__private/format_to_stream_unittest.cc @@ -25,7 +25,9 @@ #include "sus/tuple/tuple.h" namespace { -struct Streamable {}; +struct Streamable { + _sus_format_to_stream(Streamable); +}; } // namespace template <> @@ -41,8 +43,6 @@ struct fmt::formatter { } }; -_sus_format_to_stream(, Streamable); - namespace { TEST(FormatToStream, ToStringStream) { diff --git a/sus/tuple/tuple.h b/sus/tuple/tuple.h index 71654dee9..9188bd5a3 100644 --- a/sus/tuple/tuple.h +++ b/sus/tuple/tuple.h @@ -252,18 +252,19 @@ class Tuple final { /// # Implementation Note /// The non-template overload allows tuple() marker types to convert to /// Tuple for comparison. - constexpr bool operator==(const Tuple& r) const& noexcept + constexpr friend bool operator==(const Tuple& l, const Tuple& r) noexcept requires((::sus::cmp::Eq && ... && ::sus::cmp::Eq)) { return __private::storage_eq( - storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); + l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } template requires(sizeof...(Us) == sizeof...(Ts) && (::sus::cmp::Eq && ... && ::sus::cmp::Eq)) - constexpr bool operator==(const Tuple& r) const& noexcept { + constexpr friend bool operator==(const Tuple& l, + const Tuple& r) noexcept { return __private::storage_eq( - storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); + l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } /// Compares two Tuples. @@ -276,31 +277,33 @@ class Tuple final { /// Tuple for comparison. // // sus::cmp::StrongOrd> trait. - constexpr std::strong_ordering operator<=>(const Tuple& r) const& noexcept + constexpr friend std::strong_ordering operator<=>(const Tuple& l, + const Tuple& r) noexcept requires((::sus::cmp::StrongOrd && ... && ::sus::cmp::StrongOrd)) { return __private::storage_cmp( - std::strong_ordering::equal, storage_, r.storage_, + std::strong_ordering::equal, l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } template requires(sizeof...(Us) == sizeof...(Ts) && (::sus::cmp::StrongOrd && ... && ::sus::cmp::StrongOrd)) - constexpr std::strong_ordering operator<=>( - const Tuple& r) const& noexcept { + constexpr friend std::strong_ordering operator<=>( + const Tuple& l, const Tuple& r) noexcept { return __private::storage_cmp( - std::strong_ordering::equal, storage_, r.storage_, + std::strong_ordering::equal, l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } // sus::cmp::Ord> trait. - constexpr std::weak_ordering operator<=>(const Tuple& r) const& noexcept + constexpr friend std::weak_ordering operator<=>(const Tuple& l, + const Tuple& r) noexcept requires(!(::sus::cmp::StrongOrd && ... && ::sus::cmp::StrongOrd) && (::sus::cmp::Ord && ... && ::sus::cmp::Ord)) { return __private::storage_cmp( - std::weak_ordering::equivalent, storage_, r.storage_, + std::weak_ordering::equivalent, l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } template @@ -308,20 +311,21 @@ class Tuple final { !(::sus::cmp::StrongOrd && ... && ::sus::cmp::StrongOrd) && (::sus::cmp::Ord && ... && ::sus::cmp::Ord)) - constexpr std::weak_ordering operator<=>( - const Tuple& r) const& noexcept { + constexpr friend std::weak_ordering operator<=>( + const Tuple& l, const Tuple& r) noexcept { return __private::storage_cmp( - std::weak_ordering::equivalent, storage_, r.storage_, + std::weak_ordering::equivalent, l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } // sus::cmp::PartialOrd> trait. - constexpr std::partial_ordering operator<=>(const Tuple& r) const& noexcept + constexpr friend std::partial_ordering operator<=>(const Tuple& l, + const Tuple& r) noexcept requires(!(::sus::cmp::Ord && ... && ::sus::cmp::Ord) && (::sus::cmp::PartialOrd && ... && ::sus::cmp::PartialOrd)) { return __private::storage_cmp( - std::partial_ordering::equivalent, storage_, r.storage_, + std::partial_ordering::equivalent, l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } template @@ -329,10 +333,10 @@ class Tuple final { !(::sus::cmp::Ord && ... && ::sus::cmp::Ord) && (::sus::cmp::PartialOrd && ... && ::sus::cmp::PartialOrd)) - constexpr std::partial_ordering operator<=>( - const Tuple& r) const& noexcept { + constexpr friend std::partial_ordering operator<=>( + const Tuple& l, const Tuple& r) noexcept { return __private::storage_cmp( - std::partial_ordering::equivalent, storage_, r.storage_, + std::partial_ordering::equivalent, l.storage_, r.storage_, std::make_index_sequence<1u + sizeof...(Ts)>()); } @@ -374,6 +378,8 @@ class Tuple final { } } + _sus_format_to_stream(Tuple); + private: template friend class Tuple; @@ -578,17 +584,6 @@ struct fmt::formatter<::sus::tuple_type::Tuple, Char> { } }; -// Stream support (written out manually due to use of template pack). -namespace sus::tuple_type { -template StreamType> -inline StreamType& operator<<(StreamType& stream, - const Tuple& value) { - return ::sus::string::__private::format_to_stream(stream, - fmt::to_string(value)); -} -} // namespace sus::tuple_type - // Promote Tuple into the `sus` namespace. namespace sus { using ::sus::tuple_type::Tuple;