-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathunderlying.cppm
More file actions
365 lines (313 loc) · 12.6 KB
/
Copy pathunderlying.cppm
File metadata and controls
365 lines (313 loc) · 12.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
module;
#include <cmath>
#include <cstdint>
#include <expected>
#include <optional>
#include <type_traits>
export module mcpplibs.primitives.conversion.underlying;
import mcpplibs.primitives.algorithms.limits;
import mcpplibs.primitives.conversion.traits;
import mcpplibs.primitives.underlying;
namespace mcpplibs::primitives::conversion::details {
template <typename DestRep, typename SrcRep>
concept statically_castable = requires(SrcRep value) {
static_cast<std::remove_cvref_t<DestRep>>(value);
};
template <underlying_type T>
using underlying_rep_t = underlying::traits<std::remove_cv_t<T>>::rep_type;
template <typename Rep, typename Candidate>
concept builtin_numeric_proxy_candidate =
std_numeric<Candidate> && has_common_rep<Rep, Candidate> &&
!std::same_as<common_rep_t<Rep, Candidate>, void> &&
std::same_as<common_rep_t<Rep, Candidate>, std::remove_cv_t<Candidate>> &&
statically_castable<Candidate, Rep>;
template <typename Rep, typename... Candidates>
struct builtin_numeric_proxy;
template <typename Rep>
struct builtin_numeric_proxy<Rep> {
using type = void;
};
template <typename Rep, typename Candidate, typename... Candidates>
struct builtin_numeric_proxy<Rep, Candidate, Candidates...> {
using type = std::conditional_t<
builtin_numeric_proxy_candidate<Rep, Candidate>, Candidate,
typename builtin_numeric_proxy<Rep, Candidates...>::type>;
};
template <typename Rep>
using integer_builtin_proxy_t =
builtin_numeric_proxy<Rep, short, unsigned short, int,
unsigned int, long, unsigned long,
long long, unsigned long long>::type;
template <typename Rep>
using floating_builtin_proxy_t =
builtin_numeric_proxy<Rep, float, double, long double>::type;
template <std_numeric DestRep, std_numeric SrcRep>
constexpr auto numeric_risk(SrcRep value)
-> std::optional<risk::kind> {
using dest_type = std::remove_cvref_t<DestRep>;
using src_type = std::remove_cvref_t<SrcRep>;
if constexpr (std_integer<dest_type> && std_integer<src_type>) {
if constexpr (std::is_signed_v<src_type>) {
auto const signed_value = static_cast<std::intmax_t>(value);
if constexpr (std::is_signed_v<dest_type>) {
if (signed_value <
static_cast<std::intmax_t>(algorithms::min_value<dest_type>())) {
return risk::kind::underflow;
}
if (signed_value >
static_cast<std::intmax_t>(algorithms::max_value<dest_type>())) {
return risk::kind::overflow;
}
return std::nullopt;
} else {
if (signed_value < 0) {
return risk::kind::underflow;
}
if (static_cast<std::uintmax_t>(signed_value) >
static_cast<std::uintmax_t>(
algorithms::max_value<dest_type>())) {
return risk::kind::overflow;
}
return std::nullopt;
}
} else {
auto const unsigned_value = static_cast<std::uintmax_t>(value);
if (unsigned_value >
static_cast<std::uintmax_t>(algorithms::max_value<dest_type>())) {
return risk::kind::overflow;
}
return std::nullopt;
}
} else if constexpr (std_integer<dest_type> && std_floating<src_type>) {
if (std::isnan(value)) {
return risk::kind::domain_error;
}
if (std::isinf(value)) {
return value < static_cast<src_type>(0) ? risk::kind::underflow
: risk::kind::overflow;
}
auto const normalized = static_cast<long double>(value);
auto const min_value =
static_cast<long double>(algorithms::lowest_value<dest_type>());
auto const max_value =
static_cast<long double>(algorithms::max_value<dest_type>());
if (normalized < min_value) {
return risk::kind::underflow;
}
if (normalized > max_value) {
return risk::kind::overflow;
}
} else if constexpr (std_floating<dest_type> && std_integer<src_type>) {
auto const converted = static_cast<dest_type>(value);
if (std::isinf(converted)) {
return value < static_cast<src_type>(0) ? risk::kind::underflow
: risk::kind::overflow;
}
auto const roundtrip = static_cast<src_type>(converted);
if (roundtrip != value) {
return risk::kind::precision_loss;
}
} else {
if (std::isnan(value)) {
return std::nullopt;
}
if (std::isinf(value)) {
return std::nullopt;
}
auto const normalized = static_cast<long double>(value);
auto const min_value =
static_cast<long double>(algorithms::lowest_value<dest_type>());
auto const max_value =
static_cast<long double>(algorithms::max_value<dest_type>());
if (normalized < min_value) {
return risk::kind::underflow;
}
if (normalized > max_value) {
return risk::kind::overflow;
}
auto const converted = static_cast<dest_type>(value);
auto const roundtrip = static_cast<std::remove_cvref_t<SrcRep>>(converted);
if (roundtrip != value) {
return risk::kind::precision_loss;
}
}
return std::nullopt;
}
template <numeric_underlying_type Dest, numeric_underlying_type Src>
constexpr auto numeric_underlying_risk(Src value)
-> std::optional<risk::kind> {
using src_type = std::remove_cv_t<Src>;
using dest_type = std::remove_cv_t<Dest>;
using src_rep_type = underlying_rep_t<src_type>;
using dest_rep_type = underlying_rep_t<dest_type>;
using src_builtin_rep_type = std::conditional_t<
integer_underlying_type<src_type>, integer_builtin_proxy_t<src_rep_type>,
floating_builtin_proxy_t<src_rep_type>>;
using dest_builtin_rep_type = std::conditional_t<
integer_underlying_type<dest_type>,
integer_builtin_proxy_t<dest_rep_type>,
floating_builtin_proxy_t<dest_rep_type>>;
if constexpr (std::same_as<src_builtin_rep_type, void> ||
std::same_as<dest_builtin_rep_type, void>) {
static_cast<void>(value);
return risk::kind::invalid_type_combination;
} else {
auto const source_rep = underlying::traits<src_type>::to_rep(value);
return numeric_risk<dest_builtin_rep_type>(
static_cast<src_builtin_rep_type>(source_rep));
}
}
template <numeric_underlying_type DestRep, numeric_underlying_type SrcRep>
requires statically_castable<DestRep, SrcRep>
constexpr auto unchecked_rep_cast(SrcRep value) noexcept
-> std::remove_cvref_t<DestRep> {
using dest_type = std::remove_cvref_t<DestRep>;
return static_cast<dest_type>(value);
}
template <numeric_underlying_type DestRep, numeric_underlying_type SrcRep>
requires statically_castable<DestRep, SrcRep>
constexpr auto checked_rep_cast(SrcRep value)
-> cast_result<std::remove_cvref_t<DestRep>> {
using dest_type = std::remove_cvref_t<DestRep>;
using src_type = std::remove_cvref_t<SrcRep>;
if constexpr (std_numeric<dest_type> && std_numeric<src_type>) {
if (auto const kind = numeric_risk<dest_type>(value); kind.has_value()) {
return std::unexpected(*kind);
}
}
return static_cast<dest_type>(value);
}
template <numeric_underlying_type DestRep, numeric_underlying_type SrcRep>
requires statically_castable<DestRep, SrcRep>
constexpr auto saturating_rep_cast(SrcRep value) noexcept
-> std::remove_cvref_t<DestRep> {
using dest_type = std::remove_cvref_t<DestRep>;
using src_type = std::remove_cvref_t<SrcRep>;
if constexpr (std_numeric<dest_type> && std_numeric<src_type>) {
if (auto const kind = numeric_risk<dest_type>(value); kind.has_value()) {
if (*kind == risk::kind::overflow) {
return algorithms::max_value<dest_type>();
}
if (*kind == risk::kind::underflow) {
return algorithms::lowest_value<dest_type>();
}
if (*kind == risk::kind::domain_error) {
return dest_type{};
}
}
}
return static_cast<dest_type>(value);
}
template <numeric_underlying_type DestRep, numeric_underlying_type SrcRep>
requires statically_castable<DestRep, SrcRep>
constexpr auto truncating_rep_cast(SrcRep value) noexcept
-> std::remove_cvref_t<DestRep> {
using dest_type = std::remove_cvref_t<DestRep>;
using src_type = std::remove_cvref_t<SrcRep>;
if constexpr (std_integer<dest_type> && std_floating<src_type>) {
if (std::isnan(value)) {
return dest_type{};
}
if (std::isinf(value)) {
return value < static_cast<src_type>(0)
? algorithms::lowest_value<dest_type>()
: algorithms::max_value<dest_type>();
}
auto const normalized = static_cast<long double>(value);
auto const min_value =
static_cast<long double>(algorithms::lowest_value<dest_type>());
auto const max_value =
static_cast<long double>(algorithms::max_value<dest_type>());
if (normalized < min_value) {
return algorithms::lowest_value<dest_type>();
}
if (normalized > max_value) {
return algorithms::max_value<dest_type>();
}
}
return static_cast<dest_type>(value);
}
template <numeric_underlying_type DestRep, numeric_underlying_type SrcRep>
requires statically_castable<DestRep, SrcRep>
constexpr auto exact_rep_cast(SrcRep value)
-> cast_result<std::remove_cvref_t<DestRep>> {
using dest_type = std::remove_cvref_t<DestRep>;
using src_type = std::remove_cvref_t<SrcRep>;
if constexpr (!(std_numeric<dest_type> && std_numeric<src_type>)) {
return std::unexpected(risk::kind::invalid_type_combination);
} else {
if (auto const kind = numeric_risk<dest_type>(value); kind.has_value()) {
return std::unexpected(*kind);
}
return static_cast<dest_type>(value);
}
}
template <underlying_type Dest, underlying_type Src, typename RepCaster>
constexpr auto cast_underlying_value(Src value, RepCaster rep_caster) -> Dest {
using src_type = std::remove_cv_t<Src>;
using dest_type = std::remove_cv_t<Dest>;
using src_rep_type = underlying::traits<src_type>::rep_type;
using dest_rep_type = underlying::traits<dest_type>::rep_type;
auto const source_rep = underlying::traits<src_type>::to_rep(value);
auto const target_rep = rep_caster.template operator()<dest_rep_type>(
static_cast<src_rep_type>(source_rep));
return underlying::traits<dest_type>::from_rep(target_rep);
}
template <underlying_type Dest, underlying_type Src, typename RepCaster>
constexpr auto cast_underlying_result(Src value, RepCaster rep_caster)
-> cast_result<Dest> {
using src_type = std::remove_cv_t<Src>;
using dest_type = std::remove_cv_t<Dest>;
using src_rep_type = underlying::traits<src_type>::rep_type;
using dest_rep_type = underlying::traits<dest_type>::rep_type;
auto const source_rep = underlying::traits<src_type>::to_rep(value);
auto const target_rep = rep_caster.template operator()<dest_rep_type>(
static_cast<src_rep_type>(source_rep));
if (!target_rep.has_value()) {
return std::unexpected(target_rep.error());
}
return underlying::traits<dest_type>::from_rep(*target_rep);
}
} // namespace mcpplibs::primitives::conversion::details
export namespace mcpplibs::primitives::conversion {
template <numeric_underlying_type DestRep, numeric_underlying_type SrcRep>
constexpr auto numeric_risk(SrcRep value)
-> std::optional<risk::kind> {
return details::numeric_underlying_risk<DestRep>(value);
}
template <underlying_type Dest, underlying_type Src>
constexpr auto unchecked_cast(Src value) noexcept -> Dest {
return details::cast_underlying_value<Dest>(
value, []<typename DestRep, typename SrcRep>(SrcRep rep) {
return details::unchecked_rep_cast<DestRep>(rep);
});
}
template <underlying_type Dest, underlying_type Src>
constexpr auto checked_cast(Src value) -> cast_result<Dest> {
return details::cast_underlying_result<Dest>(
value, []<typename DestRep, typename SrcRep>(SrcRep rep) {
return details::checked_rep_cast<DestRep>(rep);
});
}
template <underlying_type Dest, underlying_type Src>
constexpr auto saturating_cast(Src value) noexcept -> Dest {
return details::cast_underlying_value<Dest>(
value, []<typename DestRep, typename SrcRep>(SrcRep rep) {
return details::saturating_rep_cast<DestRep>(rep);
});
}
template <underlying_type Dest, underlying_type Src>
constexpr auto truncating_cast(Src value) noexcept -> Dest {
return details::cast_underlying_value<Dest>(
value, []<typename DestRep, typename SrcRep>(SrcRep rep) {
return details::truncating_rep_cast<DestRep>(rep);
});
}
template <underlying_type Dest, underlying_type Src>
constexpr auto exact_cast(Src value) -> cast_result<Dest> {
return details::cast_underlying_result<Dest>(
value, []<typename DestRep, typename SrcRep>(SrcRep rep) {
return details::exact_rep_cast<DestRep>(rep);
});
}
} // namespace mcpplibs::primitives::conversion