1313#include " gsl/gsl"
1414
1515
16+ struct UInt ;
17+
1618namespace al {
1719
1820/* A "weak number" is a standard number type. They are prone to implicit
@@ -131,14 +133,41 @@ class number_base {
131133 using value_t = T;
132134 T c_val;
133135
134- template <weak_number U> force_inline constexpr explicit
135- number_base (U const &value) noexcept (not can_narrow<T, U>) : c_val{convert_to<T>(value)} { }
136+ template <weak_number U> requires (not can_narrow<T, U>) force_inline constexpr explicit
137+ number_base(U const &value) noexcept : c_val{convert_to<T>(value)} { }
138+
139+ force_inline constexpr explicit
140+ number_base (ConstantNum<T> const &value) noexcept : c_val{value.c_val } { }
141+
142+ template <weak_number U> force_inline static constexpr
143+ auto make_from (U const &value) noexcept (not can_narrow<T, U>) -> SelfType
144+ {
145+ /* Converting to a floating point type isn't checked here because it's
146+ * nearly impossible to otherwise ensure a large enough integer or
147+ * precise enough double will correctly fit in the target type. Since
148+ * converting to floating point results in the nearest value instead of
149+ * being modulo wrapped, this should be considered fine.
150+ */
151+ if constexpr (std::floating_point<T>)
152+ return SelfType{static_cast <T>(value)};
153+ else
154+ return SelfType{convert_to<T>(value)};
155+ }
156+
157+ [[nodiscard]] static constexpr
158+ auto bit_pack (std::byte const hi, std::byte const lo) noexcept -> SelfType
159+ requires(sizeof (SelfType) == 2)
160+ {
161+ auto const ret = static_cast <std::uint16_t >((to_integer<std::uint16_t >(hi)<<8 )
162+ | to_integer<std::uint16_t >(lo));
163+ return std::bit_cast<SelfType>(ret);
164+ }
136165
137166 /* Copy assignment from another strong number type, only for types that
138167 * won't narrow.
139168 */
140- template <strong_number U> requires (not std::is_base_of_v<number_base, U>)
141- constexpr auto operator=(U const &rhs) & noexcept LIFETIMEBOUND -> number_base&
169+ template <strong_number U> requires (not std::is_base_of_v<number_base, U>) constexpr
170+ auto operator=(U const &rhs) & noexcept LIFETIMEBOUND -> number_base&
142171 {
143172 static_assert (not can_narrow<T, typename U::value_t >,
144173 " Invalid narrowing assignment; use .cast_to<U>() or .reinterpret_as<U>() to convert" );
@@ -172,11 +201,8 @@ class number_base {
172201 noexcept (not can_narrow<typename U::value_t ,T> or std::floating_point<typename U::value_t >)
173202 -> U
174203 {
175- /* Converting to a floating point type isn't checked here because it's
176- * nearly impossible to otherwise ensure a large enough integer or
177- * precise enough double will correctly fit in the target type. Since
178- * converting to floating point results in the nearest value instead of
179- * being modulo wrapped, this should be considered fine.
204+ /* Like make_from, converting to a floating point type isn't checked
205+ * here.
180206 */
181207 if constexpr (std::floating_point<typename U::value_t >)
182208 return U{static_cast <U::value_t >(c_val)};
@@ -190,6 +216,8 @@ class number_base {
190216 template <strong_number U> [[nodiscard]] constexpr
191217 auto reinterpret_as () const noexcept -> U { return U{static_cast <U::value_t >(c_val)}; }
192218
219+ [[nodiscard]] constexpr auto popcount () const noexcept -> UInt requires(std::integral<T>);
220+
193221 /* Relevant values for the given type. Offered here as static methods
194222 * instead of through a separate templated structure.
195223 */
@@ -206,15 +234,6 @@ class number_base {
206234 requires std::numeric_limits<T>::has_signaling_NaN
207235 { return SelfType{std::numeric_limits<T>::signaling_NaN ()}; }
208236
209- [[nodiscard]] static constexpr
210- auto bit_pack (std::byte const hi, std::byte const lo) noexcept -> SelfType requires (sizeof (SelfType) == 2)
211- {
212- using unsigned_t = std::make_unsigned_t <T>;
213- auto ret = static_cast <unsigned_t >((to_integer<unsigned_t >(hi)<<8 )
214- | to_integer<unsigned_t >(lo));
215- return std::bit_cast<SelfType>(ret);
216- }
217-
218237 /* Prefix and postfix increment and decrement operators. Only valid for
219238 * integral types.
220239 */
@@ -314,13 +333,15 @@ class number_base {
314333 DECL_BINARY (&)
315334 DECL_BINARY (^)
316335#undef DECL_BINARY
317- /* Binary ops >> and << between a strong number type and integer constant.
318- * The shift amount must fit into an u8 regardless of the left-side type.
336+ /* Binary ops >> and << between a strong number type and weak integer.
337+ * Unlike the other operations, these don't require the weak integer to be
338+ * constant because the result type is always the same as the left-side
339+ * operand.
319340 */
320341#define DECL_BINARY (op ) \
321- [[nodiscard]] force_inline friend constexpr \
322- auto operator op (SelfType const &lhs, ConstantNum<std:: uint8_t > const &rhs) noexcept \
323- { return SelfType{static_cast <T>(lhs.c_val op rhs. c_val )}; }
342+ template <std::unsigned_integral U> [[nodiscard]] force_inline friend \
343+ constexpr auto operator op (SelfType const &lhs, U const &rhs) noexcept \
344+ { return SelfType{static_cast <T>(lhs.c_val op rhs)}; }
324345 DECL_BINARY (>>)
325346 DECL_BINARY (<<)
326347#undef DECL_BINARY
@@ -511,16 +532,30 @@ using usize = std::size_t;
511532using f32 = float ;
512533using f64 = double ;
513534
535+ struct UInt : al::number_base<unsigned , UInt> { using number_base::number_base; using number_base::operator =; };
536+ template <typename CharT> struct al ::formatter<UInt, CharT> : UInt::formatter<CharT> { };
537+
538+ namespace al {
539+
540+ template <weak_number T, typename SelfType>
541+ requires (not std::is_const_v<T> and not std::is_volatile_v<T>) [[nodiscard]] constexpr
542+ auto number_base<T,SelfType>::popcount() const noexcept -> UInt requires (std::integral<T>)
543+ {
544+ using unsigned_t = std::make_unsigned_t <T>;
545+ return UInt{static_cast <unsigned >(std::popcount (static_cast <unsigned_t >(c_val)))};
546+ }
547+
548+ } /* namespace al */
514549
515550[[nodiscard]] consteval
516- auto operator " " _i8(unsigned long long const n) noexcept { return i8 {n} ; }
551+ auto operator " " _i8(unsigned long long const n) noexcept { return i8::make_from (n) ; }
517552[[nodiscard]] consteval
518- auto operator " " _u8(unsigned long long const n) noexcept { return u8 {n} ; }
553+ auto operator " " _u8(unsigned long long const n) noexcept { return u8::make_from (n) ; }
519554
520555[[nodiscard]] consteval
521- auto operator " " _i16(unsigned long long const n) noexcept { return i16 {n} ; }
556+ auto operator " " _i16(unsigned long long const n) noexcept { return i16::make_from (n) ; }
522557[[nodiscard]] consteval
523- auto operator " " _u16(unsigned long long const n) noexcept { return u16 {n} ; }
558+ auto operator " " _u16(unsigned long long const n) noexcept { return u16::make_from (n) ; }
524559
525560[[nodiscard]] consteval
526561auto operator " " _i32(unsigned long long const n) noexcept { return gsl::narrow<i32 >(n); }
0 commit comments