11module ;
22
3+ #include < cmath>
34#include < compare>
45#include < concepts>
6+ #include < cstdint>
57#include < expected>
8+ #include < limits>
9+ #include < optional>
610#include < type_traits>
711#include < utility>
812
@@ -13,11 +17,9 @@ import mcpplibs.primitives.operations.dispatcher;
1317import mcpplibs.primitives.operations.impl;
1418import mcpplibs.primitives.primitive.impl;
1519import mcpplibs.primitives.primitive.traits;
16- import mcpplibs.primitives.conversion.traits;
17- import mcpplibs.primitives.conversion.underlying;
1820import mcpplibs.primitives.policy.handler;
1921import mcpplibs.primitives.policy.impl;
20- import mcpplibs.primitives.underlying.traits ;
22+ import mcpplibs.primitives.underlying;
2123
2224
2325namespace mcpplibs ::primitives::operations::details {
@@ -60,14 +62,128 @@ constexpr auto decode_three_way_code(CommonRep const &code) -> Ordering {
6062 return Ordering::equivalent;
6163}
6264
63- constexpr auto to_policy_error_kind (const conversion::risk::kind kind)
65+ enum class assign_risk : unsigned char {
66+ overflow,
67+ underflow,
68+ domain_error,
69+ precision_loss,
70+ };
71+
72+ template <typename DestRep, typename SrcRep>
73+ concept statically_castable = requires (SrcRep value) {
74+ static_cast <std::remove_cvref_t <DestRep>>(value);
75+ };
76+
77+ template <std_integer DestRep, std_integer SrcRep>
78+ constexpr auto numeric_risk (SrcRep value) -> std::optional<assign_risk> {
79+ using dest_type = std::remove_cvref_t <DestRep>;
80+ using src_type = std::remove_cvref_t <SrcRep>;
81+
82+ if constexpr (std::is_signed_v<src_type>) {
83+ auto const signed_value = static_cast <std::intmax_t >(value);
84+ if constexpr (std::is_signed_v<dest_type>) {
85+ if (signed_value <
86+ static_cast <std::intmax_t >(std::numeric_limits<dest_type>::min ())) {
87+ return assign_risk::underflow;
88+ }
89+ if (signed_value >
90+ static_cast <std::intmax_t >(std::numeric_limits<dest_type>::max ())) {
91+ return assign_risk::overflow;
92+ }
93+ } else {
94+ if (signed_value < 0 ) {
95+ return assign_risk::underflow;
96+ }
97+ if (static_cast <std::uintmax_t >(signed_value) >
98+ static_cast <std::uintmax_t >(std::numeric_limits<dest_type>::max ())) {
99+ return assign_risk::overflow;
100+ }
101+ }
102+ } else {
103+ auto const unsigned_value = static_cast <std::uintmax_t >(value);
104+ if (unsigned_value >
105+ static_cast <std::uintmax_t >(std::numeric_limits<dest_type>::max ())) {
106+ return assign_risk::overflow;
107+ }
108+ }
109+
110+ return std::nullopt ;
111+ }
112+
113+ template <std_integer DestRep, std_floating SrcRep>
114+ constexpr auto numeric_risk (SrcRep value) -> std::optional<assign_risk> {
115+ using dest_type = std::remove_cvref_t <DestRep>;
116+ using src_type = std::remove_cvref_t <SrcRep>;
117+
118+ if (std::isnan (value)) {
119+ return assign_risk::domain_error;
120+ }
121+ if (std::isinf (value)) {
122+ return value < static_cast <src_type>(0 ) ? assign_risk::underflow
123+ : assign_risk::overflow;
124+ }
125+
126+ auto const normalized = static_cast <long double >(value);
127+ auto const min_value =
128+ static_cast <long double >(std::numeric_limits<dest_type>::lowest ());
129+ auto const max_value =
130+ static_cast <long double >(std::numeric_limits<dest_type>::max ());
131+
132+ if (normalized < min_value) {
133+ return assign_risk::underflow;
134+ }
135+ if (normalized > max_value) {
136+ return assign_risk::overflow;
137+ }
138+ return std::nullopt ;
139+ }
140+
141+ template <typename DestRep, typename SrcRep>
142+ constexpr auto numeric_risk (SrcRep value) -> std::optional<assign_risk> {
143+ using dest_type = std::remove_cvref_t <DestRep>;
144+ using src_type = std::remove_cvref_t <SrcRep>;
145+
146+ if constexpr (std_integer<dest_type> && std_integer<src_type>) {
147+ return numeric_risk<dest_type, src_type>(value);
148+ } else if constexpr (std_integer<dest_type> && std_floating<src_type>) {
149+ return numeric_risk<dest_type, src_type>(value);
150+ } else {
151+ return std::nullopt ;
152+ }
153+ }
154+
155+ template <typename DestRep, typename SrcRep>
156+ requires statically_castable<DestRep, SrcRep>
157+ constexpr auto saturating_rep_cast (SrcRep value) noexcept
158+ -> std::remove_cvref_t<DestRep> {
159+ using dest_type = std::remove_cvref_t <DestRep>;
160+ using src_type = std::remove_cvref_t <SrcRep>;
161+
162+ if constexpr (std_integer<dest_type> && std_numeric<src_type>) {
163+ if (auto const kind = numeric_risk<dest_type>(value); kind.has_value ()) {
164+ if (*kind == assign_risk::overflow) {
165+ return std::numeric_limits<dest_type>::max ();
166+ }
167+ if (*kind == assign_risk::underflow) {
168+ return std::numeric_limits<dest_type>::lowest ();
169+ }
170+ if (*kind == assign_risk::domain_error) {
171+ return dest_type{};
172+ }
173+ }
174+ }
175+
176+ return static_cast <dest_type>(value);
177+ }
178+
179+ constexpr auto to_policy_error_kind (assign_risk kind)
64180 -> policy::error::kind {
65181 switch (kind) {
66- case conversion::risk::kind ::overflow:
182+ case assign_risk ::overflow:
67183 return policy::error::kind::overflow;
68- case conversion::risk::kind ::underflow:
184+ case assign_risk ::underflow:
69185 return policy::error::kind::underflow;
70- case conversion::risk::kind ::domain_error:
186+ case assign_risk ::domain_error:
71187 return policy::error::kind::domain_error;
72188 default :
73189 return policy::error::kind::unspecified;
@@ -547,7 +663,7 @@ constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs)
547663 if constexpr (std::same_as<lhs_value_policy, policy::value::checked> &&
548664 std_integer<lhs_rep> && std_numeric<common_rep>) {
549665 if (auto const kind =
550- conversion ::numeric_risk<lhs_rep>(assigned_common_rep);
666+ details ::numeric_risk<lhs_rep>(assigned_common_rep);
551667 kind.has_value ()) {
552668 return std::unexpected (
553669 details::to_error_payload<ErrorPayload>(
@@ -556,7 +672,7 @@ constexpr auto apply_assign(Lhs &lhs, Rhs const &rhs)
556672 }
557673
558674 auto const assigned_rep =
559- conversion::saturating_cast <lhs_rep>(assigned_common_rep);
675+ details::saturating_rep_cast <lhs_rep>(assigned_common_rep);
560676 lhs.store (underlying::traits<lhs_value_type>::from_rep (assigned_rep));
561677 return out;
562678}
0 commit comments