diff --git a/STYLE.md b/STYLE.md index d8b174533..142f89732 100644 --- a/STYLE.md +++ b/STYLE.md @@ -43,3 +43,9 @@ footguns, crashes, bugs, and UB. * They don't mutate through a global or parameter mutable reference or pointer. * Thus they don't have observable side effects. Calling them on the same input values multiple times always produces the same output. + * Additionally, if the function does not deref a pointer, access through + a reference, or access any static variables, it may be marked + `sus_pure_const`. +1. If a type has implicit ctor from T then it should have assignment from T. + The same is not true for an explicit ctor: no assignment should be present + for that type. diff --git a/subspace/CMakeLists.txt b/subspace/CMakeLists.txt index f7843e173..5500f7133 100644 --- a/subspace/CMakeLists.txt +++ b/subspace/CMakeLists.txt @@ -156,6 +156,7 @@ target_sources(subspace PUBLIC "num/__private/unsigned_integer_consts.inc" "num/__private/unsigned_integer_methods.inc" "num/__private/unsigned_integer_methods_impl.inc" + "num/transmogrify.h" "num/float.h" "num/float_concepts.h" "num/float_impl.h" @@ -225,6 +226,7 @@ if(${SUBSPACE_BUILD_TESTS}) "choice/choice_types_unittest.cc" "choice/choice_unittest.cc" "convert/subclass_unittest.cc" + "construct/to_bits_unittest.cc" "containers/array_unittest.cc" "containers/invalidation_off_size_unittest.cc" "containers/invalidation_on_size_unittest.cc" @@ -258,6 +260,7 @@ if(${SUBSPACE_BUILD_TESTS}) "mem/take_unittest.cc" "num/__private/literals_unittest.cc" "num/cmath_macros_unittest.cc" + "num/transmogrify_unittest.cc" "num/f32_unittest.cc" "num/f64_unittest.cc" "num/i8_unittest.cc" diff --git a/subspace/choice/choice_unittest.cc b/subspace/choice/choice_unittest.cc index 177e185d6..f5789f9e6 100644 --- a/subspace/choice/choice_unittest.cc +++ b/subspace/choice/choice_unittest.cc @@ -122,7 +122,7 @@ TEST(Choice, ConstructorFunction1Value) { } { // into() as an input to the tuple. - U u = sus::choice(sus::into(1)); + U u = sus::choice(sus::into(1_u16)); EXPECT_EQ(u.as(), 1_u32); } { @@ -187,7 +187,7 @@ TEST(Choice, ConstructorFunctionMoreThan1Value) { } { // into() as an input to the tuple. - U u = sus::choice(1u, sus::into(2)); + U u = sus::choice(1u, sus::into(2_u16)); EXPECT_EQ(u.as().into_inner<0>(), 1_u32); EXPECT_EQ(u.as().into_inner<1>(), 2_u32); } diff --git a/subspace/construct/from.h b/subspace/construct/from.h index 603cbddac..a995b095c 100644 --- a/subspace/construct/from.h +++ b/subspace/construct/from.h @@ -16,6 +16,7 @@ #include +#include "subspace/mem/forward.h" #include "subspace/result/__private/is_result_type.h" namespace sus::construct { @@ -48,7 +49,7 @@ namespace sus::construct { /// ``` template concept From = requires(FromType&& from) { - { ToType::from(static_cast(from)) } -> std::same_as; + { ToType::from(::sus::forward(from)) } -> std::same_as; }; /// A concept that indicates `ToType` can be (sometimes) constructed from a @@ -59,11 +60,9 @@ concept From = requires(FromType&& from) { /// `sus::Result`. template concept TryFrom = requires(FromType&& from) { - { ToType::try_from(static_cast(from)) }; - requires std::same_as< - typename ::sus::result::__private::IsResultType(from)))>::ok_type, - ToType>; + { + ToType::try_from(::sus::forward(from)) + } -> ::sus::result::__private::IsResultWithSuccessType; }; } // namespace sus::construct diff --git a/subspace/construct/from_unittest.cc b/subspace/construct/from_unittest.cc index a08f7a8de..d1f8dd19b 100644 --- a/subspace/construct/from_unittest.cc +++ b/subspace/construct/from_unittest.cc @@ -15,8 +15,10 @@ #include "subspace/construct/from.h" #include "subspace/construct/into.h" +#include "subspace/result/result.h" using sus::construct::From; +using sus::construct::TryFrom; namespace { @@ -44,4 +46,24 @@ static_assert(!From); static_assert(!From); static_assert(!From); +enum Error {}; + +struct TryYes { + static sus::Result try_from(S) { return sus::ok(TryYes()); } +}; +struct TryNoResult { + static TryYes try_from(S) { return TryYes(); } +}; +struct TryWrongResult { + static sus::Result try_from(S) { return sus::ok(S()); } +}; +struct TryVoidResult { + static sus::Result try_from(S) { return sus::ok(); } +}; + +static_assert(TryFrom); +static_assert(!TryFrom); +static_assert(!TryFrom); +static_assert(!TryFrom); + } // namespace diff --git a/subspace/construct/into.h b/subspace/construct/into.h index 5830fc5ba..32d363be1 100644 --- a/subspace/construct/into.h +++ b/subspace/construct/into.h @@ -22,10 +22,12 @@ #include "subspace/construct/__private/into_ref.h" #include "subspace/construct/from.h" #include "subspace/macros/compiler.h" +#include "subspace/macros/lifetimebound.h" +#include "subspace/mem/forward.h" namespace sus::construct { -/// A concept that declares `FromType` can be converted to `ToType`, through the +/// A concept that declares `FromType` can be converted to `ToType` through the /// `From` concept or through an identity transformation. /// /// When true, `ToType::from(FromType)` can be used to construct `ToType`, @@ -84,16 +86,14 @@ template requires(!std::is_const_v> && std::is_rvalue_reference_v && !std::is_array_v) -constexpr inline auto into( - FromType&& from sus_if_clang([[clang::lifetimebound]])) noexcept { +constexpr inline auto into(FromType&& from sus_lifetimebound) noexcept { return __private::IntoRef( static_cast&&>(from)); } template requires(std::is_trivially_copyable_v && !std::is_array_v) -constexpr inline auto into( - const FromType& from sus_if_clang([[clang::lifetimebound]])) noexcept { +constexpr inline auto into(const FromType& from sus_lifetimebound) noexcept { return __private::IntoRef(from); } @@ -101,8 +101,7 @@ template requires(!std::is_const_v> && std::is_trivially_move_constructible_v && !std::is_array_v) -constexpr inline auto into( - FromType& from sus_if_clang([[clang::lifetimebound]])) noexcept { +constexpr inline auto into(FromType& from sus_lifetimebound) noexcept { return __private::IntoRef( static_cast&&>(from)); } @@ -113,7 +112,7 @@ constexpr inline auto into( /// satisfied. template constexpr inline auto array_into( - FromType (&from)[N] sus_if_clang([[clang::lifetimebound]])) noexcept { + FromType (&from)[N] sus_lifetimebound) noexcept { return __private::IntoRefArray(from); } @@ -128,8 +127,7 @@ constexpr inline auto array_into( template requires(!std::is_const_v> && !std::is_array_v) -constexpr inline auto move_into( - FromType& from sus_if_clang([[clang::lifetimebound]])) noexcept { +constexpr inline auto move_into(FromType& from sus_lifetimebound) noexcept { return __private::IntoRef( static_cast>&&>(from)); } @@ -138,13 +136,37 @@ template requires(std::is_rvalue_reference_v && !std::is_const_v> && !std::is_array_v) -constexpr inline auto move_into( - FromType&& from sus_if_clang([[clang::lifetimebound]])) noexcept { +constexpr inline auto move_into(FromType&& from sus_lifetimebound) noexcept { return __private::IntoRef( static_cast>&&>(from)); } -// TODO: Consider adding sus::try_into() and a TryInto concept? +/// A concept that declares `FromType` can (sometimes) be converted to `ToType` +/// through the `TryFrom` concept or through an identity transformation. +template +concept TryInto = ::sus::construct::TryFrom || + std::same_as; + +/// Attempts to convert from the given value to a `ToType`. +/// +/// Unlike `into()`, this function can not use type deduction to determine the +/// receiving type as it needs to determine the Result type and allow the caller +/// the chance to handle the error condition. +/// +/// The `TryFrom` concept requires a `try_from()` method that returns a +/// `Result`. That `Result` will be the return type of this function. +/// +/// # Example +/// ``` +/// auto valid = sus::try_into(123_i32).unwrap_or_default(); +/// sus::check(valid == 123); +/// auto invalid = sus::try_into(-1_i32).unwrap_or_default(); +/// sus::check(invalid == 0); +/// ``` +template FromType> +constexpr inline auto try_into(FromType&& from) noexcept { + return ToType::try_from(::sus::forward(from)); +} } // namespace sus::construct @@ -153,4 +175,5 @@ namespace sus { using ::sus::construct::array_into; using ::sus::construct::into; using ::sus::construct::move_into; +using ::sus::construct::try_into; } // namespace sus diff --git a/subspace/construct/into_unittest.cc b/subspace/construct/into_unittest.cc index db0a6e9e9..1d412de02 100644 --- a/subspace/construct/into_unittest.cc +++ b/subspace/construct/into_unittest.cc @@ -17,6 +17,7 @@ #include "googletest/include/gtest/gtest.h" #include "subspace/macros/__private/compiler_bugs.h" #include "subspace/mem/forward.h" +#include "subspace/prelude.h" #include "subspace/test/behaviour_types.h" using sus::construct::From; @@ -156,4 +157,11 @@ TEST(Into, Concept) { EXPECT_EQ(f(S(3)).got_value, 3); } +TEST(TryInto, Example) { + auto valid = sus::try_into(123_i32).unwrap_or_default(); + sus::check(valid == 123u); + auto invalid = sus::try_into(-1_i32).unwrap_or_default(); + sus::check(invalid == 0u); +} + } // namespace diff --git a/subspace/construct/to_bits.h b/subspace/construct/to_bits.h new file mode 100644 index 000000000..137078ab3 --- /dev/null +++ b/subspace/construct/to_bits.h @@ -0,0 +1,157 @@ +// 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. + +#pragma once + +#include + +#include "subspace/mem/copy.h" + +namespace sus::construct { + +/// Specializing this class for `To` and `From` allows `To` to satisfy +/// `Transmogrify`. +/// +/// # Examples +/// +/// To allow bitwise conversion to `Goat` from any type satisying a +/// concept `GoatLike`: +/// ```cpp +/// // Satisfies Transmogrify. +/// template +/// struct TransmogrifyImpl { +/// constexpr static Goat mog_from(const G& g) noexcept { return ...; } +/// }; +/// ``` +/// +/// To receive something that can be bitwise converted to an `u32`. +/// ```cpp +/// auto add = [](u32 a, const sus::construct::Transmogrify auto& b) -> u32 +/// { +/// return a.wrapping_add(sus::mog(b)); +/// }; +/// sus::check(add(3_u32, -1_i32) == u32::MIN + 2); +/// ``` +template +struct TransmogrifyImpl; + +// sus::construct::Transmogrify trait for identity conversion when `T` is +// `Copy`. +template <::sus::mem::Copy T> +struct TransmogrifyImpl { + constexpr static T mog_from(const T& from) noexcept { return from; } +}; + +/// When a pair of types `T` and `F` satisfy `Transmogrify`, it means that +/// `F` can be converted +/// ([transmogrified](https://calvinandhobbes.fandom.com/wiki/Transmogrifier)) +/// to `T` through a conversion that will always succeed in producing _some_ +/// value, but may be lossy or produce a value with a different meaning. The +/// conversion may truncate or extend `F` in order to do the conversion to `T`. +/// +/// This operation is also commonly known as type casting, or type coercion. The +/// conversion to `T` can be done by calling `sus::mog(from)`. +/// +/// The conversion is defined for the identity conversion where both the input +/// and output are the same type, if the type is `Copy`, in which case the input +/// is copied and returned. As Transmogrification is meant to be a cheap +/// conversion, primarily for primitive types, it does not support `Clone` +/// types, and `sus::construct::Into` should be used in more complex cases. +/// +/// For numeric and primitive types, `Transmogrify` is defined to provide a +/// mechanism like `static_cast` but it is much safer than `static_cast` +/// as it has defined behaviour for all inputs: +/// +/// * Casting from a float to an integer will perform a static_cast, which +/// rounds the float towards zero, except: +/// * `NAN` will return 0. +/// * Values larger than the maximum integer value, including `f32::INFINITY`, +/// will saturate to the maximum value of the integer type. +/// * Values smaller than the minimum integer value, including +/// `f32::NEG_INFINITY`, will saturate to the minimum value of the integer +/// type. +/// * Casting from an integer to a float will perform a static_cast, which +/// converts to the nearest floating point value. The rounding direction for +/// values that land between representable floating point values is +/// implementation defined (per C++20 Section 7.3.10). +/// * Casting from an `f32` (or `float`) to an `f64` (or `double`) preserves the +/// value unchanged. +/// * Casting from an `f64` (or `double`) to an `f32` (or float) performs the +/// same action as a static_cast if the value is in range for f32, otherwise: +/// * `NAN` will return a `NAN`. +/// * Values outside of f32's range will return `f32::INFINITY` or +/// `f32::NEG_INFINITY` for positive and negative values respectively. +/// * Casting to and from `std::byte` produces the same values as casting to and +/// from `u8`. +/// +/// These conversions are all defined in `subspace/num/types.h`. +/// +/// The transmogrifier is one of three of the most complicated inventions. The +/// other two are the [Cerebral +/// Enhance-O-Tron](https://calvinandhobbes.fandom.com/wiki/Cerebral_Enhance-O-Tron), +/// and the [Transmogrifier +/// Gun](https://calvinandhobbes.fandom.com/wiki/Transmogrifier_Gun). +/// +/// # Extending to other types +/// +/// Types can participate in defining their transmogrification strategy by +/// providing a specialization of `sus::convert::TransmogrifyImpl`. +/// The conversions should always produce a value of type `T`, should not panic, +/// and should not cause Undefined Behaviour. +/// +/// The `Transmogrify` specialization needs a static method `mog_from()` that +/// receives `const From&` and returns `To`. +template +concept Transmogrify = requires(const From& from) { + { + ::sus::construct::TransmogrifyImpl::mog_from(from) + } noexcept -> std::same_as; +}; + +/// An infallible conversion (transmogrification) that may lose the original +/// value in the process. If the input can not be represented in the output, +/// some other value will be produced, which may lead to application bugs and +/// memory unsafety if used incorrectly. This behaves like `static_cast()` +/// but without Undefined Behaviour. +/// +/// The `mog` operation is supported for types `To` and `From` that satisfy +/// `Transmogrify`. +/// +/// To convert between types while ensuring the values are preserved, use +/// `sus::construct::Into` or `sus::construct::TryInto`. Usually prefer using +/// `sus::into(x)` or `sus::try_into(x)` over `sus::mog(x)` as most code +/// should preserve values across type transitions. +/// +/// See `Transmogrify` for how numeric and primitive values are converted. +/// +/// # Examples +/// +/// This converts `-1_i64` into a `u32`, which both changes its meaning, +/// becoming a large positive number, and truncates the high 32 bits, losing the +/// original. +/// ```cpp +/// sus::check(u32::MAX == sus::mog(-1_i64)); +/// ``` +template + requires(Transmogrify) +constexpr inline To mog(const From& from) { + return TransmogrifyImpl::mog_from(from); +} + +} // namespace sus::construct + +// Bring the mog() function into the `sus` namespace. +namespace sus { +using ::sus::construct::mog; +} diff --git a/subspace/construct/to_bits_unittest.cc b/subspace/construct/to_bits_unittest.cc new file mode 100644 index 000000000..7f32d3105 --- /dev/null +++ b/subspace/construct/to_bits_unittest.cc @@ -0,0 +1,33 @@ +// 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. + +#include "subspace/construct/to_bits.h" + +#include "googletest/include/gtest/gtest.h" +#include "subspace/prelude.h" + +namespace { + +TEST(Transmogrify, Example_Concept) { + auto add = [](u32 a, const sus::construct::Transmogrify auto& b) -> u32 { + return a.wrapping_add(sus::mog(b)); + }; + sus::check(add(3_u32, -1_i32) == u32::MIN + 2u); +} + +TEST(Transmogrify, Example_Function) { + sus::check(u32::MAX == sus::mog(-1_i64)); +} + +} // namespace diff --git a/subspace/containers/array.h b/subspace/containers/array.h index 143accd5e..623cbc4a6 100644 --- a/subspace/containers/array.h +++ b/subspace/containers/array.h @@ -69,7 +69,7 @@ struct Storage final {}; /// greater distance results in Undefined Behaviour. template class Array final { - static_assert(N <= usize::from(isize::MAX)); + static_assert(N <= ::sus::mog(isize::MAX)); static_assert(!std::is_reference_v, "Array is invalid as Array must hold value types. Use " "Array instead."); diff --git a/subspace/containers/array_unittest.cc b/subspace/containers/array_unittest.cc index 00d0decf8..bcfeb7cb8 100644 --- a/subspace/containers/array_unittest.cc +++ b/subspace/containers/array_unittest.cc @@ -141,8 +141,7 @@ TEST(Array, WithValues) { } } { - auto a = Array::with(sus::into(3), sus::into(4), sus::into(5), - sus::into(6), sus::into(7)); + auto a = Array::with(3_u8, 4_u8, 5_u8, 6_u8, 7_u8); for (auto i = 0_u8; i < 5_u8; i += 1_u8) { EXPECT_EQ(a[usize::from(i)], 3_u8 + i); } @@ -173,7 +172,7 @@ TEST(Array, ConstructorFunction) { } { // into() as an input to the array. - Array a = sus::array(1_u32, sus::into(2), 3_u32); + Array a = sus::array(1_u32, sus::into(2_u16), 3_u32); EXPECT_EQ(a[0u], 1_u32); EXPECT_EQ(a[1u], 2_u32); EXPECT_EQ(a[2u], 3_u32); @@ -460,7 +459,7 @@ TEST(Array, ImplicitIter) { TEST(Array, Map) { auto a = Array::with(3u, 4u, 5u); - auto a2 = sus::move(a).map(+[](usize i) { return u32::from(i + 1_usize); }); + auto a2 = sus::move(a).map(+[](usize i) { return u32::try_from(i + 1_usize).unwrap(); }); EXPECT_EQ(a2, (Array::with(4_u32, 5_u32, 6_u32))); } diff --git a/subspace/containers/iterators/slice_iter.h b/subspace/containers/iterators/slice_iter.h index 8a47e94da..9ec70e19b 100644 --- a/subspace/containers/iterators/slice_iter.h +++ b/subspace/containers/iterators/slice_iter.h @@ -82,20 +82,17 @@ struct [[nodiscard]] [[sus_trivial_abi]] SliceIter final } ::sus::iter::SizeHint size_hint() const noexcept { - // SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can - // not exceed isize::MAX. - const auto remaining = ::sus::num::usize::from_unchecked( - ::sus::marker::unsafe_fn, end_ - ptr_); + const auto remaining = exact_size_hint(); return ::sus::iter::SizeHint( remaining, ::sus::Option<::sus::num::usize>::with(remaining)); } /// sus::iter::ExactSizeIterator trait. ::sus::num::usize exact_size_hint() const noexcept { - // SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can + // SAFETY: The constructor checks that `end_ - ptr_` is positive and Slice can // not exceed isize::MAX. - return ::sus::num::usize::from_unchecked(::sus::marker::unsafe_fn, - end_ - ptr_); + return ::sus::num::usize::try_from(end_ - ptr_) + .unwrap_unchecked(::sus::marker::unsafe_fn); } private: @@ -140,9 +137,13 @@ struct [[sus_trivial_abi]] SliceIterMut final /// Returns a mutable slice of the items left to be iterated, consuming the /// iterator. SliceMut as_mut_slice() && { - return SliceMut::from_raw_parts_mut(::sus::marker::unsafe_fn, - ref_.to_view(), ptr_, - usize::from(end_ - ptr_)); + return SliceMut::from_raw_parts_mut( + ::sus::marker::unsafe_fn, ref_.to_view(), ptr_, + // SAFETY: `end_ > ptr_` at all times, and the distance between two + // pointers in a single allocation is at most isize::MAX which fits in + // usize. + usize::try_from(end_ - ptr_) + .unwrap_unchecked(::sus::marker::unsafe_fn)); } // sus::iter::Iterator trait. @@ -173,11 +174,11 @@ struct [[sus_trivial_abi]] SliceIterMut final } /// sus::iter::ExactSizeIterator trait. - ::sus::num::usize exact_size_hint() const noexcept { + usize exact_size_hint() const noexcept { // SAFETY: The constructor checks that end_ - ptr_ is positive and Slice can // not exceed isize::MAX. - return ::sus::num::usize::from_unchecked(::sus::marker::unsafe_fn, - end_ - ptr_); + return usize::try_from(end_ - ptr_) + .unwrap_unchecked(::sus::marker::unsafe_fn); } private: diff --git a/subspace/containers/slice.h b/subspace/containers/slice.h index a491f3c9e..69d225cce 100644 --- a/subspace/containers/slice.h +++ b/subspace/containers/slice.h @@ -27,6 +27,7 @@ #include "subspace/containers/iterators/split.h" #include "subspace/containers/iterators/windows.h" #include "subspace/containers/join.h" +#include "subspace/num/transmogrify.h" #include "subspace/fn/fn_concepts.h" #include "subspace/fn/fn_ref.h" #include "subspace/iter/iterator_defn.h" @@ -109,7 +110,7 @@ class [[sus_trivial_abi]] Slice final { sus_pure static constexpr inline Slice from_raw_parts( ::sus::marker::UnsafeFnMarker, ::sus::iter::IterRefCounter refs, const T* data sus_lifetimebound, usize len) noexcept { - ::sus::check(size_t{len} <= size_t{isize::MAX}); + ::sus::check(len <= ::sus::mog(isize::MAX)); // We strip the `const` off `data`, however only const access is provided // through this class. This is done so that mutable types can compose Slice // and store a mutable pointer. @@ -122,7 +123,7 @@ class [[sus_trivial_abi]] Slice final { /// /// #[doc.overloads=from.array] template - requires(N <= size_t{isize::MAX}) + requires(N <= ::sus::mog(isize::MAX)) sus_pure static constexpr inline Slice from( const T (&data)[N] sus_lifetimebound) { // We strip the `const` off `data`, however only const access is provided @@ -318,7 +319,7 @@ class [[sus_trivial_abi]] SliceMut final { sus_pure static constexpr inline SliceMut from_raw_parts_mut( ::sus::marker::UnsafeFnMarker, ::sus::iter::IterRefCounter refs, T* data sus_lifetimebound, usize len) noexcept { - ::sus::check(len <= usize::from(isize::MAX)); + ::sus::check(len <= ::sus::mog(isize::MAX)); return SliceMut(::sus::move(refs), data, len); } @@ -328,7 +329,7 @@ class [[sus_trivial_abi]] SliceMut final { /// /// #[doc.overloads=from.array] template - requires(N <= size_t{isize::MAX_PRIMITIVE}) + requires(N <= ::sus::mog(isize::MAX_PRIMITIVE)) sus_pure static constexpr inline SliceMut from( T (&data)[N] sus_lifetimebound) { return SliceMut(::sus::iter::IterRefCounter::empty_for_view(), data, N); diff --git a/subspace/containers/vec.h b/subspace/containers/vec.h index 5bb072b01..ea33a98ea 100644 --- a/subspace/containers/vec.h +++ b/subspace/containers/vec.h @@ -44,6 +44,7 @@ #include "subspace/mem/relocate.h" #include "subspace/mem/replace.h" #include "subspace/mem/size_of.h" +#include "subspace/num/transmogrify.h" #include "subspace/num/integer_concepts.h" #include "subspace/num/signed_integer.h" #include "subspace/num/unsigned_integer.h" @@ -114,7 +115,8 @@ class Vec final { /// # Panics /// Panics if the capacity exceeds `isize::MAX` bytes. sus_pure static inline constexpr Vec with_capacity(usize capacity) noexcept { - check(::sus::mem::size_of() * capacity <= usize::from(isize::MAX)); + check(::sus::mem::size_of() * capacity <= + ::sus::mog(isize::MAX)); auto v = Vec(nullptr, 0_usize, 0_usize); // TODO: Consider rounding up to nearest 2^N for some N? A min capacity? v.grow_to_exact(capacity); @@ -425,7 +427,7 @@ class Vec final { check(!has_iterators()); if (cap <= capacity_) return; // Nothing to do. const auto bytes = ::sus::mem::size_of() * cap; - check(bytes <= usize::from(isize::MAX)); + check(bytes <= ::sus::mog(isize::MAX)); if (!is_alloced()) { raw_data() = static_cast(malloc(bytes.primitive_value)); } else { diff --git a/subspace/containers/vec_unittest.cc b/subspace/containers/vec_unittest.cc index bfa2a30a5..e18db726a 100644 --- a/subspace/containers/vec_unittest.cc +++ b/subspace/containers/vec_unittest.cc @@ -114,7 +114,7 @@ TEST(Vec, ConstructorFunction) { } { // into() as an input to the vec. - Vec a = sus::vec(1_u32, sus::into(2), 3_u32); + Vec a = sus::vec(1_u32, sus::into(2_u16), 3_u32); EXPECT_EQ(a.len(), 3_usize); EXPECT_EQ(a[0u], 1_u32); EXPECT_EQ(a[1u], 2_u32); diff --git a/subspace/iter/__private/step.h b/subspace/iter/__private/step.h index b14b2ce19..6e5343656 100644 --- a/subspace/iter/__private/step.h +++ b/subspace/iter/__private/step.h @@ -15,31 +15,33 @@ #pragma once #include "subspace/marker/unsafe.h" -#include "subspace/option/option.h" #include "subspace/num/integer_concepts.h" #include "subspace/num/unsigned_integer.h" +#include "subspace/option/option.h" namespace sus::iter::__private { template <::sus::num::Integer T> constexpr T step_forward(T l) noexcept { // SAFETY: All `Integer` can hold `1`. - return l + T::from_unchecked(::sus::marker::unsafe_fn, 1); + return l + T::try_from(1).unwrap_unchecked(::sus::marker::unsafe_fn); } template <::sus::num::Integer T> constexpr T step_backward(T l) noexcept { // SAFETY: All `Integer` can hold `1`. - return l - T::from_unchecked(::sus::marker::unsafe_fn, 1); + return l - T::try_from(1).unwrap_unchecked(::sus::marker::unsafe_fn); } template <::sus::num::Integer T> constexpr ::sus::Option step_forward_checked(T l) noexcept { // SAFETY: All `Integer` can hold `1`. - return l.checked_add(T::from_unchecked(::sus::marker::unsafe_fn, 1)); + return l.checked_add( + T::try_from(1).unwrap_unchecked(::sus::marker::unsafe_fn)); } template <::sus::num::Integer T> constexpr ::sus::Option step_backward_checked(T l) noexcept { // SAFETY: All `Integer` can hold `1`. - return l.checked_sub(T::from_unchecked(::sus::marker::unsafe_fn, 1)); + return l.checked_sub( + T::try_from(1).unwrap_unchecked(::sus::marker::unsafe_fn)); } template <::sus::num::Integer T> constexpr T step_forward_by(T l, ::sus::num::usize steps) noexcept { @@ -62,8 +64,8 @@ constexpr ::sus::Option step_backward_by_checked( [&l](T steps) { return l.checked_sub(::sus::marker::unsafe_fn, steps); }); } template <::sus::num::Integer T> -constexpr ::sus::Option<::sus::num::usize> steps_between( - const T& l, const T& r) noexcept { +constexpr ::sus::Option<::sus::num::usize> steps_between(const T& l, + const T& r) noexcept { return r.checked_sub(l).and_then( [](T steps) { return ::sus::num::usize::try_from(steps).ok(); }); } diff --git a/subspace/iter/compat_ranges.h b/subspace/iter/compat_ranges.h index acc456380..46944f2fb 100644 --- a/subspace/iter/compat_ranges.h +++ b/subspace/iter/compat_ranges.h @@ -24,6 +24,8 @@ #include "subspace/macros/__private/compiler_bugs.h" #include "subspace/macros/lifetimebound.h" #include "subspace/mem/move.h" +#include "subspace/num/transmogrify.h" +#include "subspace/num/unsigned_integer.h" #include "subspace/option/option.h" namespace sus::iter { @@ -143,7 +145,11 @@ class IteratorOverRange final usize exact_size_hint() const noexcept requires(std::sized_sentinel_for) { - return usize::from(end_ - begin_); + // SAFETY: `end_ > begin_` so the value is not negative and offsets in a + // container are always in the range of `isize`, so the value is + // representable in `usize`. + return usize::try_from(end_ - begin_) + .unwrap_unchecked(::sus::marker::unsafe_fn); } // TODO: If std::random_access_range then implement more efficient nth(), diff --git a/subspace/iter/iterator_unittest.cc b/subspace/iter/iterator_unittest.cc index bd59050e8..2c7098d77 100644 --- a/subspace/iter/iterator_unittest.cc +++ b/subspace/iter/iterator_unittest.cc @@ -291,7 +291,7 @@ TEST(Iterator, FilterMap) { auto it = sus::Vec::with(1, 2, 3, 4, 5).into_iter(); auto fmit = sus::move(it).filter_map([](i32&& i) -> sus::Option { if (i >= 3) { - return sus::some(u32::from(i)); + return u32::try_from(i).ok(); } else { return sus::none(); } @@ -340,7 +340,7 @@ TEST(Iterator, Map) { i32 nums[5] = {1, 2, 3, 4, 5}; auto it = ArrayIterator::with_array(nums).map( - [](i32&& i) { return u32::from(i); }); + [](i32&& i) { return u32::try_from(i).unwrap(); }); auto v = sus::move(it).collect_vec(); static_assert(std::same_as>); { @@ -356,7 +356,7 @@ TEST(Iterator, Map) { }; auto it2 = ArrayIterator::with_array(nums) - .map([](i32&& i) { return u32::from(i); }) + .map([](i32&& i) { return u32::try_from(i).unwrap(); }) .map([](u32&& i) { return MapOut(i); }); auto v2 = sus::move(it2).collect_vec(); static_assert(std::same_as>); @@ -373,7 +373,7 @@ TEST(Iterator, MapDoubleEnded) { i32 nums[5] = {1, 2, 3, 4, 5}; auto it = ArrayIterator::with_array(nums).map( - [](i32&& i) { return u32::from(i); }); + [](i32&& i) { return u32::try_from(i).unwrap(); }); EXPECT_EQ(it.next_back(), sus::some(5_u32).construct()); EXPECT_EQ(it.next_back(), sus::some(4_u32).construct()); EXPECT_EQ(it.next_back(), sus::some(3_u32).construct()); @@ -387,7 +387,7 @@ TEST(Iterator, MapWhile) { auto nums = sus::Array::with(1, 2, 3, 4, 5); auto it = sus::move(nums).into_iter().map_while([](i32&& i) -> Option { if (i != 4) - return sus::some(u32::from(i)); + return u32::try_from(i).ok(); else return sus::none(); }); @@ -484,15 +484,15 @@ TEST(Iterator, Enumerate) { auto vec = Vec::with(0, 2, 4, 6, 8); // Front to back. for (auto [i, value] : vec.iter().enumerate()) { - EXPECT_EQ(i * 2u, usize::from(value)); + EXPECT_EQ(i * 2u, usize::try_from(value).unwrap()); } // Back to front. for (auto [i, value] : vec.iter().enumerate().rev()) { - EXPECT_EQ(i * 2u, usize::from(value)); + EXPECT_EQ(i * 2u, usize::try_from(value).unwrap()); } // Back to front without reversing the indices. for (auto [i, value] : vec.iter().rev().enumerate()) { - EXPECT_EQ((4u - i) * 2u, usize::from(value)); + EXPECT_EQ((4u - i) * 2u, usize::try_from(value).unwrap()); } } @@ -1348,7 +1348,7 @@ TEST(Iterator, FindMap) { // Works on lvalue iterator. { auto find_even = [](const i32& i) -> sus::Option { - if (i % 2 == 0) return sus::some(u32::from(i)); + if (i % 2 == 0) return u32::try_from(i).ok(); return sus::none(); }; @@ -1667,10 +1667,10 @@ TEST(Iterator, Fold) { // Check the accumulator type can be different from the iterating type. { auto it = sus::Array::with(1, 2, 3, 4, 5).into_iter(); - auto o = - sus::move(it).fold(10_u32, - // Receiving rvalue ensures the caller did move. - [](u32 acc, i32&& v) { return u32::from(v) + acc; }); + auto o = sus::move(it).fold( + 10_u32, + // Receiving rvalue ensures the caller did move. + [](u32 acc, i32&& v) { return u32::try_from(v).unwrap() + acc; }); static_assert(std::same_as); EXPECT_EQ(o, (5u + (4u + (3u + (2u + (1u + 10u)))))); } @@ -1701,7 +1701,7 @@ TEST(Iterator, Rfold) { auto o = sus::move(it).rfold( 10_u32, // Receiving rvalue ensures the caller did move. - [](u32 acc, i32&& v) { return u32::from(v) + acc; }); + [](u32 acc, i32&& v) { return u32::try_from(v).unwrap() + acc; }); static_assert(std::same_as); EXPECT_EQ(o, (1u + (2u + (3u + (4u + (5u + 10u)))))); } diff --git a/subspace/mem/swap_unittest.cc b/subspace/mem/swap_unittest.cc index 2cd92b18f..c8e527604 100644 --- a/subspace/mem/swap_unittest.cc +++ b/subspace/mem/swap_unittest.cc @@ -211,7 +211,7 @@ TEST(Swap, Alias) { [i = 0_i32]() mutable { return sus::mem::replace(i, i + 1); })); sus::mem::swap(mref(t), mref(t)); for (usize j : "0..100"_r) { - EXPECT_EQ(t.num[j], i32::from(j)); + EXPECT_EQ(t.num[j], i32::try_from(j).unwrap()); } EXPECT_EQ(Trivial::moves, 0u); } @@ -240,8 +240,8 @@ TEST(Swap, NoAliasUnchecked) { [i = 10_i32]() mutable { return sus::mem::replace(i, i + 1); })); sus::mem::swap_nonoverlapping(unsafe_fn, mref(t1), mref(t2)); for (usize j : "0..100"_r) { - EXPECT_EQ(t1.num[j], i32::from(j) + 10); - EXPECT_EQ(t2.num[j], i32::from(j)); + EXPECT_EQ(t1.num[j], i32::try_from(j).unwrap() + 10); + EXPECT_EQ(t2.num[j], i32::try_from(j).unwrap()); } EXPECT_EQ(Trivial::moves, 0u); } diff --git a/subspace/num/__private/float_methods.inc b/subspace/num/__private/float_methods.inc index fcfaee977..05ffb2d07 100644 --- a/subspace/num/__private/float_methods.inc +++ b/subspace/num/__private/float_methods.inc @@ -126,6 +126,13 @@ struct consts { /// #[doc.overloads=ctor.default] constexpr inline _self() noexcept = default; +/// Construction from floating point types where no bits are lost. +/// +/// #[doc.overloads=ctor.from_float] +template + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +constexpr inline _self(F v) noexcept : primitive_value(v.primitive_value) {} + /// Construction from primitive types where no bits are lost. /// /// #[doc.overloads=ctor.from_primitive] @@ -133,17 +140,42 @@ template requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) constexpr inline _self(P v) noexcept : primitive_value(v) {} -/// Constructs a ##_self## from an `Iterator` by computing the product of all -/// elements in the iterator. +/// Construction from floating point types where no bits are lost. /// -/// This method should rarely be called directly, as it is used to satisfy the -/// `sus::iter::Product` concept so that `Iterator::product()` can be called for -/// iterators over ##_self##. -static constexpr _self from_product( - ::sus::iter::Iterator<_self> auto&& it) noexcept { - auto p = _self(_primitive{1u}); - for (_self i : ::sus::move(it)) p *= i; - return p; +/// #[doc.overloads=from.float] +template + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure static constexpr _self from(F f) noexcept { + return _self(f); +} + +/// Construction from primitive floating point types where no bits are lost. +/// +/// #[doc.overloads=from.primitivefloat] +template + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure static constexpr _self from(F f) noexcept { + return _self(f); +} + +/// Assignment from floating point types where no bits are lost. +/// +/// #[doc.overloads=assign.from_float] +template + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +constexpr inline _self& operator=(F v) noexcept { + primitive_value = v.primitive_value; + return *this; +} + +/// Assignment from primitive types where no bits are lost. +/// +/// #[doc.overloads=assign.from_primitive] +template + requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) +constexpr inline _self& operator=(P v) noexcept { + primitive_value = v; + return *this; } /// Constructs a ##_self## from an `Iterator` by computing the sum of all @@ -159,20 +191,38 @@ static constexpr _self from_sum( return p; } -/// Assignment from primitive types where no bits are lost. -template - requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) -constexpr inline _self& operator=(P v) noexcept { - primitive_value = v; - return *this; +/// Constructs a ##_self## from an `Iterator` by computing the product of all +/// elements in the iterator. +/// +/// This method should rarely be called directly, as it is used to satisfy the +/// `sus::iter::Product` concept so that `Iterator::product()` can be called for +/// iterators over ##_self##. +static constexpr _self from_product( + ::sus::iter::Iterator<_self> auto&& it) noexcept { + auto p = _self(_primitive{1u}); + for (_self i : ::sus::move(it)) p *= i; + return p; } +/// Explicit conversion from the numeric type to the inner primitive type. +/// +/// Typically this should be used in a curly-brace conversion statement such as +/// `float{3_f32}` which will ensure a compiler error if the conversion can lose +/// data. Lossy conversions can be done through the `sus::convert::Transmogrify` +/// concept, such as `sus::mog(3_f32)`. template requires(::sus::mem::size_of() >= ::sus::mem::size_of<_primitive>()) sus_pure constexpr inline explicit operator U() const { return primitive_value; } +/// sus::ops::Eq<##_self##> trait. +/// #[doc.overloads=float.eq.self] +template +[[nodiscard]] sus_pure friend constexpr inline bool operator==( + _self l, _self r) noexcept { + return (l.primitive_value <=> r.primitive_value) == 0; +} /// sus::ops::Eq<##_self##, PrimitiveFloat> trait. /// #[doc.overloads=float.eq.prim] template @@ -180,15 +230,21 @@ template F r) noexcept { return (l.primitive_value <=> r) == 0; } - /// sus::ops::Eq<##_self##, Float> trait. -/// #[doc.overloads=float.eq] +/// #[doc.overloads=float.eq.float] template [[nodiscard]] sus_pure friend constexpr inline bool operator==(_self l, F r) noexcept { return (l.primitive_value <=> r.primitive_value) == 0; } +/// sus::ops::PartialOrd<##_self##, PrimitiveFloat> trait. +/// #[doc.overloads=float.ord.self] +template +[[nodiscard]] sus_pure friend constexpr inline auto operator<=>( + _self l, _self r) noexcept { + return l.primitive_value <=> r.primitive_value; +} /// sus::ops::PartialOrd<##_self##, PrimitiveFloat> trait. /// #[doc.overloads=float.ord.prim] template @@ -196,16 +252,15 @@ template F r) noexcept { return l.primitive_value <=> r; } - /// sus::ops::PartialOrd<##_self##, Float> trait. -/// #[doc.overloads=float.ord] +/// #[doc.overloads=float.ord.float] template [[nodiscard]] sus_pure friend constexpr inline auto operator<=>(_self l, F r) noexcept { return l.primitive_value <=> r.primitive_value; } -/// Return the ordering between self and other. +/// Return the ordering between `*this` and `other`. /// /// Unlike the standard partial comparison between floating point numbers, /// this comparison always produces an ordering in accordance to the @@ -232,8 +287,9 @@ template /// The interpretation of the signaling NaN bit follows the definition in the /// IEEE 754 standard, which may not match the interpretation by some of the /// older, non-conformant (e.g. MIPS) hardware implementations. -sus_pure constexpr std::strong_ordering total_cmp(_self rhs) const& noexcept { - return __private::float_strong_ordering(primitive_value, rhs.primitive_value); +sus_pure constexpr std::strong_ordering total_cmp(_self other) const& noexcept { + return __private::float_strong_ordering(primitive_value, + other.primitive_value); } /// sus::num::Neg<##_self##> trait. @@ -489,6 +545,20 @@ sus_pure inline _self mul_add(_self a, _self b) const& noexcept { return static_cast<_primitive>( ::fma(primitive_value, a.primitive_value, b.primitive_value)); } + +/// Returns the next representable value of the float type after `self` in the +/// direction of `toward`. If `self == toward`, `toward` is returned. If either +/// `self` or `toward` is NAN, NAN is returned. +/// +/// This is implemented by the +/// [cmath](https://en.cppreference.com/w/c/numeric/math/nextafter) library, see +/// the documentation for details on errors. +// +// TODO: constexpr in C++23. +sus_pure inline _self next_toward(_self toward) const& noexcept { + return __private::next_toward(primitive_value, toward.primitive_value); +} + /// Raises a number to a floating point power. sus_pure inline _self powf(_self n) const& noexcept { // MSVC pow(float) is returning a double for some reason. @@ -581,8 +651,35 @@ sus_pure constexpr inline I to_int_unchecked( /// Raw transmutation from `##_unsigned##`. /// -/// Note that this function is distinct from Into<##_self##>, which attempts to -/// preserve the numeric value, and not the bitwise value. +/// This is identical to [`std::bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast), or +/// `std::bit_cast`. It turns out this is incredibly portable, for two +/// reasons: +/// +/// * Floats and Ints have the same endianness on all modern platforms. +/// * IEEE 754 very precisely specifies the bit layout of floats. +/// +/// However there is one caveat: prior to the 2008 version of IEEE 754, how to +/// interpret the NaN signaling bit wasn’t actually specified. Most platforms +/// (notably x86 and ARM) picked the interpretation that was ultimately +/// standardized in 2008, but some didn’t (notably MIPS). As a result, all +/// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. +/// +/// Rather than trying to preserve signaling-ness cross-platform, this +/// implementation favors preserving the exact bits. This means that any +/// payloads encoded in NaNs will be preserved even if the result of this method +/// is sent over the network from an x86 machine to a MIPS one. +/// +/// If the results of this method are only manipulated by the same architecture +/// that produced them, then there is no portability concern. +/// +/// If the input isn’t NaN, then there is no portability concern. +/// +/// If you don’t care about signalingness (very likely), then there is no +/// portability concern. +/// +/// Note that this function is distinct from `Transmogrify` casting, which +/// attempts to preserve the *numeric* value, and not the bitwise value. /// /// # Examples /// ``` @@ -597,8 +694,15 @@ sus_pure static _self from_bits(_unsigned v) noexcept { } /// Raw transmutation to ##UnsignedT##. /// -/// Note that this function is distinct from Into<##_unsigned##>, which -/// attempts to preserve the numeric value, and not the bitwise value. +/// This is identical to [`std::bit_cast`](https://en.cppreference.com/w/cpp/numeric/bit_cast), or +/// `std::bit_cast`. +/// +/// See `from_bits()` for some discussion of the portability of this operation +/// (there are almost no issues). +/// +/// Note that this function is distinct from `Transmogrify` casting, which +/// attempts to preserve the *numeric* value, and not the bitwise value. sus_pure constexpr inline _unsigned to_bits() const& noexcept { return std::bit_cast(primitive_value); } diff --git a/subspace/num/__private/intrinsics.h b/subspace/num/__private/intrinsics.h index b6fbf9eb3..242e5029b 100644 --- a/subspace/num/__private/intrinsics.h +++ b/subspace/num/__private/intrinsics.h @@ -14,11 +14,12 @@ #pragma once -#include // TODO: Remove this and define nans and truncf ourselves. +#include #include #include #include +#include #include #if _MSC_VER @@ -26,8 +27,8 @@ #endif #include "subspace/assertions/unreachable.h" -#include "subspace/macros/inline.h" #include "subspace/macros/builtin.h" +#include "subspace/macros/inline.h" #include "subspace/macros/pure.h" #include "subspace/marker/unsafe.h" #include "subspace/mem/size_of.h" @@ -209,18 +210,18 @@ template requires(std::is_floating_point_v) sus_pure_const sus_always_inline constexpr T max_value() noexcept { if constexpr (::sus::mem::size_of() == ::sus::mem::size_of()) { - return 3.40282346639e+38f; + return 340282346638528859811704183484516925440.f; } else - return 1.7976931348623157E+308; + return 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0; } template requires(std::is_floating_point_v) sus_pure_const sus_always_inline constexpr T min_value() noexcept { if constexpr (::sus::mem::size_of() == ::sus::mem::size_of()) - return -3.40282346639e+38f; + return -340282346638528859811704183484516925440.f; else - return -1.7976931348623157E+308; + return -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0; } template @@ -618,7 +619,7 @@ sus_pure_const inline constexpr OverflowOut add_with_overflow(T x, }; } -template ()))> +template ()))> requires(std::is_integral_v && !std::is_signed_v && ::sus::mem::size_of() <= 8 && ::sus::mem::size_of() == ::sus::mem::size_of()) @@ -631,7 +632,7 @@ sus_pure_const inline constexpr OverflowOut add_with_overflow_signed( }; } -template ()))> +template ()))> requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8 && ::sus::mem::size_of() == ::sus::mem::size_of()) @@ -668,7 +669,7 @@ sus_pure_const inline constexpr OverflowOut sub_with_overflow(T x, }; } -template ()))> +template ()))> requires(std::is_integral_v && std::is_signed_v && ::sus::mem::size_of() <= 8 && ::sus::mem::size_of() == ::sus::mem::size_of()) @@ -1258,12 +1259,14 @@ sus_pure_const sus_always_inline constexpr int32_t exponent_bits( unchecked_shr(into_unsigned_integer(x) & mask, 52)); } -sus_pure_const sus_always_inline constexpr int32_t exponent_value( +/// This function requires that `x` is a normal value to produce a value result. +sus_pure_const sus_always_inline constexpr int32_t float_normal_exponent_value( float x) noexcept { return exponent_bits(x) - int32_t{127}; } -sus_pure_const sus_always_inline constexpr int32_t exponent_value( +/// This function requires that `x` is a normal value to produce a value result. +sus_pure_const sus_always_inline constexpr int32_t float_normal_exponent_value( double x) noexcept { return exponent_bits(x) - int32_t{1023}; } @@ -1399,8 +1402,10 @@ sus_pure_const inline constexpr T truncate_float(T x) noexcept { : uint32_t{52}; if (float_is_inf_or_nan(x) || float_is_zero(x)) return x; + if (float_nonzero_is_subnormal(x)) [[unlikely]] + return T{0}; - const int32_t exponent = exponent_value(x); + const int32_t exponent = float_normal_exponent_value(x); // If the exponent is greater than the most negative mantissa // exponent, then x is already an integer. @@ -1511,4 +1516,76 @@ sus_pure_const constexpr inline T float_clamp(marker::UnsafeFnMarker, T x, return x; } +// TODO: constexpr in C++23. +template + requires(std::is_floating_point_v && ::sus::mem::size_of() <= 8) +sus_pure_const inline T next_toward(T from, T to) { + if constexpr (::sus::mem::size_of() == 4u) + return std::nexttowardf(from, to); + else + return std::nexttoward(from, to); +} + +#pragma warning(push) +// MSVC claims that "overflow in constant arithmetic" occurs on the static_cast +// to float/double, but we check for overflow first, the conversion is in range. +#pragma warning(disable : 4756) + +template + requires(std::is_integral_v && std::is_floating_point_v && + (::sus::mem::size_of() <= 8) && + (::sus::mem::size_of() == 4 || ::sus::mem::size_of() == 8)) +sus_pure_const constexpr inline Out static_cast_int_to_float(T x) noexcept { + // C++20 Section 7.3.10: A prvalue of an integer type or of an unscoped + // enumeration type can be converted to a prvalue of a floatingpoint type. The + // result is exact if possible. If the value being converted is in the range + // of values that can be represented but the value cannot be represented + // exactly, it is an implementation-defined choice of either the next lower or + // higher representable value. [Note: Loss of precision occurs if the integral + // value cannot be represented exactly as a value of the floating-point type. + // — end note] If the value being converted is outside the range of values + // that can be represented, the behavior is undefined. + // + // SAFETY: The output is a floating point 32 or 64 bits. The input is an + // integer of size <= 64 bits. + // + // i64::MIN = -9223372036854775808. + // u64::MAX = 18446744073709551615. + // f32::MIN = -340282346638528859811704183484516925440. + // f32::MAX = 340282346638528859811704183484516925440. + // + // Thus the integers of the largest magnitude can be represented by f32, and + // since f64 is larger they can be represented there too. So no static_cast + // input here will cause UB. + return static_cast(x); +} + +template + requires(std::is_floating_point_v && std::is_floating_point_v && + ::sus::mem::size_of() == 8 && ::sus::mem::size_of() == 4) +sus_pure_const constexpr inline Out static_cast_to_smaller_float(T x) noexcept { + if (x <= T{max_value()} && x >= T{min_value()}) [[likely]] { + // C++20 Section 7.3.9: A prvalue of floating-point type can be converted to + // a prvalue of another floating-point type. If the source value can be + // exactly represented in the destination type, the result of the conversion + // is that exact representation. If the source value is between two adjacent + // destination values, the result of the conversion is an + // implementation-defined choice of either of those values. Otherwise, the + // behavior is undefined. + // + // SAFETY: Because the value `x` is at or between two valid values of type + // `Out`, the static_cast does not cause UB. + return static_cast(x); // Handles values in range. + } + if (x > T{max_value()}) { + return infinity(); // Handles large values and INFINITY. + } + if (x < T{min_value()}) { + return negative_infinity(); // Handles small values and NEG_INFINITY. + } + return nan(); // All that's left are NaNs. +} + +#pragma warning(pop) + } // namespace sus::num::__private diff --git a/subspace/num/__private/signed_integer_methods.inc b/subspace/num/__private/signed_integer_methods.inc index cead7f42b..d11fff135 100644 --- a/subspace/num/__private/signed_integer_methods.inc +++ b/subspace/num/__private/signed_integer_methods.inc @@ -52,6 +52,10 @@ static const u32 BITS; constexpr inline _self() noexcept = default; /** Construction from signed types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.signedint.typed] */ @@ -60,6 +64,10 @@ template constexpr inline _self(S v) noexcept : primitive_value(v.primitive_value) {} /** Construction from signed primitive types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.signedint.primitive] */ @@ -68,6 +76,10 @@ template constexpr inline _self(P v) noexcept : primitive_value(v) {} /** Construction from signed enum types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.signedenum] */ @@ -76,6 +88,10 @@ template constexpr inline _self(P v) noexcept : primitive_value(v) {} /** Construction from signed enum class types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.signedenumclass] */ @@ -85,6 +101,10 @@ explicit constexpr inline _self(P v) noexcept : primitive_value(static_cast<_primitive>(v)) {} /** Construction from unsigned types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.unsignedint.typed] */ @@ -93,6 +113,10 @@ template constexpr inline _self(U v) noexcept : primitive_value(v.primitive_value) {} /** Construction from unsigned primitive types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.unsignedint.primitive] */ @@ -101,6 +125,10 @@ template constexpr inline _self(P v) noexcept : primitive_value(v) {} /** Construction from unsigned enum types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.unsignedenum] */ @@ -109,6 +137,10 @@ template constexpr inline _self(P v) noexcept : primitive_value(v) {} /** Construction from unsigned enum class types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.ctor.unsignedenumclass] */ @@ -118,6 +150,10 @@ explicit constexpr inline _self(P v) noexcept : primitive_value(static_cast<_primitive>(v)) {} /** Assignment from signed types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.assign.signedint.typed] */ @@ -129,6 +165,10 @@ constexpr inline _self& operator=(S v) noexcept { } /** Assignment from signed primitive types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.assign.signedint.primitive] */ @@ -140,6 +180,10 @@ constexpr inline _self& operator=(P v) noexcept { } /** Assignment from signed enum types where no bits are lost. + * + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.assign.signedenum] */ @@ -150,53 +194,19 @@ constexpr inline _self& operator=(P v) noexcept { return *this; } -/** Assignment from unsigned types where no bits are lost. - * - * #[doc.overloads=signedint.assign.unsignedint.typed] - */ -template - requires(::sus::mem::size_of() < ::sus::mem::size_of<_primitive>()) -constexpr inline _self& operator=(U v) noexcept { - primitive_value = v.primitive_value; - return *this; -} - -/** Assignment from unsigned primitive types where no bits are lost. - * - * #[doc.overloads=signedint.assign.unsignedint.primitive] - */ -template - requires(::sus::mem::size_of

() < ::sus::mem::size_of<_primitive>()) -constexpr inline _self& operator=(P v) noexcept { - primitive_value = v; - return *this; -} - -/** Assignment from unsigned enum types where no bits are lost. +/** Constructs a ##_self## from a signed integer type (i8, i16, i32, etc) where + * no bits are lost. * - * #[doc.overloads=signedint.assign.unsignedenum.small] - */ -template - requires(::sus::mem::size_of

() < ::sus::mem::size_of<_primitive>()) -constexpr inline _self& operator=(P v) noexcept { - primitive_value = v; - return *this; -} - -/** Constructs a ##_self## from a signed integer type (i8, i16, i32, etc). - * - * # Panics - * The function will panic if the input value is out of range for ##_self##. + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * - * #[doc.overloads=0] + * #[doc.overloads=from.signed] */ template + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) sus_pure static constexpr _self from(S s) noexcept { - if constexpr (MIN_PRIMITIVE > S::MIN_PRIMITIVE) - ::sus::check(s.primitive_value >= MIN_PRIMITIVE); - if constexpr (MAX_PRIMITIVE < S::MAX_PRIMITIVE) - ::sus::check(s.primitive_value <= MAX_PRIMITIVE); - return _self(static_cast<_primitive>(s.primitive_value)); + return _self(s); } /** Try to construct a ##_self## from a signed integer type (i8, i16, i32, etc). @@ -223,22 +233,6 @@ try_from(S s) noexcept { return R::with(_self(static_cast<_primitive>(s.primitive_value))); } -/** Constructs a ##_self## from an unsigned integer type (u8, u16, u32, etc). - * - * # Panics - * The function will panic if the input value is out of range for ##_self##. - * - * #[doc.overloads=1] - */ -template -sus_pure static constexpr _self from(U u) noexcept { - constexpr auto umax = __private::into_unsigned(MAX_PRIMITIVE); - if constexpr (umax < U::MAX_PRIMITIVE) { - ::sus::check(u.primitive_value <= umax); - } - return _self(static_cast<_primitive>(u.primitive_value)); -} - /** Try to construct a ##_self## from an unsigned integer type (u8, u16, u32, * etc). * @@ -260,21 +254,19 @@ try_from(U u) noexcept { return R::with(_self(static_cast<_primitive>(u.primitive_value))); } -/** Constructs a ##_self## from a signed primitive integer type (int, long, - * etc). +/** Constructs a ##_self## from a signed primitive integer type (int, long, etc) + * where no bits are lost. * - * # Panics - * The function will panic if the input value is out of range for ##_self##. + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * - * #[doc.overloads=signedint.from.signedint] + * #[doc.overloads=from.signed.prim] */ template -sus_pure static constexpr _self from(S s) { - if constexpr (MIN_PRIMITIVE > __private::min_value()) - ::sus::check(s >= MIN_PRIMITIVE); - if constexpr (MAX_PRIMITIVE < __private::max_value()) - ::sus::check(s <= MAX_PRIMITIVE); - return _self(static_cast<_primitive>(s)); + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure static constexpr _self from(S s) noexcept { + return _self(s); } /** Tries to construct a ##_self## from a signed primitive integer type (int, @@ -287,7 +279,7 @@ sus_pure static constexpr _self from(S s) { template sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(S s) { +try_from(S s) noexcept { using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if constexpr (MIN_PRIMITIVE > __private::min_value()) { if (s < MIN_PRIMITIVE) { @@ -302,22 +294,21 @@ try_from(S s) { return R::with(_self(static_cast<_primitive>(s))); } -/** Constructs a ##_self## from a signed enum type (or enum class). +/** Constructs a ##_self## from a signed enum type (or enum class) where no bits + * are lost. * - * # Panics - * The function will panic if the input value is out of range for ##_self##. + * For conversions from types with a larger range use `try_from()`. For lossy + * conversions use the `sus::construct::Transmogrify` concept with + * `sus::mog<##_self##>()`. * * #[doc.overloads=signedint.from.signedenum] */ template - requires(SignedPrimitiveEnum || SignedPrimitiveEnumClass) -sus_pure static constexpr _self from(S s) { + requires((SignedPrimitiveEnum || SignedPrimitiveEnumClass) && + ::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure static constexpr _self from(S s) noexcept { using D = std::underlying_type_t; - if constexpr (MIN_PRIMITIVE > __private::min_value()) - ::sus::check(static_cast(s) >= MIN_PRIMITIVE); - if constexpr (MAX_PRIMITIVE < __private::max_value()) - ::sus::check(static_cast(s) <= MAX_PRIMITIVE); - return _self(static_cast<_primitive>(s)); + return _self(static_cast>(s)); } /** Tries to construct a ##_self## from a signed enum type (or enum class). @@ -330,7 +321,7 @@ template requires(SignedPrimitiveEnum || SignedPrimitiveEnumClass) sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(S s) { +try_from(S s) noexcept { using D = std::underlying_type_t; using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if constexpr (MIN_PRIMITIVE > __private::min_value()) { @@ -346,23 +337,6 @@ try_from(S s) { return R::with(_self(static_cast<_primitive>(s))); } -/** Constructs a ##_self## from an unsigned primitive integer type (unsigned - * int, unsigned long, etc). - * - * # Panics - * The function will panic if the input value is out of range for ##_self##. - * - * #[doc.overloads=signedint.from.unsignedint] - */ -template -sus_pure static constexpr _self from(U u) { - constexpr auto umax = __private::into_unsigned(MAX_PRIMITIVE); - if constexpr (umax < __private::max_value()) { - ::sus::check(u <= umax); - } - return _self(static_cast<_primitive>(u)); -} - /** Constructs a ##_self## from an unsigned primitive integer type (unsigned * int, unsigned long, etc). * @@ -373,7 +347,7 @@ sus_pure static constexpr _self from(U u) { template sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(U u) { +try_from(U u) noexcept { using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; constexpr auto umax = __private::into_unsigned(MAX_PRIMITIVE); if constexpr (umax < __private::max_value()) { @@ -384,24 +358,6 @@ try_from(U u) { return R::with(_self(static_cast<_primitive>(u))); } -/** Constructs a ##_self## from an unsigned enum type (or enum class). - * - * # Panics - * The function will panic if the input value is out of range for ##_self##. - * - * #[doc.overloads=signedint.from.unsignedenum] - */ -template - requires(UnsignedPrimitiveEnum || UnsignedPrimitiveEnumClass) -sus_pure static constexpr _self from(U u) { - using D = std::underlying_type_t; - constexpr auto umax = __private::into_unsigned(MAX_PRIMITIVE); - if constexpr (umax < __private::max_value()) { - ::sus::check(static_cast(u) <= umax); - } - return _self(static_cast<_primitive>(u)); -} - /** Constructs a ##_self## from an unsigned enum type (or enum class). * * Returns an error if the source value is outside of the range of ##_self##. @@ -412,7 +368,7 @@ template requires(UnsignedPrimitiveEnum || UnsignedPrimitiveEnumClass) sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(U u) { +try_from(U u) noexcept { using D = std::underlying_type_t; using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; constexpr auto umax = __private::into_unsigned(MAX_PRIMITIVE); @@ -424,51 +380,6 @@ try_from(U u) { return R::with(_self(static_cast<_primitive>(u))); } -/** Constructs a ##_self## from a signed or unsigned integer type (i8, i16, u32, - * u64 etc). - * - * # Safety - * If the input value is out of range for ##_self##, the value will be - * truncated, which may lead to application bugs and memory unsafety. - * - * #[doc.overloads=signedint.fromunchecked.int] - */ -template -sus_pure static constexpr _self from_unchecked(::sus::marker::UnsafeFnMarker, - I i) noexcept { - return _self(static_cast<_primitive>(i.primitive_value)); -} - -/** Constructs a ##_self## from a signed or unsigned integer primitive type - * (int, long, unsigned int, etc). - * - * # Safety - * If the input value is out of range for ##_self##, the value will be - * truncated, which may lead to application bugs and memory unsafety. - * - * #[doc.overloads=signedint.fromunchecked.primitive] - */ -template -sus_pure static constexpr _self from_unchecked(::sus::marker::UnsafeFnMarker, - P p) noexcept { - return _self(static_cast<_primitive>(p)); -} - -/** Constructs a ##_self## from a signed or unsigned enum type (or enum class). - * - * # Safety - * If the input value is out of range for ##_self##, the value will be - * truncated, which may lead to application bugs and memory unsafety. - * - * #[doc.overloads=signedint.fromunchecked.enum] - */ -template - requires(PrimitiveEnum

|| PrimitiveEnumClass

) -sus_pure static constexpr _self from_unchecked(::sus::marker::UnsafeFnMarker, - P p) noexcept { - return _self(static_cast<_primitive>(p)); -} - /// Constructs a ##_self## from an `Iterator` by computing the product of all /// elements in the itertor. /// @@ -507,18 +418,19 @@ static constexpr _self from_sum( return p; } +/// Explicit conversion from the numeric type to the inner primitive type. +/// +/// Typically this should be used in a curly-brace conversion statement such as +/// `int32_t{3_i32}` which will ensure a compiler error if the conversion can +/// lose data. Lossy conversions can be done through the `sus::convert::Transmogrify` +/// concept, such as `sus::mog(3_i32)` or +/// `sus::mog(3_i32)`. template requires(::sus::mem::size_of() >= ::sus::mem::size_of<_primitive>()) sus_pure constexpr inline explicit operator U() const { return primitive_value; } -template - requires(::sus::mem::size_of() >= ::sus::mem::size_of<_primitive>()) -sus_pure constexpr inline explicit operator U() const { - return static_cast(primitive_value); -} - /** Returns true if the current value is positive and false if the number is * zero or negative. */ @@ -549,14 +461,14 @@ sus_pure constexpr _self signum() const& noexcept { * #[doc.overloads=int.eq.self] */ [[nodiscard]] sus_pure friend constexpr inline bool operator==( const _self& l, const _self& r) noexcept = default; -/** sus::ops::Eq<##_self##, UnsignedPrimitiveInteger> trait. +/** sus::ops::Eq<##_self##, SignedPrimitiveInteger> trait. * #[doc.overloads=int.eq.signedprimitive] */ template [[nodiscard]] sus_pure friend constexpr inline bool operator==( const _self& l, const P& r) noexcept { return l.primitive_value == r; } -/** sus::ops::Eq<##_self##, Unsigned> trait. +/** sus::ops::Eq<##_self##, Signed> trait. * #[doc.overloads=int.eq.signed] */ template [[nodiscard]] sus_pure friend constexpr inline bool operator==( diff --git a/subspace/num/__private/unsigned_integer_methods.inc b/subspace/num/__private/unsigned_integer_methods.inc index 7effcb071..e362a8589 100644 --- a/subspace/num/__private/unsigned_integer_methods.inc +++ b/subspace/num/__private/unsigned_integer_methods.inc @@ -108,6 +108,10 @@ explicit constexpr inline _self(P v) noexcept : primitive_value(v) {} /// Construction from unsigned types where no bits are lost. /// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. +/// /// #[doc.overloads=ctor.unsigned.small.typed] template requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) @@ -115,6 +119,10 @@ constexpr inline _self(U v) noexcept : primitive_value(v.primitive_value) {} /// Construction from unsigned primitive types where no bits are lost. /// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. +/// /// #[doc.overloads=ctor.unsigned.small.primitive] template requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) @@ -122,6 +130,10 @@ constexpr inline _self(P v) noexcept : primitive_value(v) {} /// Construction from unsigned enum types where no bits are lost. /// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. +/// /// #[doc.overloads=ctor.unsigned.smallenum] template requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) @@ -129,6 +141,10 @@ constexpr inline _self(P v) noexcept : primitive_value(v) {} /// Construction from unsigned enum class types where no bits are lost. /// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. +/// /// #[doc.overloads=ctor.unsigned.smallenumclass] template requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) @@ -185,7 +201,11 @@ constexpr inline _self& operator=(P v) noexcept { #else -/// Assignment from unsigned types where no bits are lost. +/// Assignment from unsigned types (u8, u16, etc.) where no bits are lost. +/// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. /// /// #[doc.overloads=assign.unsigned.small.typed] template @@ -195,7 +215,12 @@ constexpr inline _self& operator=(U v) noexcept { return *this; } -/// Assignment from unsigned primitive types where no bits are lost. +/// Assignment from unsigned primitive types (unsigned int, unsigned long, etc.) +/// where no bits are lost. +/// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. /// /// #[doc.overloads=assign.unsigned.small.primitive] template @@ -207,6 +232,10 @@ constexpr inline _self& operator=(P v) noexcept { /// Assignment from unsigned enum types where no bits are lost. /// +/// For conversions from types with a larger range use `try_from()`. For lossy +/// conversions use the `sus::construct::Transmogrify` concept with +/// `sus::mog<##_self##>()`. +/// /// #[doc.overloads=assign.unsigned.smallenum] template requires(::sus::mem::size_of

() <= ::sus::mem::size_of<_primitive>()) @@ -239,7 +268,7 @@ _self with_addr(usize addr) const& noexcept { /// /// #[doc.overloads=uptr.from.pointer] template -sus_pure static _self from(U* u) { +sus_pure static _self from(U* u) noexcept { return _self(reinterpret_cast<_primitive>(u)); } @@ -251,7 +280,7 @@ sus_pure static _self from(U* u) { template sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(U* u); +try_from(U* u) noexcept; /// Constructs a _self from an unsigned integer type (u8, u16, u32, etc) with /// the same size as a pointer. @@ -283,7 +312,7 @@ try_from(U u) noexcept; /// #[doc.overloads=unsigned.from.unsignedprimitive] template requires(::sus::mem::size_of() == ::sus::mem::size_of<_primitive>()) -sus_pure static constexpr _self from(U u) { +sus_pure static constexpr _self from(U u) noexcept { if constexpr (MAX_PRIMITIVE < __private::max_value()) ::sus::check(u <= MAX_PRIMITIVE); return _self(static_cast<_primitive>(u)); @@ -299,20 +328,17 @@ template requires(::sus::mem::size_of() == ::sus::mem::size_of<_primitive>()) sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(U u); +try_from(U u) noexcept; #else -/// Constructs a _self from an unsigned integer type (u8, u16, u32, etc). -/// -/// # Panics -/// The function will panic if the input value is out of range for _self. +/// Constructs a _self from an unsigned integer type (u8, u16, u32, etc) where +/// no bits are lost. /// /// #[doc.overloads=unsigned.from.unsigned] template + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) sus_pure static constexpr _self from(U u) noexcept { - if constexpr (MAX_PRIMITIVE < U::MAX_PRIMITIVE) - ::sus::check(u.primitive_value <= MAX_PRIMITIVE); return _self(static_cast<_primitive>(u.primitive_value)); } @@ -328,16 +354,12 @@ sus_pure static constexpr ::sus::result::Result<_self, try_from(U u) noexcept; /// Constructs a _self from an unsigned primitive integer type (unsigned -/// int, unsigned long, etc). -/// -/// # Panics -/// The function will panic if the input value is out of range for _self. +/// int, unsigned long, etc) where no bits are lost. /// /// #[doc.overloads=unsigned.from.unsignedprimitive] template -sus_pure static constexpr _self from(U u) { - if constexpr (MAX_PRIMITIVE < __private::max_value()) - ::sus::check(u <= MAX_PRIMITIVE); + requires(::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure static constexpr _self from(U u) noexcept { return _self(static_cast<_primitive>(u)); } @@ -350,22 +372,7 @@ sus_pure static constexpr _self from(U u) { template sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(U u); - -/// Constructs a _self from a signed integer type (i8, i16, i32, etc). -/// -/// # Panics -/// The function will panic if the input value is out of range for _self. -/// -/// #[doc.overloads=unsigned.from.signed] -template -sus_pure static constexpr _self from(S s) noexcept { - ::sus::check(s.primitive_value >= decltype(S::primitive_value){0}); - constexpr auto umax = __private::into_unsigned(S::MAX_PRIMITIVE); - if constexpr (MAX_PRIMITIVE < umax) - ::sus::check(__private::into_unsigned(s.primitive_value) <= MAX_PRIMITIVE); - return _self(static_cast<_primitive>(s.primitive_value)); -} +try_from(U u) noexcept; /// Tries to construct a _self from a signed integer type (i8, i16, i32, /// etc). @@ -378,22 +385,6 @@ sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> try_from(S s) noexcept; -/// Constructs a _self from a signed primitive integer type (int, long, -/// etc). -/// -/// # Panics -/// The function will panic if the input value is out of range for _self. -/// -/// #[doc.overloads=unsigned.from.signedprimitive] -template -sus_pure static constexpr _self from(S s) { - ::sus::check(s >= 0); - constexpr auto umax = __private::into_unsigned(__private::max_value()); - if constexpr (MAX_PRIMITIVE < umax) - ::sus::check(__private::into_unsigned(s) <= MAX_PRIMITIVE); - return _self(static_cast<_primitive>(s)); -} - /// Tries to construct a _self from a signed primitive integer type (int, /// long, etc). /// @@ -403,25 +394,7 @@ sus_pure static constexpr _self from(S s) { template sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(S s); - -/// Constructs a _self from a signed enum type (or enum class). -/// -/// # Panics -/// The function will panic if the input value is out of range for _self. -/// -/// #[doc.overloads=unsigned.from.signedenum] -template - requires(SignedPrimitiveEnum || SignedPrimitiveEnumClass) -sus_pure static constexpr _self from(S s) { - using D = std::underlying_type_t; - ::sus::check(static_cast(s) >= 0); - constexpr auto umax = __private::into_unsigned(__private::max_value()); - if constexpr (MAX_PRIMITIVE < umax) { - ::sus::check(__private::into_unsigned(static_cast(s)) <= MAX_PRIMITIVE); - } - return _self(static_cast<_primitive>(s)); -} +try_from(S s) noexcept; /// Tries to construct a _self from a signed enum type (or enum class). /// @@ -432,21 +405,17 @@ template requires(SignedPrimitiveEnum || SignedPrimitiveEnumClass) sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(S s); +try_from(S s) noexcept; -/// Constructs a _self from an unsigned enum type (or enum class). -/// -/// # Panics -/// The function will panic if the input value is out of range for _self. +/// Constructs a _self from an unsigned enum type (or enum class) where no bits +/// are lost. /// /// #[doc.overloads=unsigned.from.unsignedenum] template - requires(UnsignedPrimitiveEnum || UnsignedPrimitiveEnumClass) -sus_pure static constexpr _self from(U u) { - using D = std::underlying_type_t; - if constexpr (MAX_PRIMITIVE < __private::max_value()) - ::sus::check(static_cast(u) <= MAX_PRIMITIVE); - return _self(static_cast<_primitive>(u)); + requires((UnsignedPrimitiveEnum || UnsignedPrimitiveEnumClass) && + ::sus::mem::size_of() <= ::sus::mem::size_of<_primitive>()) +sus_pure static constexpr _self from(U u) noexcept { + return _self(static_cast>(u)); } /// Tries to construct a _self from an unsigned enum type (or enum class). @@ -458,50 +427,7 @@ template requires(UnsignedPrimitiveEnum || UnsignedPrimitiveEnumClass) sus_pure static constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -try_from(U u); - -/// Constructs a _self from a signed or unsigned integer type (i8, i16, u32, -/// u64 etc). -/// -/// # Safety -/// If the input value is out of range for _self, the value will be -/// truncated, which may lead to application bugs and memory unsafety. -/// -/// #[doc.overloads=unsigned.from.unchecked.int] -template -sus_pure static constexpr _self from_unchecked(::sus::marker::UnsafeFnMarker, - I i) noexcept { - return _self(static_cast<_primitive>(i.primitive_value)); -} - -/// Constructs a _self from a signed or unsigned integer primitive type -/// (int, long, unsigned int, etc). -/// -/// # Safety -/// If the input value is out of range for _self, the value will be -/// truncated, which may lead to application bugs and memory unsafety. -/// -/// #[doc.overloads=unsigned.from.unchecked.primitive] -template -sus_pure static constexpr _self from_unchecked(::sus::marker::UnsafeFnMarker, - P p) noexcept { - return _self(static_cast<_primitive>(p)); -} - -/// Constructs a _self from a signed or unsigned enum type -/// (or enum class). -/// -/// # Safety -/// If the input value is out of range for _self, the value will be -/// truncated, which may lead to application bugs and memory unsafety. -/// -/// #[doc.overloads=unsigned.from.unchecked.enum] -template - requires(PrimitiveEnum

|| PrimitiveEnumClass

) -sus_pure static constexpr _self from_unchecked(::sus::marker::UnsafeFnMarker, - P p) noexcept { - return _self(static_cast<_primitive>(p)); -} +try_from(U u) noexcept; #endif @@ -552,16 +478,18 @@ sus_pure constexpr inline explicit operator U() const { #else +/// Explicit conversion from the numeric type to the inner primitive type. +/// +/// Typically this should be used in a curly-brace conversion statement such as +/// `uint32_t{3_i32}` which will ensure a compiler error if the conversion can +/// lose data. Lossy conversions can be done through the `sus::convert::Transmogrify` +/// concept, such as `sus::mog(3_i32)` or +/// `sus::mog(3_i32)`. template requires(::sus::mem::size_of() >= ::sus::mem::size_of<_primitive>()) sus_pure constexpr inline explicit operator U() const { return primitive_value; } -template - requires(::sus::mem::size_of() > ::sus::mem::size_of<_primitive>()) -sus_pure constexpr inline explicit operator U() const { - return primitive_value; -} #endif diff --git a/subspace/num/__private/unsigned_integer_methods_impl.inc b/subspace/num/__private/unsigned_integer_methods_impl.inc index 00a337258..f466f5c0b 100644 --- a/subspace/num/__private/unsigned_integer_methods_impl.inc +++ b/subspace/num/__private/unsigned_integer_methods_impl.inc @@ -33,7 +33,7 @@ namespace sus::num { template sus_pure constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -_self::try_from(U* u) { +_self::try_from(U* u) noexcept { using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; return R::with(_self(reinterpret_cast<_primitive>(u))); } @@ -54,7 +54,7 @@ _self::try_from(U u) noexcept { template requires(::sus::mem::size_of() == ::sus::mem::size_of<_primitive>()) sus_pure constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -_self::try_from(U u) { +_self::try_from(U u) noexcept { using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if constexpr (MAX_PRIMITIVE < __private::max_value()) { if (u > MAX_PRIMITIVE) { @@ -80,7 +80,7 @@ _self::try_from(U u) noexcept { template sus_pure constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -_self::try_from(U u) { +_self::try_from(U u) noexcept { using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if constexpr (MAX_PRIMITIVE < __private::max_value()) { if (u > MAX_PRIMITIVE) { @@ -108,7 +108,7 @@ _self::try_from(S s) noexcept { template sus_pure constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -_self::try_from(S s) { +_self::try_from(S s) noexcept { using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if (s < 0) { return R::with_err(::sus::num::TryFromIntError::with_out_of_bounds()); @@ -125,7 +125,7 @@ _self::try_from(S s) { template requires(SignedPrimitiveEnum || SignedPrimitiveEnumClass) sus_pure constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -_self::try_from(S s) { +_self::try_from(S s) noexcept { using D = std::underlying_type_t; using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if (static_cast(s) < 0) { @@ -143,7 +143,7 @@ _self::try_from(S s) { template requires(UnsignedPrimitiveEnum || UnsignedPrimitiveEnumClass) sus_pure constexpr ::sus::result::Result<_self, ::sus::num::TryFromIntError> -_self::try_from(U u) { +_self::try_from(U u) noexcept { using D = std::underlying_type_t; using R = ::sus::result::Result<_self, ::sus::num::TryFromIntError>; if constexpr (MAX_PRIMITIVE < __private::max_value()) { diff --git a/subspace/num/f32_unittest.cc b/subspace/num/f32_unittest.cc index d342a0969..f2190f410 100644 --- a/subspace/num/f32_unittest.cc +++ b/subspace/num/f32_unittest.cc @@ -1106,4 +1106,15 @@ TEST(f32, fmt) { EXPECT_EQ(fmt::format("{:.4f}", 1234.567_f32), "1234.5670"); } +TEST(f32, NextToward) { + EXPECT_EQ((0_f32).next_toward(f32::NAN).is_nan(), true); + EXPECT_EQ(f32::NAN.next_toward(0_f32).is_nan(), true); + EXPECT_EQ((1_f32).next_toward(f32::INFINITY) - 1_f32, + f32(std::numeric_limits::epsilon())); + EXPECT_GT((0_f32).next_toward(f32::INFINITY), 0_f32); + EXPECT_GT((0_f32).next_toward(1_f32), 0_f32); + EXPECT_LT((0_f32).next_toward(f32::NEG_INFINITY), 0_f32); + EXPECT_LT((0_f32).next_toward(-1_f32), 0_f32); +} + } // namespace diff --git a/subspace/num/f64_unittest.cc b/subspace/num/f64_unittest.cc index 6d8e5b57a..a02b678ad 100644 --- a/subspace/num/f64_unittest.cc +++ b/subspace/num/f64_unittest.cc @@ -1063,13 +1063,13 @@ TEST(f64, FromLeBytes) { TEST(f64, FromNeBytes) { if constexpr (sus::assertions::is_big_endian()) { auto value = f64::from_ne_bytes( - sus::Array::with(0x40_u8, 0x29_u8, 0x00_u8, 0x00_u8, - 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8)); + sus::Array::with(0x40_u8, 0x29_u8, 0x00_u8, 0x00_u8, 0x00_u8, + 0x00_u8, 0x00_u8, 0x00_u8)); EXPECT_EQ(value, 12.5_f64); } else { auto value = f64::from_ne_bytes( - sus::Array::with(0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, - 0x00_u8, 0x00_u8, 0x29_u8, 0x40_u8)); + sus::Array::with(0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, 0x00_u8, + 0x00_u8, 0x29_u8, 0x40_u8)); EXPECT_EQ(value, 12.5_f64); } } @@ -1082,4 +1082,15 @@ TEST(f64, fmt) { EXPECT_EQ(fmt::format("{:.4f}", 1234890.567_f64), "1234890.5670"); } +TEST(f64, NextToward) { + EXPECT_EQ((0_f64).next_toward(f64::NAN).is_nan(), true); + EXPECT_EQ(f64::NAN.next_toward(0_f64).is_nan(), true); + EXPECT_EQ((1_f64).next_toward(f64::INFINITY) - 1_f64, + f64(std::numeric_limits::epsilon())); + EXPECT_GT((0_f64).next_toward(f64::INFINITY), 0_f64); + EXPECT_GT((0_f64).next_toward(1_f64), 0_f64); + EXPECT_LT((0_f64).next_toward(f64::NEG_INFINITY), 0_f64); + EXPECT_LT((0_f64).next_toward(-1_f64), 0_f64); +} + } // namespace diff --git a/subspace/num/float_concepts.h b/subspace/num/float_concepts.h index 9b557daed..35bb0ecd0 100644 --- a/subspace/num/float_concepts.h +++ b/subspace/num/float_concepts.h @@ -22,7 +22,7 @@ namespace sus::num { template concept Float = - std::same_as> || std::same_as>; + std::same_as || std::same_as; template concept PrimitiveFloat = std::same_as || std::same_as || diff --git a/subspace/num/i16_unittest.cc b/subspace/num/i16_unittest.cc index 9be621c7f..7d532dc81 100644 --- a/subspace/num/i16_unittest.cc +++ b/subspace/num/i16_unittest.cc @@ -310,24 +310,27 @@ TEST(i16, ToPrimitive) { static_assert(IsExplicitlyConvertible); static_assert(IsExplicitlyConvertible); static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); } TEST(i16, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using signed_char = signed char; + + static_assert(!sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -338,17 +341,17 @@ TEST(i16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -359,17 +362,17 @@ TEST(i16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -380,18 +383,11 @@ TEST(i16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(i16::from(char{2}), 2_i16); - EXPECT_EQ(i16::from(size_t{2}), 2_i16); + EXPECT_EQ(i16::from(signed_char{2}), 2_i16); EXPECT_EQ(i16::from(int8_t{2}), 2_i16); EXPECT_EQ(i16::from(int16_t{2}), 2_i16); - EXPECT_EQ(i16::from(int32_t{2}), 2_i16); - EXPECT_EQ(i16::from(int64_t{2}), 2_i16); - EXPECT_EQ(i16::from(uint8_t{2}), 2_i16); - EXPECT_EQ(i16::from(uint16_t{2}), 2_i16); - EXPECT_EQ(i16::from(uint32_t{2}), 2_i16); - EXPECT_EQ(i16::from(uint64_t{2}), 2_i16); - - EXPECT_EQ(i16::try_from(char{2}).unwrap(), 2_i16); + + EXPECT_EQ(i16::try_from(signed_char{2}).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(size_t{2}).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(int8_t{2}).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(int16_t{2}).unwrap(), 2_i16); @@ -407,19 +403,11 @@ TEST(i16, From) { EXPECT_TRUE(i16::try_from(uint16_t{u16::MAX}).is_err()); EXPECT_TRUE(i16::try_from(uint32_t{u32::MAX}).is_err()); - EXPECT_EQ(i16::from(ENUM(, char)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, size_t)::Z), 2_i16); + EXPECT_EQ(i16::from(ENUM(, signed_char)::Z), 2_i16); EXPECT_EQ(i16::from(ENUM(, int8_t)::Z), 2_i16); EXPECT_EQ(i16::from(ENUM(, int16_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, int32_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, int64_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, uint8_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, uint16_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, uint32_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(, uint64_t)::Z), 2_i16); - EXPECT_EQ(i16::from(ENUM(class, uint64_t)::Z), 2_i16); - - EXPECT_EQ(i16::try_from(ENUM(, char)::Z).unwrap(), 2_i16); + + EXPECT_EQ(i16::try_from(ENUM(, signed_char)::Z).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(ENUM(, size_t)::Z).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(ENUM(, int8_t)::Z).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(ENUM(, int16_t)::Z).unwrap(), 2_i16); @@ -437,38 +425,17 @@ TEST(i16, From) { EXPECT_TRUE(i16::try_from(ENUM(, uint32_t)::MAX).is_err()); EXPECT_TRUE(i16::try_from(ENUM(class, uint32_t)::MAX).is_err()); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, char{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, size_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, int8_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, int16_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, int32_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, int64_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, uint8_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, uint16_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, uint32_t{2}), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, uint64_t{2}), 2_i16); - - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_i16); - static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -479,17 +446,10 @@ TEST(i16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); EXPECT_EQ(i16::from(2_i8), 2_i16); EXPECT_EQ(i16::from(2_i16), 2_i16); - EXPECT_EQ(i16::from(2_i32), 2_i16); - EXPECT_EQ(i16::from(2_i64), 2_i16); - EXPECT_EQ(i16::from(2_isize), 2_i16); - EXPECT_EQ(i16::from(2_u8), 2_i16); - EXPECT_EQ(i16::from(2_u16), 2_i16); - EXPECT_EQ(i16::from(2_u32), 2_i16); - EXPECT_EQ(i16::from(2_u64), 2_i16); - EXPECT_EQ(i16::from(2_usize), 2_i16); EXPECT_EQ(i16::try_from(2_i8).unwrap(), 2_i16); EXPECT_EQ(i16::try_from(2_i16).unwrap(), 2_i16); @@ -506,108 +466,6 @@ TEST(i16, From) { EXPECT_TRUE(i16::try_from(i32::MAX).is_err()); EXPECT_TRUE(i16::try_from(u16::MAX).is_err()); EXPECT_TRUE(i16::try_from(u32::MAX).is_err()); - - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_i8), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_i16), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_i32), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_i64), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_isize), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_u8), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_u16), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_u32), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_u64), 2_i16); - EXPECT_EQ(i16::from_unchecked(unsafe_fn, 2_usize), 2_i16); -} - -TEST(i16DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = i16::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i16::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i16::from(i32::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(i64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(u16::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(u32::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(u64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i16::from(usize::MAX); - ensure_use(&x); - }, - ""); -#endif } TEST(i16, InvokeEverything) { diff --git a/subspace/num/i32_unittest.cc b/subspace/num/i32_unittest.cc index 8b3e47081..e8d6718d6 100644 --- a/subspace/num/i32_unittest.cc +++ b/subspace/num/i32_unittest.cc @@ -323,23 +323,26 @@ TEST(i32, ToPrimitive) { static_assert(IsExplicitlyConvertible); static_assert(NotConvertible); static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); } TEST(i32, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using signed_char = signed char; + + static_assert(!sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -350,17 +353,17 @@ TEST(i32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -371,17 +374,17 @@ TEST(i32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -392,18 +395,12 @@ TEST(i32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(i32::from(char{2}), 2_i32); - EXPECT_EQ(i32::from(size_t{2}), 2_i32); + EXPECT_EQ(i32::from(signed_char{2}), 2_i32); EXPECT_EQ(i32::from(int8_t{2}), 2_i32); EXPECT_EQ(i32::from(int16_t{2}), 2_i32); EXPECT_EQ(i32::from(int32_t{2}), 2_i32); - EXPECT_EQ(i32::from(int64_t{2}), 2_i32); - EXPECT_EQ(i32::from(uint8_t{2}), 2_i32); - EXPECT_EQ(i32::from(uint16_t{2}), 2_i32); - EXPECT_EQ(i32::from(uint32_t{2}), 2_i32); - EXPECT_EQ(i32::from(uint64_t{2}), 2_i32); - EXPECT_EQ(i32::try_from(char{2}).unwrap(), 2_i32); + EXPECT_EQ(i32::try_from(signed_char{2}).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(size_t{2}).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(int8_t{2}).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(int16_t{2}).unwrap(), 2_i32); @@ -419,19 +416,12 @@ TEST(i32, From) { EXPECT_TRUE(i32::try_from(uint32_t{u32::MAX}).is_err()); EXPECT_TRUE(i32::try_from(uint64_t{u64::MAX}).is_err()); - EXPECT_EQ(i32::from(ENUM(, char)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(, size_t)::Z), 2_i32); + EXPECT_EQ(i32::from(ENUM(, signed_char)::Z), 2_i32); EXPECT_EQ(i32::from(ENUM(, int8_t)::Z), 2_i32); EXPECT_EQ(i32::from(ENUM(, int16_t)::Z), 2_i32); EXPECT_EQ(i32::from(ENUM(, int32_t)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(, int64_t)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(, uint8_t)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(, uint16_t)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(, uint32_t)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(, uint64_t)::Z), 2_i32); - EXPECT_EQ(i32::from(ENUM(class, uint64_t)::Z), 2_i32); - - EXPECT_EQ(i32::try_from(ENUM(, char)::Z).unwrap(), 2_i32); + + EXPECT_EQ(i32::try_from(ENUM(, signed_char)::Z).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(ENUM(, size_t)::Z).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(ENUM(, int8_t)::Z).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(ENUM(, int16_t)::Z).unwrap(), 2_i32); @@ -449,39 +439,17 @@ TEST(i32, From) { EXPECT_TRUE(i32::try_from(ENUM(, uint64_t)::MAX).is_err()); EXPECT_TRUE(i32::try_from(ENUM(class, uint64_t)::MAX).is_err()); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, char{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, size_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, int8_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, int16_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, int32_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, int64_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, uint8_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, uint16_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, uint32_t{2}), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, uint64_t{2}), 2_i32); - - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_i32); - static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -492,17 +460,11 @@ TEST(i32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); EXPECT_EQ(i32::from(2_i8), 2_i32); EXPECT_EQ(i32::from(2_i16), 2_i32); EXPECT_EQ(i32::from(2_i32), 2_i32); - EXPECT_EQ(i32::from(2_i64), 2_i32); - EXPECT_EQ(i32::from(2_isize), 2_i32); - EXPECT_EQ(i32::from(2_u8), 2_i32); - EXPECT_EQ(i32::from(2_u16), 2_i32); - EXPECT_EQ(i32::from(2_u32), 2_i32); - EXPECT_EQ(i32::from(2_u64), 2_i32); - EXPECT_EQ(i32::from(2_usize), 2_i32); EXPECT_EQ(i32::try_from(2_i8).unwrap(), 2_i32); EXPECT_EQ(i32::try_from(2_i16).unwrap(), 2_i32); @@ -519,96 +481,6 @@ TEST(i32, From) { EXPECT_TRUE(i32::try_from(i64::MIN).is_err()); EXPECT_TRUE(i32::try_from(u32::MAX).is_err()); EXPECT_TRUE(i32::try_from(u64::MAX).is_err()); - - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_i8), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_i16), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_i32), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_i64), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_isize), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_u8), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_u16), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_u32), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_u64), 2_i32); - EXPECT_EQ(i32::from_unchecked(unsafe_fn, 2_usize), 2_i32); -} - -TEST(i32DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = i32::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i32::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i32::from(i64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(u32::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(u64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i32::from(usize::MAX); - ensure_use(&x); - }, - ""); -#endif } TEST(i32, TryFromBoundaries) { @@ -625,12 +497,12 @@ TEST(i32, TryFromBoundaries) { EXPECT_TRUE(i32::try_from(i64::from(i32::MAX) + 1).is_err()); // Unsigned primitives. - EXPECT_TRUE(i32::try_from(uint32_t{u32::from(i32::MAX)}).is_ok()); - EXPECT_TRUE(i32::try_from(uint32_t{u32::from(i32::MAX) + 1u}).is_err()); + EXPECT_TRUE(i32::try_from(uint32_t{u32::try_from(i32::MAX).unwrap()}).is_ok()); + EXPECT_TRUE(i32::try_from(uint32_t{u32::try_from(i32::MAX).unwrap() + 1u}).is_err()); // Unsigned integers. - EXPECT_TRUE(i32::try_from(u32::from(i32::MAX)).is_ok()); - EXPECT_TRUE(i32::try_from(u32::from(i32::MAX) + 1u).is_err()); + EXPECT_TRUE(i32::try_from(u32::try_from(i32::MAX).unwrap()).is_ok()); + EXPECT_TRUE(i32::try_from(u32::try_from(i32::MAX).unwrap() + 1u).is_err()); } // ** Signed only @@ -2303,14 +2175,14 @@ TEST(i32, ReverseBits) { constexpr auto a4 = (-1_i32).reverse_bits(); EXPECT_EQ(a4, i32(-1)); constexpr auto a5 = i32(1).reverse_bits(); - EXPECT_EQ(a5, 1_i32 << (sus::into(sizeof(i32)) * 8_u32 - 1_u32)); + EXPECT_EQ(a5, 1_i32 << (sus::mog(sizeof(i32)) * 8_u32 - 1_u32)); EXPECT_EQ((0_i32).reverse_bits(), 0_i32); EXPECT_EQ((2_i32).reverse_bits(), 1_i32 << 30_u32); EXPECT_EQ((0xf8f800_i32).reverse_bits(), 0x1f1f00_i32); EXPECT_EQ((i32(-1)).reverse_bits(), -1_i32); EXPECT_EQ((i32(1)).reverse_bits().primitive_value, - 1_i32 << (sus::into(sizeof(i32)) * 8_u32 - 1_u32)); + 1_i32 << (sus::mog(sizeof(i32)) * 8_u32 - 1_u32)); } TEST(i32, RotateLeft) { diff --git a/subspace/num/i64_unittest.cc b/subspace/num/i64_unittest.cc index 373180639..79e70bf41 100644 --- a/subspace/num/i64_unittest.cc +++ b/subspace/num/i64_unittest.cc @@ -312,22 +312,25 @@ TEST(i64, ToPrimitive) { static_assert(NotConvertible); static_assert(NotConvertible); static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); } TEST(i64, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using signed_char = signed char; + + static_assert(!sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -338,17 +341,17 @@ TEST(i64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -359,17 +362,17 @@ TEST(i64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -380,18 +383,13 @@ TEST(i64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(i64::from(char{2}), 2_i64); - EXPECT_EQ(i64::from(size_t{2}), 2_i64); + EXPECT_EQ(i64::from(signed_char{2}), 2_i64); EXPECT_EQ(i64::from(int8_t{2}), 2_i64); EXPECT_EQ(i64::from(int16_t{2}), 2_i64); EXPECT_EQ(i64::from(int32_t{2}), 2_i64); EXPECT_EQ(i64::from(int64_t{2}), 2_i64); - EXPECT_EQ(i64::from(uint8_t{2}), 2_i64); - EXPECT_EQ(i64::from(uint16_t{2}), 2_i64); - EXPECT_EQ(i64::from(uint32_t{2}), 2_i64); - EXPECT_EQ(i64::from(uint64_t{2}), 2_i64); - EXPECT_EQ(i64::try_from(char{2}).unwrap(), 2_i64); + EXPECT_EQ(i64::try_from(signed_char{2}).unwrap(), 2_i64); EXPECT_EQ(i64::try_from(size_t{2}).unwrap(), 2_i64); EXPECT_EQ(i64::try_from(int8_t{2}).unwrap(), 2_i64); EXPECT_EQ(i64::try_from(int16_t{2}).unwrap(), 2_i64); @@ -404,19 +402,13 @@ TEST(i64, From) { EXPECT_TRUE(i64::try_from(uint64_t{u64::MAX}).is_err()); - EXPECT_EQ(i64::from(ENUM(, char)::Z), 2_i64); - EXPECT_EQ(i64::from(ENUM(, size_t)::Z), 2_i64); + EXPECT_EQ(i64::from(ENUM(, signed_char)::Z), 2_i64); EXPECT_EQ(i64::from(ENUM(, int8_t)::Z), 2_i64); EXPECT_EQ(i64::from(ENUM(, int16_t)::Z), 2_i64); EXPECT_EQ(i64::from(ENUM(, int32_t)::Z), 2_i64); EXPECT_EQ(i64::from(ENUM(, int64_t)::Z), 2_i64); - EXPECT_EQ(i64::from(ENUM(, uint8_t)::Z), 2_i64); - EXPECT_EQ(i64::from(ENUM(, uint16_t)::Z), 2_i64); - EXPECT_EQ(i64::from(ENUM(, uint32_t)::Z), 2_i64); - EXPECT_EQ(i64::from(ENUM(, uint64_t)::Z), 2_i64); - EXPECT_EQ(i64::from(ENUM(class, uint64_t)::Z), 2_i64); - EXPECT_EQ(i64::try_from(ENUM(, char)::Z).unwrap(), 2_i64); + EXPECT_EQ(i64::try_from(ENUM(, signed_char)::Z).unwrap(), 2_i64); EXPECT_EQ(i64::try_from(ENUM(, size_t)::Z).unwrap(), 2_i64); EXPECT_EQ(i64::try_from(ENUM(, int8_t)::Z).unwrap(), 2_i64); EXPECT_EQ(i64::try_from(ENUM(, int16_t)::Z).unwrap(), 2_i64); @@ -431,39 +423,17 @@ TEST(i64, From) { EXPECT_TRUE(i64::try_from(ENUM(, uint64_t)::MAX).is_err()); EXPECT_TRUE(i64::try_from(ENUM(class, uint64_t)::MAX).is_err()); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, char{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, size_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, int8_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, int16_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, int32_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, int64_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, uint8_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, uint16_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, uint32_t{2}), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, uint64_t{2}), 2_i64); - - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_i64); - static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -474,17 +444,13 @@ TEST(i64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); EXPECT_EQ(i64::from(2_i8), 2_i64); EXPECT_EQ(i64::from(2_i16), 2_i64); EXPECT_EQ(i64::from(2_i32), 2_i64); EXPECT_EQ(i64::from(2_i64), 2_i64); EXPECT_EQ(i64::from(2_isize), 2_i64); - EXPECT_EQ(i64::from(2_u8), 2_i64); - EXPECT_EQ(i64::from(2_u16), 2_i64); - EXPECT_EQ(i64::from(2_u32), 2_i64); - EXPECT_EQ(i64::from(2_u64), 2_i64); - EXPECT_EQ(i64::from(2_usize), 2_i64); EXPECT_EQ(i64::try_from(2_i8).unwrap(), 2_i16); EXPECT_EQ(i64::try_from(2_i16).unwrap(), 2_i16); @@ -498,54 +464,6 @@ TEST(i64, From) { EXPECT_EQ(i64::try_from(2_usize).unwrap(), 2_i16); EXPECT_TRUE(i64::try_from(u64::MAX).is_err()); - - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_i8), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_i16), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_i32), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_i64), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_isize), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_u8), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_u16), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_u32), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_u64), 2_i64); - EXPECT_EQ(i64::from_unchecked(unsafe_fn, 2_usize), 2_i64); -} - -TEST(i64DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = i64::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i64::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i64::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i64::from(u64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i64::from(usize::MAX); - ensure_use(&x); - }, - ""); -#endif } TEST(i64, CheckedMul) { diff --git a/subspace/num/i8_unittest.cc b/subspace/num/i8_unittest.cc index fc7072973..ae5ebbe27 100644 --- a/subspace/num/i8_unittest.cc +++ b/subspace/num/i8_unittest.cc @@ -186,16 +186,16 @@ TEST(i8, Constants) { } template -concept IsImplicitlyConvertible = (std::is_convertible_v && - std::is_assignable_v); +concept IsImplicitlyConvertible = + (std::is_convertible_v && std::is_assignable_v); template -concept IsExplicitlyConvertible = (std::constructible_from && - !std::is_convertible_v && - !std::is_assignable_v); +concept IsExplicitlyConvertible = + (std::constructible_from && !std::is_convertible_v && + !std::is_assignable_v); template -concept NotConvertible = (!std::constructible_from && - !std::is_convertible_v && - !std::is_assignable_v); +concept NotConvertible = + (!std::constructible_from && !std::is_convertible_v && + !std::is_assignable_v); template auto make_enum() { @@ -307,25 +307,29 @@ TEST(i8, ToPrimitive) { static_assert(IsExplicitlyConvertible); static_assert(IsExplicitlyConvertible); static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); } TEST(i8, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using signed_char = signed char; + + static_assert(!sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -336,17 +340,17 @@ TEST(i8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -357,17 +361,17 @@ TEST(i8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -378,18 +382,10 @@ TEST(i8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(i8::from(char{2}), 2_i8); - EXPECT_EQ(i8::from(size_t{2}), 2_i8); + EXPECT_EQ(i8::from(signed_char{2}), 2_i8); EXPECT_EQ(i8::from(int8_t{2}), 2_i8); - EXPECT_EQ(i8::from(int16_t{2}), 2_i8); - EXPECT_EQ(i8::from(int32_t{2}), 2_i8); - EXPECT_EQ(i8::from(int64_t{2}), 2_i8); - EXPECT_EQ(i8::from(uint8_t{2}), 2_i8); - EXPECT_EQ(i8::from(uint16_t{2}), 2_i8); - EXPECT_EQ(i8::from(uint32_t{2}), 2_i8); - EXPECT_EQ(i8::from(uint64_t{2}), 2_i8); - - EXPECT_EQ(i8::try_from(char{2}).unwrap(), 2_i8); + + EXPECT_EQ(i8::try_from(signed_char{2}).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(size_t{2}).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(int8_t{2}).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(int16_t{2}).unwrap(), 2_i8); @@ -405,19 +401,10 @@ TEST(i8, From) { EXPECT_TRUE(i8::try_from(uint8_t{u8::MAX}).is_err()); EXPECT_TRUE(i8::try_from(uint16_t{u16::MAX}).is_err()); - EXPECT_EQ(i8::from(ENUM(, char)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, size_t)::Z), 2_i8); + EXPECT_EQ(i8::from(ENUM(,signed char)::Z), 2_i8); EXPECT_EQ(i8::from(ENUM(, int8_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, int16_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, int32_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, int64_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, uint8_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, uint16_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, uint32_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(, uint64_t)::Z), 2_i8); - EXPECT_EQ(i8::from(ENUM(class, uint64_t)::Z), 2_i8); - - EXPECT_EQ(i8::try_from(ENUM(, char)::Z).unwrap(), 2_i8); + + EXPECT_EQ(i8::try_from(ENUM(, signed_char)::Z).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(ENUM(, size_t)::Z).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(ENUM(, int8_t)::Z).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(ENUM(, int16_t)::Z).unwrap(), 2_i8); @@ -434,39 +421,17 @@ TEST(i8, From) { EXPECT_TRUE(i8::try_from(ENUM(, uint8_t)::MAX).is_err()); EXPECT_TRUE(i8::try_from(ENUM(, uint16_t)::MAX).is_err()); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, char{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, size_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, int8_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, int16_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, int32_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, int64_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, uint8_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, uint16_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, uint32_t{2}), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, uint64_t{2}), 2_i8); - - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_i8); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -477,17 +442,9 @@ TEST(i8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); EXPECT_EQ(i8::from(2_i8), 2_i8); - EXPECT_EQ(i8::from(2_i16), 2_i8); - EXPECT_EQ(i8::from(2_i32), 2_i8); - EXPECT_EQ(i8::from(2_i64), 2_i8); - EXPECT_EQ(i8::from(2_isize), 2_i8); - EXPECT_EQ(i8::from(2_u8), 2_i8); - EXPECT_EQ(i8::from(2_u16), 2_i8); - EXPECT_EQ(i8::from(2_u32), 2_i8); - EXPECT_EQ(i8::from(2_u64), 2_i8); - EXPECT_EQ(i8::from(2_usize), 2_i8); EXPECT_EQ(i8::try_from(2_i8).unwrap(), 2_i8); EXPECT_EQ(i8::try_from(2_i16).unwrap(), 2_i8); @@ -504,120 +461,6 @@ TEST(i8, From) { EXPECT_TRUE(i8::try_from(i16::MAX).is_err()); EXPECT_TRUE(i8::try_from(u8::MAX).is_err()); EXPECT_TRUE(i8::try_from(u16::MAX).is_err()); - - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_i8), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_i16), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_i32), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_i64), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_isize), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_u8), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_u16), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_u32), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_u64), 2_i8); - EXPECT_EQ(i8::from_unchecked(unsafe_fn, 2_usize), 2_i8); -} - -TEST(i8DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = i8::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i8::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = i8::from(i16::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(i32::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(i64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(u8::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(u16::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(u32::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(u64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = i8::from(usize::MAX); - ensure_use(&x); - }, - ""); -#endif } TEST(i8, InvokeEverything) { diff --git a/subspace/num/integer_concepts.h b/subspace/num/integer_concepts.h index 7802f0379..6ef5630dc 100644 --- a/subspace/num/integer_concepts.h +++ b/subspace/num/integer_concepts.h @@ -42,6 +42,7 @@ concept Integer = Unsigned || Signed; template concept UnsignedPrimitiveInteger = std::same_as || std::same_as || + std::same_as || (std::is_unsigned_v && std::same_as) || std::same_as || std::same_as || std::same_as || std::same_as || diff --git a/subspace/num/isize_unittest.cc b/subspace/num/isize_unittest.cc index 8f2b30604..0b07d7fad 100644 --- a/subspace/num/isize_unittest.cc +++ b/subspace/num/isize_unittest.cc @@ -337,36 +337,39 @@ TEST(isize, CompileTimeConversionEnum) { TEST(isize, ToPrimitive) { static_assert(sizeof(isize) > sizeof(int8_t) - ? NotConvertible - : IsExplicitlyConvertible); + ? NotConvertible + : IsExplicitlyConvertible); static_assert(sizeof(isize) > sizeof(int16_t) - ? NotConvertible - : IsExplicitlyConvertible); + ? NotConvertible + : IsExplicitlyConvertible); static_assert(sizeof(isize) > sizeof(int32_t) - ? NotConvertible - : IsExplicitlyConvertible); + ? NotConvertible + : IsExplicitlyConvertible); static_assert(sizeof(isize) > sizeof(int64_t) - ? NotConvertible - : IsExplicitlyConvertible); - static_assert(NotConvertible); - static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + ? NotConvertible + : IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); } TEST(isize, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using signed_char = signed char; + + static_assert(!sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -377,17 +380,17 @@ TEST(isize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -398,17 +401,17 @@ TEST(isize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -419,18 +422,13 @@ TEST(isize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(isize::from(char{2}), 2_isize); - EXPECT_EQ(isize::from(size_t{2}), 2_isize); + EXPECT_EQ(isize::from(signed_char{2}), 2_isize); EXPECT_EQ(isize::from(int8_t{2}), 2_isize); EXPECT_EQ(isize::from(int16_t{2}), 2_isize); EXPECT_EQ(isize::from(int32_t{2}), 2_isize); EXPECT_EQ(isize::from(int64_t{2}), 2_isize); - EXPECT_EQ(isize::from(uint8_t{2}), 2_isize); - EXPECT_EQ(isize::from(uint16_t{2}), 2_isize); - EXPECT_EQ(isize::from(uint32_t{2}), 2_isize); - EXPECT_EQ(isize::from(uint64_t{2}), 2_isize); - EXPECT_EQ(isize::try_from(char{2}).unwrap(), 2_isize); + EXPECT_EQ(isize::try_from(signed_char{2}).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(size_t{2}).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(int8_t{2}).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(int16_t{2}).unwrap(), 2_isize); @@ -448,19 +446,13 @@ TEST(isize, From) { } EXPECT_TRUE(isize::try_from(uint64_t{u64::MAX}).is_err()); - EXPECT_EQ(isize::from(ENUM(, char)::Z), 2_isize); - EXPECT_EQ(isize::from(ENUM(, size_t)::Z), 2_isize); + EXPECT_EQ(isize::from(ENUM(, signed_char)::Z), 2_isize); EXPECT_EQ(isize::from(ENUM(, int8_t)::Z), 2_isize); EXPECT_EQ(isize::from(ENUM(, int16_t)::Z), 2_isize); EXPECT_EQ(isize::from(ENUM(, int32_t)::Z), 2_isize); EXPECT_EQ(isize::from(ENUM(, int64_t)::Z), 2_isize); - EXPECT_EQ(isize::from(ENUM(, uint8_t)::Z), 2_isize); - EXPECT_EQ(isize::from(ENUM(, uint16_t)::Z), 2_isize); - EXPECT_EQ(isize::from(ENUM(, uint32_t)::Z), 2_isize); - EXPECT_EQ(isize::from(ENUM(, uint64_t)::Z), 2_isize); - EXPECT_EQ(isize::from(ENUM(class, uint64_t)::Z), 2_isize); - EXPECT_EQ(isize::try_from(ENUM(, char)::Z).unwrap(), 2_isize); + EXPECT_EQ(isize::try_from(ENUM(,signed char)::Z).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(ENUM(, size_t)::Z).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(ENUM(, int8_t)::Z).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(ENUM(, int16_t)::Z).unwrap(), 2_isize); @@ -481,40 +473,17 @@ TEST(isize, From) { EXPECT_TRUE(isize::try_from(ENUM(, uint64_t)::MAX).is_err()); EXPECT_TRUE(isize::try_from(ENUM(class, uint64_t)::MAX).is_err()); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, char{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, size_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, int8_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, int16_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, int32_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, int64_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, uint8_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, uint16_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, uint32_t{2}), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, uint64_t{2}), 2_isize); - - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), - 2_isize); - static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -525,17 +494,13 @@ TEST(isize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); EXPECT_EQ(isize::from(2_i8), 2_isize); EXPECT_EQ(isize::from(2_i16), 2_isize); EXPECT_EQ(isize::from(2_i32), 2_isize); EXPECT_EQ(isize::from(2_i64), 2_isize); EXPECT_EQ(isize::from(2_isize), 2_isize); - EXPECT_EQ(isize::from(2_u8), 2_isize); - EXPECT_EQ(isize::from(2_u16), 2_isize); - EXPECT_EQ(isize::from(2_u32), 2_isize); - EXPECT_EQ(isize::from(2_u64), 2_isize); - EXPECT_EQ(isize::from(2_usize), 2_isize); EXPECT_EQ(isize::try_from(2_i8).unwrap(), 2_isize); EXPECT_EQ(isize::try_from(2_i16).unwrap(), 2_isize); @@ -554,112 +519,6 @@ TEST(isize, From) { EXPECT_TRUE(isize::try_from(u64::MAX).is_err()); } EXPECT_TRUE(isize::try_from(u64::MAX).is_err()); - - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_i8), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_i16), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_i32), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_i64), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_isize), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_u8), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_u16), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_u32), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_u64), 2_isize); - EXPECT_EQ(isize::from_unchecked(unsafe_fn, 2_usize), 2_isize); -} - -TEST(isizeDeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = isize::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = isize::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - bool not64 = sizeof(isize) != sizeof(i64); - if (not64) { - EXPECT_DEATH( - { - auto x = isize::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(ENUM(, uint32_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(ENUM(class, uint32_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = isize::from(i64::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(i64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(u32::MAX); - ensure_use(&x); - }, - ""); - } - EXPECT_DEATH( - { - auto x = isize::from(u64::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = isize::from(usize::MAX); - ensure_use(&x); - }, - ""); -#endif } TEST(isize, InvokeEverything) { diff --git a/subspace/num/overflow_integer.h b/subspace/num/overflow_integer.h index 18af3f690..42291fef3 100644 --- a/subspace/num/overflow_integer.h +++ b/subspace/num/overflow_integer.h @@ -21,6 +21,7 @@ #include "subspace/marker/unsafe.h" #include "subspace/option/option.h" #include "subspace/result/result.h" +#include "subspace/num/transmogrify.h" namespace sus::num { @@ -46,11 +47,6 @@ class OverflowInteger { /// Satisfies `sus::construct::From, U>` if the inner /// integer type `I` satisfies `sus::construct::From`. - /// - /// # Panics - /// - /// The `from()` method on subspace integer types may panic if the input is - /// out of bounds, in which case this method will also panic. template requires(::sus::construct::From) sus_pure static constexpr OverflowInteger from(U u) noexcept { @@ -89,18 +85,20 @@ class OverflowInteger { /// return an OverflowInteger without ever panicking. static constexpr OverflowInteger from_product( ::sus::iter::Iterator auto&& it) noexcept { - // SAFETY: All integers can hold positive 1. - auto p = OverflowInteger(CONSTRUCT, - I::from_unchecked(::sus::marker::unsafe_fn, 1)); + auto p = OverflowInteger( + CONSTRUCT, + // SAFETY: This is not lossy, as all integers can hold positive 1. + ::sus::mog(1)); for (I i : ::sus::move(it)) p *= i; return p; } static constexpr OverflowInteger from_product( ::sus::iter::Iterator auto&& it) noexcept { - // SAFETY: All integers can hold positive 1. - auto p = OverflowInteger(CONSTRUCT, - I::from_unchecked(::sus::marker::unsafe_fn, 1)); + auto p = OverflowInteger( + CONSTRUCT, + // SAFETY: This is not lossy, as all integers can hold positive 1. + ::sus::mog(1)); for (OverflowInteger i : ::sus::move(it)) p *= i; return p; } diff --git a/subspace/num/overflow_integer_unittest.cc b/subspace/num/overflow_integer_unittest.cc index 3cb570189..7300163c6 100644 --- a/subspace/num/overflow_integer_unittest.cc +++ b/subspace/num/overflow_integer_unittest.cc @@ -75,19 +75,25 @@ TEST(OverflowInteger, From) { static_assert(::sus::construct::Into>); static_assert(::sus::construct::Into>); static_assert(::sus::construct::Into>); - static_assert(::sus::construct::Into>); - static_assert(::sus::construct::Into>); - static_assert(::sus::construct::Into>); - static_assert(::sus::construct::Into>); - static_assert(::sus::construct::Into>); - - EXPECT_EQ(OverflowInteger::from(13_u64).unwrap(), 13); - EXPECT_EQ(OverflowInteger(sus::into(13_u64)).unwrap(), 13); + static_assert(!::sus::construct::Into>); + static_assert(!::sus::construct::Into>); + static_assert(!::sus::construct::Into>); + static_assert(!::sus::construct::Into>); + static_assert(!::sus::construct::Into>); + + EXPECT_EQ(OverflowInteger::from(13_i32).unwrap(), 13); + EXPECT_EQ(OverflowInteger(sus::into(13_i32)).unwrap(), 13); } TEST(OverflowInteger, TryFrom) { - static_assert(::sus::construct::TryFrom, u16>); - static_assert(::sus::construct::TryFrom, i64>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); + static_assert(::sus::construct::TryInto>); EXPECT_EQ(OverflowInteger::try_from(13_u64).unwrap().unwrap(), 13); EXPECT_EQ(OverflowInteger::try_from(u64::MAX).unwrap_err(), diff --git a/subspace/num/signed_integer.h b/subspace/num/signed_integer.h index c67226bca..ad1d57ca0 100644 --- a/subspace/num/signed_integer.h +++ b/subspace/num/signed_integer.h @@ -48,12 +48,21 @@ namespace sus::num { // TODO: div_ceil() and div_floor()? Lots of discussion still on // https://github.com/rust-lang/rust/issues/88581 for signed types. -// TODO: Split apart the declarations and the definitions? Then they can be in -// u32_defn.h and u32_impl.h, allowing most of the library to just use -// u32_defn.h which will keep some headers smaller. But then the combined -// headers are larger, is that worse? - /// A 32-bit signed integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting to a +/// larger signed integer type, the value will be sign-extended. Between +/// integers this behaves the same as static_cast() on primitive integers. +/// Between integers and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] i32 final { #define _self i32 #define _primitive int32_t @@ -65,6 +74,20 @@ struct [[sus_trivial_abi]] i32 final { #include "subspace/num/__private/signed_integer_consts.inc" /// An 8-bit signed integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting to a +/// larger signed integer type, the value will be sign-extended. Between +/// integers this behaves the same as static_cast() on primitive integers. +/// Between integers and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] i8 final { #define _self i8 #define _primitive int8_t @@ -76,6 +99,20 @@ struct [[sus_trivial_abi]] i8 final { #include "subspace/num/__private/signed_integer_consts.inc" /// A 16-bit signed integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting to a +/// larger signed integer type, the value will be sign-extended. Between +/// integers this behaves the same as static_cast() on primitive integers. +/// Between integers and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] i16 final { #define _self i16 #define _primitive int16_t @@ -87,6 +124,20 @@ struct [[sus_trivial_abi]] i16 final { #include "subspace/num/__private/signed_integer_consts.inc" /// A 64-bit signed integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting to a +/// larger signed integer type, the value will be sign-extended. Between +/// integers this behaves the same as static_cast() on primitive integers. +/// Between integers and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] i64 final { #define _self i64 #define _primitive int64_t @@ -107,6 +158,20 @@ struct [[sus_trivial_abi]] i64 final { /// capabilities. See [CHERI]( /// https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-947.pdf) for an example. So /// this type is not always the same size as a pointer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting to a +/// larger signed integer type, the value will be sign-extended. Between +/// integers this behaves the same as static_cast() on primitive integers. +/// Between integers and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] isize final { #define _self isize #define _primitive ::sus::num::__private::addr_type<>::signed_type diff --git a/subspace/num/transmogrify.h b/subspace/num/transmogrify.h new file mode 100644 index 000000000..37d5cbc32 --- /dev/null +++ b/subspace/num/transmogrify.h @@ -0,0 +1,369 @@ +// 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. + +#pragma once + +#include + +#include "subspace/construct/to_bits.h" +#include "subspace/lib/__private/forward_decl.h" +#include "subspace/num/__private/intrinsics.h" +#include "subspace/num/float_concepts.h" +#include "subspace/num/integer_concepts.h" + +// # ================ From signed integers. ============================ + +// ## === Into `Integer` + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>( + from.primitive_value)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>( + static_cast>(from))); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>( + static_cast>(from))); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>( + from.primitive_value)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>(from)); + } +}; + +// ## === Into `PrimitiveInteger` + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return ::sus::mog(from.primitive_value); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(from); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(static_cast>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(static_cast>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + if (from.is_nan()) [[unlikely]] + return T(0); + + struct MinMax { + F min, max; + }; + constexpr MinMax minmax = []() { + if constexpr (::sus::mem::size_of() == 1u) { + if constexpr (std::is_signed_v) { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(-128.f, 127.f); + } else { + return MinMax(-128.0, 127.0); + } + } else { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(0.f, 255.f); + } else { + return MinMax(0.0, 255.0); + } + } + } else if constexpr (::sus::mem::size_of() == 2u) { + if constexpr (std::is_signed_v) { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(-32768.f, 32767.f); + } else { + return MinMax(-32768.0, 32767.0); + } + } else { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(0.f, 65535.f); + } else { + return MinMax(0.0, 65535.0); + } + } + } else if constexpr (::sus::mem::size_of() == 4u) { + if constexpr (std::is_signed_v) { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(-2147483648.f, 2147483647.f); + } else { + return MinMax(-2147483648.0, 2147483647.0); + } + } else { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(0.f, 4294967295.f); + } else { + return MinMax(0.0, 4294967295.0); + } + } + } else { + static_assert(::sus::mem::size_of() == 8u); + if constexpr (std::is_signed_v) { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(-9223372036854775808.f, 9223372036854775807.f); + } else { + return MinMax(-9223372036854775808.0, 9223372036854775807.0); + } + } else { + if constexpr (::sus::mem::size_of() == 4u) { + return MinMax(0.f, 18446744073709551615.f); + } else { + return MinMax(0.0, 18446744073709551615.0); + } + } + } + }(); + if (from >= minmax.max) [[unlikely]] + return ::sus::num::__private::max_value(); + if (from <= minmax.min) [[unlikely]] + return ::sus::num::__private::min_value(); + // SAFETY: The conversion from a float to an integer is UB if the value is + // out of range for the integer. We have checked above that it is not out of + // range by comparing with the floating point representation of the + // integer's min/max values. + return static_cast(from.primitive_value); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + if constexpr (::sus::mem::size_of() == 4u) { + return ::sus::mog(::sus::num::f32(from)); + } else { + static_assert(::sus::mem::size_of() == 8u); + return ::sus::mog(::sus::num::f64(from)); + } + } +}; + +// ## === Into `PrimitiveEnum` + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>( + static_cast>(from))); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>( + static_cast>(from))); + } +}; + +// ## === Into `PrimitiveEnumClass` + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>( + static_cast>(from))); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return static_cast(::sus::mog>( + static_cast>(from))); + } +}; + +// ## === Into `Float` + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>( + from.primitive_value)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>(from)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>( + from.primitive_value)); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return T(::sus::mog().primitive_value)>(from)); + } +}; + +// ## === Into `PrimitiveFloat` + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return ::sus::mog(from.primitive_value); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return ::sus::num::__private::static_cast_int_to_float(from); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + return ::sus::mog(from.primitive_value); + } +}; + +// sus::construct::Transmogrify trait. +template +struct sus::construct::TransmogrifyImpl { + constexpr static T mog_from(const F& from) noexcept { + static_assert(::sus::mem::size_of() == 4u || + ::sus::mem::size_of() == 8u); + static_assert(::sus::mem::size_of() == 4u || + ::sus::mem::size_of() == 8u); + + if constexpr (::sus::mem::size_of() == 4u) { + if constexpr (::sus::mem::size_of() == 4u) { + return from; + } else { + return ::sus::num::__private::static_cast_to_smaller_float(from); + } + } else { + if constexpr (::sus::mem::size_of() == 4u) { + // C++20 Section 7.3.7: A prvalue of type float can be converted to a + // prvalue of type double. The value is unchanged. + return T{from}; + } else { + return from; + } + } + } +}; diff --git a/subspace/num/transmogrify_unittest.cc b/subspace/num/transmogrify_unittest.cc new file mode 100644 index 000000000..1ca54e618 --- /dev/null +++ b/subspace/num/transmogrify_unittest.cc @@ -0,0 +1,678 @@ +// 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. + +#include "subspace/num/transmogrify.h" + +#include "googletest/include/gtest/gtest.h" +#include "subspace/prelude.h" + +namespace { + +template +struct CheckTransmogrify { + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); +}; + +TEST(NumTransmogrify, Satisfies) { + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); + CheckTransmogrify(); +} + +TEST(NumTransmogrify, u8) { + using Self = u8; + // Negative to larger unsigned self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX - Self::try_from(i8::MAX).unwrap()); + } + // Larger unsigned to smaller self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX); + } + // Unsigned self to signed. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, int8_t{-1}); + } +} + +TEST(NumTransmogrify, u16) { + using Self = u16; + // Smaller negative to larger unsigned self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX - Self::try_from(i8::MAX).unwrap()); + } + // Larger unsigned to smaller self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX); + } + // Larger unsigned self to smaller signed. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, int16_t{-1}); + } +} + +TEST(NumTransmogrify, u32) { + using Self = u32; + // Smaller negative to larger unsigned self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX - Self::try_from(i8::MAX).unwrap()); + } + // Larger unsigned to smaller self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX); + } + // Larger unsigned self to smaller signed. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, int16_t{-1}); + } +} + +TEST(NumTransmogrify, u64) { + using Self = u64; + // Smaller negative to larger unsigned self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX - Self::try_from(i8::MAX).unwrap()); + } + // Larger unsigned to smaller self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX); + } + // Larger unsigned self to smaller signed. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, int16_t{-1}); + } +} + +TEST(NumTransmogrify, uptr) { + using Self = uptr; + // Smaller negative to larger unsigned self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, static_cast(i8::MIN_PRIMITIVE)); + } + // Larger unsigned to smaller self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX_BIT_PATTERN); + } + // Larger unsigned self to smaller signed. + { + auto i = sus::mog(Self::MAX_BIT_PATTERN); + static_assert(std::same_as); + EXPECT_EQ(i, int16_t{-1}); + } +} + +TEST(NumTransmogrify, usize) { + using Self = usize; + // Smaller negative to larger unsigned self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX - Self::try_from(i8::MAX).unwrap()); + } + // Larger unsigned to smaller self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, Self::MAX); + } + // Larger unsigned self to smaller signed. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, int16_t{-1}); + } +} + +TEST(NumTransmogrify, i8) { + using Self = i8; + // Smaller unsigned to larger signed self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, i8::MIN); + } + // Larger unsigned to smaller signed self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, -1_i8); + } + // Signed self to unsigned. + { + auto i = sus::mog(-1_i8); + static_assert(std::same_as); + EXPECT_EQ(i, u8::MAX_PRIMITIVE); + } +} + +TEST(NumTransmogrify, i16) { + using Self = i16; + // Smaller unsigned to larger signed self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, i8::MIN); + } + // Larger unsigned to smaller signed self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, -1_i8); + } + // Larger signed self to smaller unsigned. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, i16::MAX_PRIMITIVE); + } +} + +TEST(NumTransmogrify, i32) { + using Self = i32; + // Smaller unsigned to larger signed self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, i8::MIN); + } + // Larger unsigned to smaller signed self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, -1_i8); + } + // Larger signed self to smaller unsigned. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, u16::MAX_PRIMITIVE); + } +} + +TEST(NumTransmogrify, i64) { + using Self = i64; + // Smaller unsigned to larger signed self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, i8::MIN); + } + // Larger unsigned to smaller signed self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, -1_i8); + } + // Larger signed self to smaller unsigned. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, u16::MAX_PRIMITIVE); + } +} + +TEST(NumTransmogrify, isize) { + using Self = isize; + // Smaller unsigned to larger signed self. + { + auto i = sus::mog(i8::MIN); + static_assert(std::same_as); + EXPECT_EQ(i, i8::MIN); + } + // Larger unsigned to smaller signed self. + { + auto i = sus::mog(u64::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, -1_i8); + } + // Larger signed self to smaller unsigned. + { + auto i = sus::mog(Self::MAX); + static_assert(std::same_as); + EXPECT_EQ(i, u16::MAX_PRIMITIVE); + } +} + +TEST(NumTransmogrify, LosslessFloatConversion) { + EXPECT_EQ(sus::mog(-1.8949651689383756e-14_f32), + -1.8949651689383756e-14_f64); + EXPECT_EQ(sus::mog(-1.8949651689383756e-14_f32), + -1.8949651689383756e-14_f32); + EXPECT_EQ(sus::mog(-4.59218127443847370761468605771e-102_f64), + -4.59218127443847370761468605771e-102_f64); +} + +TEST(NumTransmogrify, f64tof32) { + EXPECT_EQ(sus::mog(f64::NAN).is_nan(), true); + EXPECT_EQ(sus::mog(f64::INFINITY), f32::INFINITY); + EXPECT_EQ(sus::mog(f64::NEG_INFINITY), f32::NEG_INFINITY); + EXPECT_EQ(sus::mog(f64::MAX), f32::INFINITY); + EXPECT_EQ(sus::mog(f64::MIN), f32::NEG_INFINITY); + + // Just past the valid range of values for f32 in either direciton. A + // static_cast(double) for these values would cause UB. + EXPECT_EQ(sus::mog( + sus::mog(f32::MIN).next_toward(f64::NEG_INFINITY)), + f32::NEG_INFINITY); + EXPECT_EQ( + sus::mog(sus::mog(f32::MAX).next_toward(f64::INFINITY)), + f32::INFINITY); + + // This is a value with bits set throughout the exponent and mantissa. Its + // exponent is <= 127 and >= -126 so it's possible to represent it in f32. + EXPECT_EQ(sus::mog(-4.59218127443847370761468605771e-102_f64), + -4.59218127443847370761468605771e-102_f32); +} + +TEST(NumTransmogrify, f32) { + static_assert(std::same_as(0_f32)), u16>); + + // Float to smaller unsigned. + { + EXPECT_EQ(sus::mog(f32::NAN), 0_u16); + + EXPECT_EQ(sus::mog(0_f32), u16::MIN); + EXPECT_EQ(sus::mog(-0_f32), u16::MIN); + EXPECT_EQ(sus::mog(-0.00001_f32), u16::MIN); + EXPECT_EQ(sus::mog(-99999999_f32), u16::MIN); + EXPECT_EQ(sus::mog(f32::NEG_INFINITY), u16::MIN); + + EXPECT_EQ(sus::mog(0.1_f32), 0_u16); + EXPECT_EQ(sus::mog(0.51_f32), 0_u16); + EXPECT_EQ(sus::mog(0.9999_f32), 0_u16); + EXPECT_EQ(sus::mog(1_f32), 1_u16); + EXPECT_EQ(sus::mog(65535_f32), u16::MAX); + EXPECT_EQ(sus::mog(65535.00001_f32), u16::MAX); + EXPECT_EQ(sus::mog(65536_f32), u16::MAX); + EXPECT_EQ(sus::mog(999999999_f32), u16::MAX); + EXPECT_EQ(sus::mog(f32::INFINITY), u16::MAX); + } + // Float to smaller signed. + { + EXPECT_EQ(sus::mog(f32::NAN), 0_i16); + + EXPECT_EQ(sus::mog(0_f32), 0_i16); + EXPECT_EQ(sus::mog(-0_f32), 0_i16); + EXPECT_EQ(sus::mog(-0.00001_f32), 0_i16); + EXPECT_EQ(sus::mog(-0.9999_f32), 0_i16); + EXPECT_EQ(sus::mog(-1_f32), -1_i16); + EXPECT_EQ(sus::mog(-32767.999_f32), -32767_i16); + EXPECT_EQ(sus::mog(-32768_f32), i16::MIN); + EXPECT_EQ(sus::mog(-32768.00001_f32), i16::MIN); + EXPECT_EQ(sus::mog(-99999999_f32), i16::MIN); + EXPECT_EQ(sus::mog(f32::NEG_INFINITY), i16::MIN); + + EXPECT_EQ(sus::mog(0.1_f32), 0_i16); + EXPECT_EQ(sus::mog(0.51_f32), 0_i16); + EXPECT_EQ(sus::mog(0.9999_f32), 0_i16); + EXPECT_EQ(sus::mog(1_f32), 1_i16); + EXPECT_EQ(sus::mog(32767.999_f32), i16::MAX); + EXPECT_EQ(sus::mog(32767.00001_f32), i16::MAX); + EXPECT_EQ(sus::mog(32767_f32), i16::MAX); + EXPECT_EQ(sus::mog(999999999_f32), i16::MAX); + EXPECT_EQ(sus::mog(f32::INFINITY), i16::MAX); + } + + // Float to larger unsigned. + { + EXPECT_EQ(sus::mog(f32::NAN), 0_u64); + + EXPECT_EQ(sus::mog(0_f32), u64::MIN); + EXPECT_EQ(sus::mog(-0_f32), u64::MIN); + EXPECT_EQ(sus::mog(-0.00001_f32), u64::MIN); + EXPECT_EQ(sus::mog(-99999999_f32), u64::MIN); + EXPECT_EQ(sus::mog(f32::NEG_INFINITY), u64::MIN); + + EXPECT_EQ(sus::mog(0.1_f32), 0_u64); + EXPECT_EQ(sus::mog(0.51_f32), 0_u64); + EXPECT_EQ(sus::mog(0.9999_f32), 0_u64); + EXPECT_EQ(sus::mog(1_f32), 1_u64); + EXPECT_LT(sus::mog((18446744073709551615_f32).next_toward(0_f32)), + u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615_f32), u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615.00001_f32), u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615_f32 + 1_f32), u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615_f32 * 2_f32), u64::MAX); + EXPECT_EQ(sus::mog(f32::INFINITY), u64::MAX); + } + // Float to larger signed. + { + EXPECT_EQ(sus::mog(f32::NAN), 0_i64); + + EXPECT_EQ(sus::mog(0_f32), 0_i64); + EXPECT_EQ(sus::mog(-0_f32), 0_i64); + EXPECT_EQ(sus::mog(-0.00001_f32), 0_i64); + EXPECT_EQ(sus::mog(-0.9999_f32), 0_i64); + EXPECT_EQ(sus::mog(-1_f32), -1_i64); + EXPECT_GT(sus::mog((-9223372036854775808_f32).next_toward(0_f32)), + i64::MIN); + EXPECT_EQ(sus::mog(-9223372036854775808_f32), i64::MIN); + EXPECT_EQ(sus::mog(-9223372036854775808.00001_f32), i64::MIN); + EXPECT_EQ(sus::mog(-9999999999999999999_f32), i64::MIN); + EXPECT_EQ(sus::mog(f32::NEG_INFINITY), i64::MIN); + + EXPECT_EQ(sus::mog(0.1_f32), 0_i64); + EXPECT_EQ(sus::mog(0.51_f32), 0_i64); + EXPECT_EQ(sus::mog(0.9999_f32), 0_i64); + EXPECT_EQ(sus::mog(1_f32), 1_i64); + EXPECT_LT(sus::mog((9223372036854775807_f32).next_toward(0_f32)), + i64::MAX); + EXPECT_EQ(sus::mog(9223372036854775807_f32), i64::MAX); + EXPECT_EQ(sus::mog(92233720368547758075.00001_f32), i64::MAX); + EXPECT_EQ(sus::mog(9223372036854775808_f32), i64::MAX); + EXPECT_EQ(sus::mog(9999999999999999999_f32), i64::MAX); + EXPECT_EQ(sus::mog(f32::INFINITY), i64::MAX); + } + + // Ints to f32. + { + EXPECT_EQ(sus::mog(0_i8), 0_f32); + EXPECT_EQ(sus::mog(0_u8), 0_f32); + EXPECT_EQ(sus::mog(i16::MIN), -32768_f32); + EXPECT_EQ(sus::mog(i16::MAX), 32767_f32); + // These values are rounded in an implementation-defined way. + EXPECT_EQ(sus::mog(i32::MIN), -2147483600_f32); + EXPECT_EQ(sus::mog(i32::MAX), 2147483600_f32); + EXPECT_EQ(sus::mog(i64::MIN), -9223372036854775808_f32); + EXPECT_EQ(sus::mog(i64::MAX), 9223372036854775808_f32); + EXPECT_EQ(sus::mog(u64::MIN), 0_f32); + EXPECT_EQ(sus::mog(u64::MAX), 18446744073709551616e+0_f32); + } +} + +TEST(NumTransmogrify, f64) { + static_assert(std::same_as(0_f64)), u16>); + + // Float to smaller unsigned. + { + EXPECT_EQ(sus::mog(f64::NAN), 0_u16); + + EXPECT_EQ(sus::mog(0_f64), u16::MIN); + EXPECT_EQ(sus::mog(-0_f64), u16::MIN); + EXPECT_EQ(sus::mog(-0.00001_f64), u16::MIN); + EXPECT_EQ(sus::mog(-99999999_f64), u16::MIN); + EXPECT_EQ(sus::mog(f64::NEG_INFINITY), u16::MIN); + + EXPECT_EQ(sus::mog(0.1_f64), 0_u16); + EXPECT_EQ(sus::mog(0.51_f64), 0_u16); + EXPECT_EQ(sus::mog(0.9999_f64), 0_u16); + EXPECT_EQ(sus::mog(1_f64), 1_u16); + EXPECT_EQ(sus::mog(65535_f64), u16::MAX); + EXPECT_EQ(sus::mog(65535.00001_f64), u16::MAX); + EXPECT_EQ(sus::mog(65536_f64), u16::MAX); + EXPECT_EQ(sus::mog(999999999_f64), u16::MAX); + EXPECT_EQ(sus::mog(f64::INFINITY), u16::MAX); + } + // Float to smaller signed. + { + EXPECT_EQ(sus::mog(f64::NAN), 0_i16); + + EXPECT_EQ(sus::mog(0_f64), 0_i16); + EXPECT_EQ(sus::mog(-0_f64), 0_i16); + EXPECT_EQ(sus::mog(-0.00001_f64), 0_i16); + EXPECT_EQ(sus::mog(-0.9999_f64), 0_i16); + EXPECT_EQ(sus::mog(-1_f64), -1_i16); + EXPECT_EQ(sus::mog(-32767.999_f64), -32767_i16); + EXPECT_EQ(sus::mog(-32768_f64), i16::MIN); + EXPECT_EQ(sus::mog(-32768.00001_f64), i16::MIN); + EXPECT_EQ(sus::mog(-99999999_f64), i16::MIN); + EXPECT_EQ(sus::mog(f64::NEG_INFINITY), i16::MIN); + + EXPECT_EQ(sus::mog(0.1_f64), 0_i16); + EXPECT_EQ(sus::mog(0.51_f64), 0_i16); + EXPECT_EQ(sus::mog(0.9999_f64), 0_i16); + EXPECT_EQ(sus::mog(1_f64), 1_i16); + EXPECT_EQ(sus::mog(65535_f64), i16::MAX); + EXPECT_EQ(sus::mog(65535.00001_f64), i16::MAX); + EXPECT_EQ(sus::mog(65536_f64), i16::MAX); + EXPECT_EQ(sus::mog(999999999_f64), i16::MAX); + EXPECT_EQ(sus::mog(f64::INFINITY), i16::MAX); + } + + // Float to unsigned. + { + EXPECT_EQ(sus::mog(f64::NAN), 0_u64); + + EXPECT_EQ(sus::mog(0_f64), u64::MIN); + EXPECT_EQ(sus::mog(-0_f64), u64::MIN); + EXPECT_EQ(sus::mog(-0.00001_f64), u64::MIN); + EXPECT_EQ(sus::mog(-99999999_f64), u64::MIN); + EXPECT_EQ(sus::mog(f64::NEG_INFINITY), u64::MIN); + + EXPECT_EQ(sus::mog(0.1_f64), 0_u64); + EXPECT_EQ(sus::mog(0.51_f64), 0_u64); + EXPECT_EQ(sus::mog(0.9999_f64), 0_u64); + EXPECT_EQ(sus::mog(1_f64), 1_u64); + EXPECT_LT(sus::mog((18446744073709551615_f64).next_toward(0_f64)), + u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615_f64), u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615.00001_f64), u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615_f64 + 1_f64), u64::MAX); + EXPECT_EQ(sus::mog(18446744073709551615_f64 * 2_f64), u64::MAX); + EXPECT_EQ(sus::mog(f64::INFINITY), u64::MAX); + } + // Float to signed. + { + EXPECT_EQ(sus::mog(f64::NAN), 0_i64); + + EXPECT_EQ(sus::mog(0_f64), 0_i64); + EXPECT_EQ(sus::mog(-0_f64), 0_i64); + EXPECT_EQ(sus::mog(-0.00001_f64), 0_i64); + EXPECT_EQ(sus::mog(-0.9999_f64), 0_i64); + EXPECT_EQ(sus::mog(-1_f64), -1_i64); + EXPECT_GT(sus::mog((-9223372036854775808_f64).next_toward(0_f64)), + i64::MIN); + EXPECT_EQ(sus::mog(-9223372036854775808_f64), i64::MIN); + EXPECT_EQ(sus::mog(-9223372036854775808.00001_f64), i64::MIN); + EXPECT_EQ(sus::mog(-9223372036854775808_f64 * 2_f64), i64::MIN); + EXPECT_EQ(sus::mog(f64::NEG_INFINITY), i64::MIN); + + EXPECT_EQ(sus::mog(0.1_f64), 0_i64); + EXPECT_EQ(sus::mog(0.51_f64), 0_i64); + EXPECT_EQ(sus::mog(0.9999_f64), 0_i64); + EXPECT_EQ(sus::mog(1_f64), 1_i64); + EXPECT_LT(sus::mog((9223372036854775807_f64).next_toward(0_f64)), + i64::MAX); + EXPECT_EQ(sus::mog(9223372036854775807_f64), i64::MAX); + EXPECT_EQ(sus::mog(9223372036854775807.00001_f64), i64::MAX); + EXPECT_EQ(sus::mog(9223372036854775807_f64 * 2_f64), i64::MAX); + EXPECT_EQ(sus::mog(f64::INFINITY), i64::MAX); + } + + // Ints to f64. + { + EXPECT_EQ(sus::mog(0_i8), 0_f64); + EXPECT_EQ(sus::mog(0_u8), 0_f64); + EXPECT_EQ(sus::mog(i16::MIN), -32768_f64); + EXPECT_EQ(sus::mog(i16::MAX), 32767_f64); + // These values are rounded in an implementation-defined way. + EXPECT_EQ(sus::mog(i32::MIN), -2147483648_f64); + EXPECT_EQ(sus::mog(i32::MAX), 2147483648_f64); + EXPECT_EQ(sus::mog(i64::MIN), -9223372036854775808_f64); + EXPECT_EQ(sus::mog(i64::MAX), 9223372036854775808_f64); + EXPECT_EQ(sus::mog(u64::MIN), 0_f64); + EXPECT_EQ(sus::mog(u64::MAX), 18446744073709551616e+0_f64); + } +} + +TEST(NumTransmogrify, stdbyte) { + EXPECT_EQ(sus::mog(std::byte{0xff}), 0xff_u8); + EXPECT_EQ(sus::mog(std::byte{0xff}), 0xff_u32); + EXPECT_EQ(sus::mog(std::byte{0xff}), -1_i8); + EXPECT_EQ(sus::mog(std::byte{0xff}), 0xff_i32); + + EXPECT_EQ(sus::mog(0xff_u8), std::byte{0xff}); + EXPECT_EQ(sus::mog(0xff_u32), std::byte{0xff}); + EXPECT_EQ(sus::mog(-1_i8), std::byte{0xff}); + EXPECT_EQ(sus::mog(0xff_i32), std::byte{0xff}); + + // Truncating from integers. + EXPECT_EQ(sus::mog(-2_i32), std::byte{0xfe}); + EXPECT_EQ(sus::mog(259_i32), std::byte{3}); +} + +enum E : uint16_t { + EA = 1, + EB = 2, + ED = 4, +}; + +enum class EC : int16_t { + A = 1, + B = 2, + D = 4, +}; + +TEST(NumTransmogrify, Enums) { + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(!sus::construct::Transmogrify); + static_assert(!sus::construct::Transmogrify); + + EXPECT_EQ(sus::mog(1_i64), EA); + EXPECT_EQ(sus::mog(2_u64), EB); + + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(sus::construct::Transmogrify); + static_assert(!sus::construct::Transmogrify); + static_assert(!sus::construct::Transmogrify); + + EXPECT_EQ(sus::mog(2_i64), EC::B); + EXPECT_EQ(sus::mog(4_u64), EC::D); +} + +} // namespace diff --git a/subspace/num/types.h b/subspace/num/types.h index 344fdd0fd..9da77a492 100644 --- a/subspace/num/types.h +++ b/subspace/num/types.h @@ -14,6 +14,7 @@ #pragma once +#include "subspace/num/transmogrify.h" #include "subspace/num/float.h" #include "subspace/num/float_impl.h" #include "subspace/num/signed_integer.h" diff --git a/subspace/num/u16_unittest.cc b/subspace/num/u16_unittest.cc index 1ac17eea5..111fa2a40 100644 --- a/subspace/num/u16_unittest.cc +++ b/subspace/num/u16_unittest.cc @@ -309,8 +309,8 @@ TEST(u16, CompileTimeConversionEnum) { TEST(u16, ToPrimitive) { static_assert(NotConvertible); static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); static_assert(NotConvertible); static_assert(IsExplicitlyConvertible); static_assert(IsExplicitlyConvertible); @@ -320,17 +320,20 @@ TEST(u16, ToPrimitive) { } TEST(u16, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using unsigned_char = unsigned char; + + static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -341,17 +344,17 @@ TEST(u16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -362,17 +365,17 @@ TEST(u16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -383,18 +386,11 @@ TEST(u16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(u16::from(char{2}), 2_u16); - EXPECT_EQ(u16::from(size_t{2}), 2_u16); - EXPECT_EQ(u16::from(int8_t{2}), 2_u16); - EXPECT_EQ(u16::from(int16_t{2}), 2_u16); - EXPECT_EQ(u16::from(int32_t{2}), 2_u16); - EXPECT_EQ(u16::from(int64_t{2}), 2_u16); + EXPECT_EQ(u16::from(unsigned_char{2}), 2_u16); EXPECT_EQ(u16::from(uint8_t{2}), 2_u16); EXPECT_EQ(u16::from(uint16_t{2}), 2_u16); - EXPECT_EQ(u16::from(uint32_t{2}), 2_u16); - EXPECT_EQ(u16::from(uint64_t{2}), 2_u16); - EXPECT_EQ(u16::try_from(char{2}).unwrap(), 2_u16); + EXPECT_EQ(u16::try_from(unsigned_char{2}).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(size_t{2}).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(int8_t{2}).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(int16_t{2}).unwrap(), 2_u16); @@ -411,19 +407,11 @@ TEST(u16, From) { EXPECT_TRUE(u16::try_from(int32_t{i32::MAX}).is_err()); EXPECT_TRUE(u16::try_from(uint32_t{u32::MAX}).is_err()); - EXPECT_EQ(u16::from(ENUM(, char)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, size_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, int8_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, int16_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, int32_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, int64_t)::Z), 2_u16); + EXPECT_EQ(u16::from(ENUM(,unsigned char)::Z), 2_u16); EXPECT_EQ(u16::from(ENUM(, uint8_t)::Z), 2_u16); EXPECT_EQ(u16::from(ENUM(, uint16_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, uint32_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(, uint64_t)::Z), 2_u16); - EXPECT_EQ(u16::from(ENUM(class, uint64_t)::Z), 2_u16); - EXPECT_EQ(u16::try_from(ENUM(, char)::Z).unwrap(), 2_u16); + EXPECT_EQ(u16::try_from(ENUM(, unsigned_char)::Z).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(ENUM(, size_t)::Z).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(ENUM(, int8_t)::Z).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(ENUM(, int16_t)::Z).unwrap(), 2_u16); @@ -442,39 +430,17 @@ TEST(u16, From) { EXPECT_TRUE(u16::try_from(ENUM(, uint32_t)::MAX).is_err()); EXPECT_TRUE(u16::try_from(ENUM(class, uint32_t)::MAX).is_err()); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, char{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, size_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, int8_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, int16_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, int32_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, int64_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, uint8_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, uint16_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, uint32_t{2}), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, uint64_t{2}), 2_u16); - - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_u16); - - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -485,17 +451,10 @@ TEST(u16, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); - EXPECT_EQ(u16::from(2_i8), 2_u16); - EXPECT_EQ(u16::from(2_i16), 2_u16); - EXPECT_EQ(u16::from(2_i32), 2_u16); - EXPECT_EQ(u16::from(2_i64), 2_u16); - EXPECT_EQ(u16::from(2_isize), 2_u16); EXPECT_EQ(u16::from(2_u8), 2_u16); EXPECT_EQ(u16::from(2_u16), 2_u16); - EXPECT_EQ(u16::from(2_u32), 2_u16); - EXPECT_EQ(u16::from(2_u64), 2_u16); - EXPECT_EQ(u16::from(2_usize), 2_u16); EXPECT_EQ(u16::try_from(2_i8).unwrap(), 2_u16); EXPECT_EQ(u16::try_from(2_i16).unwrap(), 2_u16); @@ -513,102 +472,6 @@ TEST(u16, From) { EXPECT_TRUE(u16::try_from(i32::MIN).is_err()); EXPECT_TRUE(u16::try_from(i32::MAX).is_err()); EXPECT_TRUE(u16::try_from(u32::MAX).is_err()); - - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_i8), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_i16), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_i32), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_i64), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_isize), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_u8), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_u16), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_u32), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_u64), 2_u16); - EXPECT_EQ(u16::from_unchecked(unsafe_fn, 2_usize), 2_u16); -} - -TEST(u16DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = u16::from(int64_t{-1}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u16::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u16::from(-1_i8); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(-1_i16); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(-1_i32); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(-1_i64); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u16::from(-1_isize); - ensure_use(&x); - }, - ""); -#endif } TEST(u16, InvokeEverything) { diff --git a/subspace/num/u32_unittest.cc b/subspace/num/u32_unittest.cc index b9572d350..dc247df55 100644 --- a/subspace/num/u32_unittest.cc +++ b/subspace/num/u32_unittest.cc @@ -323,7 +323,7 @@ TEST(u32, ToPrimitive) { static_assert(NotConvertible); static_assert(NotConvertible); static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); static_assert(NotConvertible); static_assert(NotConvertible); static_assert(IsExplicitlyConvertible); @@ -333,17 +333,21 @@ TEST(u32, ToPrimitive) { } TEST(u32, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using unsigned_char = unsigned char; + + static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(sizeof(size_t) <= sizeof(u32) == + sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -354,17 +358,18 @@ TEST(u32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(sizeof(size_t) <= sizeof(u32) == + sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -375,17 +380,18 @@ TEST(u32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(sizeof(size_t) <= sizeof(u32) == + sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -396,18 +402,12 @@ TEST(u32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(u32::from(char{2}), 2_u32); - EXPECT_EQ(u32::from(size_t{2}), 2_u32); - EXPECT_EQ(u32::from(int8_t{2}), 2_u32); - EXPECT_EQ(u32::from(int16_t{2}), 2_u32); - EXPECT_EQ(u32::from(int32_t{2}), 2_u32); - EXPECT_EQ(u32::from(int64_t{2}), 2_u32); + EXPECT_EQ(u32::from(unsigned_char{2}), 2_u32); EXPECT_EQ(u32::from(uint8_t{2}), 2_u32); EXPECT_EQ(u32::from(uint16_t{2}), 2_u32); EXPECT_EQ(u32::from(uint32_t{2}), 2_u32); - EXPECT_EQ(u32::from(uint64_t{2}), 2_u32); - EXPECT_EQ(u32::try_from(char{2}).unwrap(), 2_u32); + EXPECT_EQ(u32::try_from(unsigned_char{2}).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(size_t{2}).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(int8_t{2}).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(int16_t{2}).unwrap(), 2_u32); @@ -424,24 +424,13 @@ TEST(u32, From) { EXPECT_TRUE(u32::try_from(int64_t{i64::MAX}).is_err()); EXPECT_TRUE(u32::try_from(uint64_t{u64::MAX}).is_err()); - EXPECT_EQ(u32::from(ENUM(, char)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(, size_t)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(, int8_t)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(, int16_t)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(, int32_t)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(, int64_t)::Z), 2_u32); + EXPECT_EQ(u32::from(ENUM(, unsigned_char)::Z), 2_u32); EXPECT_EQ(u32::from(ENUM(, uint8_t)::Z), 2_u32); EXPECT_EQ(u32::from(ENUM(, uint16_t)::Z), 2_u32); EXPECT_EQ(u32::from(ENUM(, uint32_t)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(, uint64_t)::Z), 2_u32); - EXPECT_EQ(u32::from(ENUM(class, uint64_t)::Z), 2_u32); - EXPECT_EQ(u32::try_from(ENUM(, char)::Z).unwrap(), 2_u32); + EXPECT_EQ(u32::try_from(ENUM(, unsigned_char)::Z).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(ENUM(, size_t)::Z).unwrap(), 2_u32); - EXPECT_EQ(u32::try_from(ENUM(, int8_t)::Z).unwrap(), 2_u32); - EXPECT_EQ(u32::try_from(ENUM(, int16_t)::Z).unwrap(), 2_u32); - EXPECT_EQ(u32::try_from(ENUM(, int32_t)::Z).unwrap(), 2_u32); - EXPECT_EQ(u32::try_from(ENUM(, int64_t)::Z).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(ENUM(, uint8_t)::Z).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(ENUM(, uint16_t)::Z).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(ENUM(, uint32_t)::Z).unwrap(), 2_u32); @@ -455,39 +444,18 @@ TEST(u32, From) { EXPECT_TRUE(u32::try_from(ENUM(, uint64_t)::MAX).is_err()); EXPECT_TRUE(u32::try_from(ENUM(class, uint64_t)::MAX).is_err()); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, char{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, size_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, int8_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, int16_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, int32_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, int64_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, uint8_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, uint16_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, uint32_t{2}), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, uint64_t{2}), 2_u32); - - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_u32); - - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sizeof(usize) <= sizeof(u32) == + sus::construct::From); + static_assert(sizeof(uptr) <= sizeof(u64) == sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -498,17 +466,11 @@ TEST(u32, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); - EXPECT_EQ(u32::from(2_i8), 2_u32); - EXPECT_EQ(u32::from(2_i16), 2_u32); - EXPECT_EQ(u32::from(2_i32), 2_u32); - EXPECT_EQ(u32::from(2_i64), 2_u32); - EXPECT_EQ(u32::from(2_isize), 2_u32); EXPECT_EQ(u32::from(2_u8), 2_u32); EXPECT_EQ(u32::from(2_u16), 2_u32); EXPECT_EQ(u32::from(2_u32), 2_u32); - EXPECT_EQ(u32::from(2_u64), 2_u32); - EXPECT_EQ(u32::from(2_usize), 2_u32); EXPECT_EQ(u32::try_from(2_i8).unwrap(), 2_u32); EXPECT_EQ(u32::try_from(2_i16).unwrap(), 2_u32); @@ -526,120 +488,19 @@ TEST(u32, From) { EXPECT_TRUE(u32::try_from(i64::MIN).is_err()); EXPECT_TRUE(u32::try_from(i64::MAX).is_err()); EXPECT_TRUE(u32::try_from(u64::MAX).is_err()); - - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_i8), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_i16), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_i32), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_i64), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_isize), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_u8), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_u16), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_u32), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_u64), 2_u32); - EXPECT_EQ(u32::from_unchecked(unsafe_fn, 2_usize), 2_u32); -} - -TEST(u32DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = u32::from(int64_t{-1}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u32::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u32::from(-1_i8); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(-1_i16); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(-1_i32); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(-1_i64); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u32::from(-1_isize); - ensure_use(&x); - }, - ""); -#endif } TEST(u32, TryFromBoundaries) { // Signed primitives. - EXPECT_TRUE(u32::try_from(int64_t{i64::from(u32::MAX)}).is_ok()); - EXPECT_TRUE(u32::try_from(int64_t{i64::from(u32::MAX) + 1}).is_err()); + EXPECT_TRUE(u32::try_from(int64_t{i64::try_from(u32::MAX).unwrap()}).is_ok()); + EXPECT_TRUE( + u32::try_from(int64_t{i64::try_from(u32::MAX).unwrap() + 1}).is_err()); EXPECT_TRUE(u32::try_from(int64_t{0}).is_ok()); EXPECT_TRUE(u32::try_from(int64_t{-1}).is_err()); // Signed integers. - EXPECT_TRUE(u32::try_from(i64::from(u32::MAX)).is_ok()); - EXPECT_TRUE(u32::try_from(i64::from(u32::MAX) + 1).is_err()); + EXPECT_TRUE(u32::try_from(i64::try_from(u32::MAX).unwrap()).is_ok()); + EXPECT_TRUE(u32::try_from(i64::try_from(u32::MAX).unwrap() + 1).is_err()); EXPECT_TRUE(u32::try_from(0_i32).is_ok()); EXPECT_TRUE(u32::try_from(-1_i32).is_err()); @@ -1633,13 +1494,13 @@ TEST(u32, ReverseBits) { constexpr auto a3 = (0xf8f800_u32).reverse_bits(); EXPECT_EQ(a3, 0x1f1f00_u32); constexpr auto a5 = (1_u32).reverse_bits(); - EXPECT_EQ(a5, 1_u32 << (sus::into(sizeof(u32)) * 8_u32 - 1_u32)); + EXPECT_EQ(a5, 1_u32 << (sus::mog(sizeof(u32)) * 8_u32 - 1_u32)); EXPECT_EQ((0_u32).reverse_bits(), 0_u32); EXPECT_EQ((2_u32).reverse_bits(), 1_u32 << 30_u32); EXPECT_EQ((0xf8f800_u32).reverse_bits(), 0x1f1f00_u32); EXPECT_EQ((1_u32).reverse_bits().primitive_value, - 1_u32 << (sus::into(sizeof(u32)) * 8_u32 - 1_u32)); + 1_u32 << (sus::mog(sizeof(u32)) * 8_u32 - 1_u32)); } TEST(u32, RotateLeft) { diff --git a/subspace/num/u64_unittest.cc b/subspace/num/u64_unittest.cc index ea821652e..5d72202f6 100644 --- a/subspace/num/u64_unittest.cc +++ b/subspace/num/u64_unittest.cc @@ -326,17 +326,20 @@ TEST(u64, ToPrimitive) { } TEST(u64, From) { - static_assert(sus::construct::From); + using unsigned_char = unsigned char; + + static_assert(sus::construct::From); + static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -347,17 +350,17 @@ TEST(u64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); + static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -368,17 +371,17 @@ TEST(u64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); + static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -389,18 +392,14 @@ TEST(u64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(u64::from(char{2}), 2_u64); + EXPECT_EQ(u64::from(unsigned_char{2}), 2_u64); EXPECT_EQ(u64::from(size_t{2}), 2_u64); - EXPECT_EQ(u64::from(int8_t{2}), 2_u64); - EXPECT_EQ(u64::from(int16_t{2}), 2_u64); - EXPECT_EQ(u64::from(int32_t{2}), 2_u64); - EXPECT_EQ(u64::from(int64_t{2}), 2_u64); EXPECT_EQ(u64::from(uint8_t{2}), 2_u64); EXPECT_EQ(u64::from(uint16_t{2}), 2_u64); EXPECT_EQ(u64::from(uint32_t{2}), 2_u64); EXPECT_EQ(u64::from(uint64_t{2}), 2_u64); - EXPECT_EQ(u64::try_from(char{2}).unwrap(), 2_u64); + EXPECT_EQ(u64::try_from(unsigned_char{2}).unwrap(), 2_u64); EXPECT_EQ(u64::try_from(size_t{2}).unwrap(), 2_u64); EXPECT_EQ(u64::try_from(int8_t{2}).unwrap(), 2_u64); EXPECT_EQ(u64::try_from(int16_t{2}).unwrap(), 2_u64); @@ -414,19 +413,15 @@ TEST(u64, From) { EXPECT_TRUE(u64::try_from(int64_t{i64::MIN}).is_err()); EXPECT_TRUE(u64::try_from(int64_t{i64::MAX}).is_ok()); - EXPECT_EQ(u64::from(ENUM(, char)::Z), 2_u64); + EXPECT_EQ(u64::from(ENUM(, unsigned_char)::Z), 2_u64); EXPECT_EQ(u64::from(ENUM(, size_t)::Z), 2_u64); - EXPECT_EQ(u64::from(ENUM(, int8_t)::Z), 2_u64); - EXPECT_EQ(u64::from(ENUM(, int16_t)::Z), 2_u64); - EXPECT_EQ(u64::from(ENUM(, int32_t)::Z), 2_u64); - EXPECT_EQ(u64::from(ENUM(, int64_t)::Z), 2_u64); EXPECT_EQ(u64::from(ENUM(, uint8_t)::Z), 2_u64); EXPECT_EQ(u64::from(ENUM(, uint16_t)::Z), 2_u64); EXPECT_EQ(u64::from(ENUM(, uint32_t)::Z), 2_u64); EXPECT_EQ(u64::from(ENUM(, uint64_t)::Z), 2_u64); EXPECT_EQ(u64::from(ENUM(class, uint64_t)::Z), 2_u64); - EXPECT_EQ(u64::try_from(ENUM(, char)::Z).unwrap(), 2_u64); + EXPECT_EQ(u64::try_from(ENUM(, unsigned_char)::Z).unwrap(), 2_u64); EXPECT_EQ(u64::try_from(ENUM(, size_t)::Z).unwrap(), 2_u64); EXPECT_EQ(u64::try_from(ENUM(, int8_t)::Z).unwrap(), 2_u64); EXPECT_EQ(u64::try_from(ENUM(, int16_t)::Z).unwrap(), 2_u64); @@ -442,39 +437,17 @@ TEST(u64, From) { EXPECT_TRUE(u64::try_from(ENUM(, int64_t)::MAX).is_ok()); EXPECT_TRUE(u64::try_from(ENUM(class, int64_t)::MAX).is_ok()); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, char{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, size_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, int8_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, int16_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, int32_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, int64_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, uint8_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, uint16_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, uint32_t{2}), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, uint64_t{2}), 2_u64); - - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_u64); - - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); + static_assert(sizeof(uptr) <= sizeof(u64) == sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -485,12 +458,8 @@ TEST(u64, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); - EXPECT_EQ(u64::from(2_i8), 2_u64); - EXPECT_EQ(u64::from(2_i16), 2_u64); - EXPECT_EQ(u64::from(2_i32), 2_u64); - EXPECT_EQ(u64::from(2_i64), 2_u64); - EXPECT_EQ(u64::from(2_isize), 2_u64); EXPECT_EQ(u64::from(2_u8), 2_u64); EXPECT_EQ(u64::from(2_u16), 2_u64); EXPECT_EQ(u64::from(2_u32), 2_u64); @@ -510,78 +479,6 @@ TEST(u64, From) { EXPECT_TRUE(u64::try_from(i64::MIN).is_err()); EXPECT_TRUE(u64::try_from(i64::MAX).is_ok()); - - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_i8), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_i16), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_i32), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_i64), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_isize), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_u8), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_u16), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_u32), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_u64), 2_u64); - EXPECT_EQ(u64::from_unchecked(unsafe_fn, 2_usize), 2_u64); -} - -TEST(u64DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = u64::from(int64_t{-1}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u64::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u64::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u64::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u64::from(-1_i8); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u64::from(-1_i16); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u64::from(-1_i32); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u64::from(-1_i64); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u64::from(-1_isize); - ensure_use(&x); - }, - ""); -#endif } TEST(u64, CheckedMul) { diff --git a/subspace/num/u8_unittest.cc b/subspace/num/u8_unittest.cc index 118754c8d..2a0b2b8b6 100644 --- a/subspace/num/u8_unittest.cc +++ b/subspace/num/u8_unittest.cc @@ -306,9 +306,9 @@ TEST(u8, CompileTimeConversionEnum) { TEST(u8, ToPrimitive) { static_assert(NotConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); - static_assert(IsExplicitlyConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); + static_assert(NotConvertible); static_assert(IsExplicitlyConvertible); static_assert(IsExplicitlyConvertible); static_assert(IsExplicitlyConvertible); @@ -318,17 +318,20 @@ TEST(u8, ToPrimitive) { } TEST(u8, From) { - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + using unsigned_char = unsigned char; + + static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -339,17 +342,17 @@ TEST(u8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -360,17 +363,17 @@ TEST(u8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -381,18 +384,10 @@ TEST(u8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(u8::from(char{2}), 2_u8); - EXPECT_EQ(u8::from(size_t{2}), 2_u8); - EXPECT_EQ(u8::from(int8_t{2}), 2_u8); - EXPECT_EQ(u8::from(int16_t{2}), 2_u8); - EXPECT_EQ(u8::from(int32_t{2}), 2_u8); - EXPECT_EQ(u8::from(int64_t{2}), 2_u8); + EXPECT_EQ(u8::from(unsigned_char{2}), 2_u8); EXPECT_EQ(u8::from(uint8_t{2}), 2_u8); - EXPECT_EQ(u8::from(uint16_t{2}), 2_u8); - EXPECT_EQ(u8::from(uint32_t{2}), 2_u8); - EXPECT_EQ(u8::from(uint64_t{2}), 2_u8); - EXPECT_EQ(u8::try_from(char{2}).unwrap(), 2_u8); + EXPECT_EQ(u8::try_from(unsigned_char{2}).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(size_t{2}).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(int8_t{2}).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(int16_t{2}).unwrap(), 2_u8); @@ -409,19 +404,10 @@ TEST(u8, From) { EXPECT_TRUE(u8::try_from(int16_t{i16::MAX}).is_err()); EXPECT_TRUE(u8::try_from(uint16_t{u16::MAX}).is_err()); - EXPECT_EQ(u8::from(ENUM(, char)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, size_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, int8_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, int16_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, int32_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, int64_t)::Z), 2_u8); + EXPECT_EQ(u8::from(ENUM(, unsigned_char)::Z), 2_u8); EXPECT_EQ(u8::from(ENUM(, uint8_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, uint16_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, uint32_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(, uint64_t)::Z), 2_u8); - EXPECT_EQ(u8::from(ENUM(class, uint64_t)::Z), 2_u8); - EXPECT_EQ(u8::try_from(ENUM(, char)::Z).unwrap(), 2_u8); + EXPECT_EQ(u8::try_from(ENUM(, unsigned_char)::Z).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(ENUM(, size_t)::Z).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(ENUM(, int8_t)::Z).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(ENUM(, int16_t)::Z).unwrap(), 2_u8); @@ -440,39 +426,17 @@ TEST(u8, From) { EXPECT_TRUE(u8::try_from(ENUM(, uint16_t)::MAX).is_err()); EXPECT_TRUE(u8::try_from(ENUM(class, uint16_t)::MAX).is_err()); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, char{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, size_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, int8_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, int16_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, int32_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, int64_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, uint8_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, uint16_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, uint32_t{2}), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, uint64_t{2}), 2_u8); - - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, ENUM(class, uint64_t)::Z), 2_u8); - - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -483,17 +447,9 @@ TEST(u8, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); - EXPECT_EQ(u8::from(2_i8), 2_u8); - EXPECT_EQ(u8::from(2_i16), 2_u8); - EXPECT_EQ(u8::from(2_i32), 2_u8); - EXPECT_EQ(u8::from(2_i64), 2_u8); - EXPECT_EQ(u8::from(2_isize), 2_u8); EXPECT_EQ(u8::from(2_u8), 2_u8); - EXPECT_EQ(u8::from(2_u16), 2_u8); - EXPECT_EQ(u8::from(2_u32), 2_u8); - EXPECT_EQ(u8::from(2_u64), 2_u8); - EXPECT_EQ(u8::from(2_usize), 2_u8); EXPECT_EQ(u8::try_from(2_i8).unwrap(), 2_u8); EXPECT_EQ(u8::try_from(2_i16).unwrap(), 2_u8); @@ -511,102 +467,6 @@ TEST(u8, From) { EXPECT_TRUE(u8::try_from(i16::MIN).is_err()); EXPECT_TRUE(u8::try_from(i16::MAX).is_err()); EXPECT_TRUE(u8::try_from(u16::MAX).is_err()); - - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_i8), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_i16), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_i32), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_i64), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_isize), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_u8), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_u16), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_u32), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_u64), 2_u8); - EXPECT_EQ(u8::from_unchecked(unsafe_fn, 2_usize), 2_u8); -} - -TEST(u8DeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = u8::from(int64_t{-1}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u8::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(ENUM(, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(ENUM(class, int64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = u8::from(-1_i8); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(-1_i16); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(-1_i32); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(-1_i64); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = u8::from(-1_isize); - ensure_use(&x); - }, - ""); -#endif } TEST(u8, InvokeEverything) { diff --git a/subspace/num/unsigned_integer.h b/subspace/num/unsigned_integer.h index 4cfb8d472..003522571 100644 --- a/subspace/num/unsigned_integer.h +++ b/subspace/num/unsigned_integer.h @@ -34,6 +34,7 @@ #include "subspace/num/__private/intrinsics.h" #include "subspace/num/__private/literals.h" #include "subspace/num/__private/primitive_type.h" +#include "subspace/num/float_concepts.h" #include "subspace/num/integer_concepts.h" #include "subspace/num/try_from_int_error.h" #include "subspace/string/__private/format_to_stream.h" @@ -43,6 +44,20 @@ namespace sus::num { // TODO: from_str_radix(). Need Result typ`e and Errors. /// A 32-bit unsigned integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting from a +/// smaller signed type, the value will be sign-extended. Between integers this +/// behaves the same as static_cast() on primitive integers. Between integers +/// and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] u32 final { #define _self u32 #define _pointer 0 @@ -56,6 +71,20 @@ struct [[sus_trivial_abi]] u32 final { #include "subspace/num/__private/unsigned_integer_consts.inc" /// An 8-bit unsigned integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting from a +/// smaller signed type, the value will be sign-extended. Between integers this +/// behaves the same as static_cast() on primitive integers. Between integers +/// and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] u8 final { #define _self u8 #define _pointer 0 @@ -69,6 +98,20 @@ struct [[sus_trivial_abi]] u8 final { #include "subspace/num/__private/unsigned_integer_consts.inc" /// A 16-bit unsigned integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting from a +/// smaller signed type, the value will be sign-extended. Between integers this +/// behaves the same as static_cast() on primitive integers. Between integers +/// and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] u16 final { #define _self u16 #define _pointer 0 @@ -82,6 +125,20 @@ struct [[sus_trivial_abi]] u16 final { #include "subspace/num/__private/unsigned_integer_consts.inc" /// A 64-bit unsigned integer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting from a +/// smaller signed type, the value will be sign-extended. Between integers this +/// behaves the same as static_cast() on primitive integers. Between integers +/// and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] u64 final { #define _self u64 #define _pointer 0 @@ -107,6 +164,20 @@ struct [[sus_trivial_abi]] u64 final { /// this type is not always the same size as a pointer and should not be used to /// hold a pointer value without acknowledging that it is only the address part /// of the pointer. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting from a +/// smaller signed type, the value will be sign-extended. Between integers this +/// behaves the same as static_cast() on primitive integers. Between integers +/// and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] usize final { #define _self usize #define _pointer 0 @@ -137,6 +208,20 @@ struct [[sus_trivial_abi]] usize final { /// /// To explicitly construct a `uptr` with empty metadata, use /// `uptr().with_addr(address)`. +/// +/// # Conversions +/// +/// To convert to and from integer values, use `sus::into()` when +/// `sus::construct::Into` is satisfied between the two types for +/// lossless conversion. Otherwise use `sus::try_into()` when +/// `sus::construct::TryInto` is satisfied to convert and handle cases +/// where the value can not be represented in the target type. +/// +/// To do a bitwise conversion, use `sus::mog()` which is supported with +/// all integer and float types and C++ primitive types. When converting from a +/// smaller signed type, the value will be sign-extended. Between integers this +/// behaves the same as static_cast() on primitive integers. Between integers +/// and floats, this strictly converts the bit value. struct [[sus_trivial_abi]] uptr final { #define _self uptr #define _pointer 1 diff --git a/subspace/num/usize_unittest.cc b/subspace/num/usize_unittest.cc index 9c282af85..bd3cd963e 100644 --- a/subspace/num/usize_unittest.cc +++ b/subspace/num/usize_unittest.cc @@ -348,17 +348,20 @@ TEST(usize, ToPrimitive) { } TEST(usize, From) { - static_assert(sus::construct::From); + using unsigned_char = unsigned char; + + static_assert(sus::construct::From); + static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -369,17 +372,17 @@ TEST(usize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); + static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -390,17 +393,17 @@ TEST(usize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - static_assert(sus::construct::From); + static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); - static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -411,18 +414,14 @@ TEST(usize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); - EXPECT_EQ(usize::from(char{2}), 2_usize); + EXPECT_EQ(usize::from(unsigned_char{2}), 2_usize); EXPECT_EQ(usize::from(size_t{2}), 2_usize); - EXPECT_EQ(usize::from(int8_t{2}), 2_usize); - EXPECT_EQ(usize::from(int16_t{2}), 2_usize); - EXPECT_EQ(usize::from(int32_t{2}), 2_usize); - EXPECT_EQ(usize::from(int64_t{2}), 2_usize); EXPECT_EQ(usize::from(uint8_t{2}), 2_usize); EXPECT_EQ(usize::from(uint16_t{2}), 2_usize); EXPECT_EQ(usize::from(uint32_t{2}), 2_usize); EXPECT_EQ(usize::from(uint64_t{2}), 2_usize); - EXPECT_EQ(usize::try_from(char{2}).unwrap(), 2_usize); + EXPECT_EQ(usize::try_from(unsigned_char{2}).unwrap(), 2_usize); EXPECT_EQ(usize::try_from(size_t{2}).unwrap(), 2_usize); EXPECT_EQ(usize::try_from(int8_t{2}).unwrap(), 2_usize); EXPECT_EQ(usize::try_from(int16_t{2}).unwrap(), 2_usize); @@ -440,19 +439,15 @@ TEST(usize, From) { EXPECT_TRUE(usize::try_from(int64_t{i64::MIN}).is_err()); EXPECT_TRUE(usize::try_from(int64_t{i64::MAX}).is_ok()); - EXPECT_EQ(usize::from(ENUM(, char)::Z), 2_usize); + EXPECT_EQ(usize::from(ENUM(, unsigned_char)::Z), 2_usize); EXPECT_EQ(usize::from(ENUM(, size_t)::Z), 2_usize); - EXPECT_EQ(usize::from(ENUM(, int8_t)::Z), 2_usize); - EXPECT_EQ(usize::from(ENUM(, int16_t)::Z), 2_usize); - EXPECT_EQ(usize::from(ENUM(, int32_t)::Z), 2_usize); - EXPECT_EQ(usize::from(ENUM(, int64_t)::Z), 2_usize); EXPECT_EQ(usize::from(ENUM(, uint8_t)::Z), 2_usize); EXPECT_EQ(usize::from(ENUM(, uint16_t)::Z), 2_usize); EXPECT_EQ(usize::from(ENUM(, uint32_t)::Z), 2_usize); EXPECT_EQ(usize::from(ENUM(, uint64_t)::Z), 2_usize); EXPECT_EQ(usize::from(ENUM(class, uint64_t)::Z), 2_usize); - EXPECT_EQ(usize::try_from(ENUM(, char)::Z).unwrap(), 2_usize); + EXPECT_EQ(usize::try_from(ENUM(,unsigned char)::Z).unwrap(), 2_usize); EXPECT_EQ(usize::try_from(ENUM(, size_t)::Z).unwrap(), 2_usize); EXPECT_EQ(usize::try_from(ENUM(, int8_t)::Z).unwrap(), 2_usize); EXPECT_EQ(usize::try_from(ENUM(, int16_t)::Z).unwrap(), 2_usize); @@ -473,38 +468,17 @@ TEST(usize, From) { EXPECT_TRUE(usize::try_from(ENUM(, int64_t)::MAX).is_ok()); EXPECT_TRUE(usize::try_from(ENUM(class, int64_t)::MAX).is_ok()); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, char{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, size_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, int8_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, int16_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, int32_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, int64_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, uint8_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, uint16_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, uint32_t{2}), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, uint64_t{2}), 2_usize); - - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, char)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, size_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, int8_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, int16_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, int32_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, int64_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, uint8_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, uint16_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, uint32_t)::Z), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, ENUM(, uint64_t)::Z), 2_usize); - - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); - static_assert(sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); + static_assert(!sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); static_assert(sus::construct::From); + static_assert(sizeof(uptr) <= sizeof(usize) == sus::construct::From); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); @@ -515,12 +489,8 @@ TEST(usize, From) { static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); static_assert(sus::construct::TryFrom); + static_assert(sus::construct::TryFrom); - EXPECT_EQ(usize::from(2_i8), 2_usize); - EXPECT_EQ(usize::from(2_i16), 2_usize); - EXPECT_EQ(usize::from(2_i32), 2_usize); - EXPECT_EQ(usize::from(2_i64), 2_usize); - EXPECT_EQ(usize::from(2_isize), 2_usize); EXPECT_EQ(usize::from(2_u8), 2_usize); EXPECT_EQ(usize::from(2_u16), 2_usize); EXPECT_EQ(usize::from(2_u32), 2_usize); @@ -544,101 +514,6 @@ TEST(usize, From) { } EXPECT_TRUE(usize::try_from(i64::MIN).is_err()); EXPECT_TRUE(usize::try_from(i64::MAX).is_ok()); - - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_i8), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_i16), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_i32), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_i64), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_isize), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_u8), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_u16), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_u32), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_u64), 2_usize); - EXPECT_EQ(usize::from_unchecked(unsafe_fn, 2_usize), 2_usize); -} - -TEST(usizeDeathTest, FromOutOfRange) { -#if GTEST_HAS_DEATH_TEST - EXPECT_DEATH( - { - auto x = usize::from(int64_t{-1}); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(int64_t{-1 - 0x7fff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - - EXPECT_DEATH( - { - auto x = usize::from(ENUM(, int64_t)::MIN); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(ENUM(class, int64_t)::MIN); - ensure_use(&x); - }, - ""); - - bool not64 = - sizeof(usize) != sizeof(u64); // `if constexpr` breaks death tests. - if (not64) { - EXPECT_DEATH( - { - auto x = usize::from(ENUM(, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(ENUM(class, uint64_t)::MAX); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(uint64_t{0xffff'ffff'ffff'ffff}); - ensure_use(&x); - }, - ""); - } - - EXPECT_DEATH( - { - auto x = usize::from(-1_i8); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(-1_i16); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(-1_i32); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(-1_i64); - ensure_use(&x); - }, - ""); - EXPECT_DEATH( - { - auto x = usize::from(-1_isize); - ensure_use(&x); - }, - ""); -#endif } TEST(usize, InvokeEverything) { diff --git a/subspace/ops/try.h b/subspace/ops/try.h index 46084ccfd..458e04554 100644 --- a/subspace/ops/try.h +++ b/subspace/ops/try.h @@ -20,7 +20,7 @@ namespace sus::ops { /// Specializations of this struct are used to implement the Try concept. template -struct TryImpl; +struct TryImpl {}; /// A concept for types that can indicate success and failure. /// diff --git a/subspace/option/option_compat_unittest.cc b/subspace/option/option_compat_unittest.cc index 4c2f86616..14e5e8271 100644 --- a/subspace/option/option_compat_unittest.cc +++ b/subspace/option/option_compat_unittest.cc @@ -255,16 +255,16 @@ TEST(OptionCompat, ToOptionalRef) { } TEST(OptionCompat, FromOptionalCopyWithConversion) { - static_assert(sus::construct::Into); + static_assert(sus::construct::Into); // Move. - constexpr sus::Option o = sus::into(std::optional(101)); - EXPECT_EQ(o.as_value(), 101_i32); + constexpr sus::Option o = sus::into(std::optional(101)); + EXPECT_EQ(o.as_value(), 101_i64); // Copy. - constexpr auto f = std::optional(101); - constexpr sus::Option t = sus::into(f); - EXPECT_EQ(t.as_value(), 101_i32); + constexpr auto f = std::optional(101); + constexpr sus::Option t = sus::into(f); + EXPECT_EQ(t.as_value(), 101_i64); } } // namespace diff --git a/subspace/option/option_unittest.cc b/subspace/option/option_unittest.cc index a325ade7e..c435b8b7b 100644 --- a/subspace/option/option_unittest.cc +++ b/subspace/option/option_unittest.cc @@ -2820,16 +2820,16 @@ struct CollectSum { }; TEST(Option, From) { - static_assert(sus::construct::Into); + static_assert(sus::construct::Into< i32, i64>); // Move. - Option o = sus::into(Option::with(101)); - EXPECT_EQ(o.as_value(), 101_i32); + Option o = sus::into(Option::with(101)); + EXPECT_EQ(o.as_value(), 101_i64); // Copy. - auto f = Option::with(101); - Option t = sus::into(f); - EXPECT_EQ(t.as_value(), 101_i32); + auto f = Option::with(101); + Option t = sus::into(f); + EXPECT_EQ(t.as_value(), 101_i64); } TEST(Option, FromIter) { diff --git a/subspace/ptr/swap_unittest.cc b/subspace/ptr/swap_unittest.cc index f5ab8d766..c47f03c3a 100644 --- a/subspace/ptr/swap_unittest.cc +++ b/subspace/ptr/swap_unittest.cc @@ -40,8 +40,8 @@ TEST(PtrSwapNonOverlapping, SmallSizedType_PowTwoSized) { for (usize i : "0..100"_r) { SCOPED_TRACE(size_t{i}); - EXPECT_EQ(a[i], S(u16::from(100u + i))); - EXPECT_EQ(b[i], S(u16::from(0u + i))); + EXPECT_EQ(a[i], S(u16::try_from(100u + i).unwrap())); + EXPECT_EQ(b[i], S(u16::try_from(0u + i).unwrap())); } } @@ -64,8 +64,8 @@ TEST(PtrSwapNonOverlapping, SmallSizedType_NonPowTwoSized) { for (usize i : "0..100"_r) { SCOPED_TRACE(size_t{i}); - EXPECT_EQ(a[i], S(u16::from(100u + i))); - EXPECT_EQ(b[i], S(u16::from(0u + i))); + EXPECT_EQ(a[i], S(u16::try_from(100u + i).unwrap())); + EXPECT_EQ(b[i], S(u16::try_from(0u + i).unwrap())); } } diff --git a/subspace/result/__private/is_result_type.h b/subspace/result/__private/is_result_type.h index 826811813..837b39155 100644 --- a/subspace/result/__private/is_result_type.h +++ b/subspace/result/__private/is_result_type.h @@ -30,4 +30,9 @@ struct IsResultType<::sus::result::Result> final : std::true_type { using err_type = V; }; +template +concept IsResultWithSuccessType = + IsResultType::value && + std::same_as::ok_type>; + } // namespace sus::result::__private diff --git a/subspace/result/result.h b/subspace/result/result.h index 575fe47f5..6ad99a848 100644 --- a/subspace/result/result.h +++ b/subspace/result/result.h @@ -715,6 +715,28 @@ class [[nodiscard]] Result final { } } + /// Returns the contained Ok value or a default. + /// + /// Consumes the Result and, if it held an Ok value, the value is returned. + /// Otherwise the default value of the Ok value's type is returned. + constexpr T unwrap_or_default() && noexcept + requires(!std::is_reference_v && + (std::is_void_v || ::sus::construct::Default)) + { + ResultState was = ::sus::mem::replace(mref(state_), ResultState::IsMoved); + check_with_message( + was != ResultState::IsMoved, + *"called `Result::unwrap_or_default()` on a moved Result"); + if constexpr (!std::is_void_v) { + if (was == ResultState::IsOk) { + return ::sus::mem::take_and_destruct(::sus::marker::unsafe_fn, + mref(storage_.ok_)); + } else { + return T(); + } + } + } + /// Returns the contained `Ok` value, consuming the self value, without /// checking that the value is not an `Err`. /// diff --git a/subspace/result/result_unittest.cc b/subspace/result/result_unittest.cc index a85b706fa..333adcb9d 100644 --- a/subspace/result/result_unittest.cc +++ b/subspace/result/result_unittest.cc @@ -553,6 +553,31 @@ TEST(Result, Unwrap) { EXPECT_EQ(&cu, &m); } +TEST(Result, UnwrapOrDefault) { + { + constexpr auto a = Result::with(3_i32).unwrap_or_default(); + static_assert(std::same_as); + EXPECT_EQ(a, 3_i32); + + constexpr auto d = + Result::with_err(Error()).unwrap_or_default(); + static_assert(std::same_as); + EXPECT_EQ(d, 0_i32); + } + { + // Returns void, doesn't panic. + Result::with().unwrap_or_default(); + static_assert( + std::same_as::with().unwrap_or_default()), + void>); + // Returns void, doesn't panic. + Result::with_err(Error()).unwrap_or_default(); + static_assert( + std::same_as::with().unwrap_or_default()), + void>); + } +} + TEST(ResultDeathTest, UnwrapWithErr) { #if GTEST_HAS_DEATH_TEST auto r = Result::with_err(Error()); diff --git a/subspace/tuple/tuple_unittest.cc b/subspace/tuple/tuple_unittest.cc index 5ba513926..58c682d92 100644 --- a/subspace/tuple/tuple_unittest.cc +++ b/subspace/tuple/tuple_unittest.cc @@ -204,7 +204,7 @@ TEST(Tuple, ConstructorFunction) { } { // into() as an input to the tuple. - Tuple a = sus::tuple(1_u32, sus::into(2), 3_u32); + Tuple a = sus::tuple(1_u32, sus::into(2_u16), 3_u32); EXPECT_EQ(a.at<0>(), 1_u32); EXPECT_EQ(a.at<1>(), 2_u32); EXPECT_EQ(a.at<2>(), 3_u32);