Skip to content

Commit d6a50d8

Browse files
refactor: Separate literal operators and validation logic into new literals module
Signed-off-by: FrozenlemonTee <1115306170@qq.com>
1 parent 11beede commit d6a50d8

2 files changed

Lines changed: 236 additions & 117 deletions

File tree

src/underlying/impl.cppm

Lines changed: 0 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
module;
2-
#include <concepts>
3-
#include <cstddef>
4-
#include <cstdint>
5-
#include <stdexcept>
6-
#include <limits>
72
#include <type_traits>
83

94
export module mcpplibs.primitives.underlying.impl;
@@ -38,115 +33,3 @@ struct mcpplibs::primitives::underlying::traits<T> {
3833

3934
static constexpr bool is_valid_rep(rep_type) noexcept { return true; }
4035
};
41-
42-
namespace mcpplibs::primitives::underlying::details {
43-
44-
template <std::integral T>
45-
consteval auto cast_integer_literal(unsigned long long value) -> T {
46-
if (value > static_cast<unsigned long long>(std::numeric_limits<T>::max())) {
47-
throw std::out_of_range{"integer literal is out of range for target underlying type"};
48-
}
49-
50-
return static_cast<T>(value);
51-
}
52-
53-
template <std::floating_point T>
54-
consteval auto cast_floating_literal(long double value) -> T {
55-
if (!(value < 0 || value >= 0)) {
56-
throw std::out_of_range{"floating literal must be finite"};
57-
}
58-
59-
auto const lowest = static_cast<long double>(std::numeric_limits<T>::lowest());
60-
if (auto const max = static_cast<long double>(std::numeric_limits<T>::max());
61-
value < lowest || value > max) {
62-
throw std::out_of_range{"floating literal is out of range for target underlying type"};
63-
}
64-
65-
return static_cast<T>(value);
66-
}
67-
68-
} // namespace mcpplibs::primitives::underlying::details
69-
70-
export namespace mcpplibs::primitives::literals {
71-
72-
consteval auto operator""_uchar(const char value) -> unsigned char {
73-
return static_cast<unsigned char>(value);
74-
}
75-
76-
consteval auto operator""_char8(const char8_t value) -> char8_t { return value; }
77-
78-
consteval auto operator""_char16(const char16_t value) -> char16_t { return value; }
79-
80-
consteval auto operator""_char32(const char32_t value) -> char32_t { return value; }
81-
82-
consteval auto operator""_wchar(const wchar_t value) -> wchar_t { return value; }
83-
84-
consteval auto operator""_u8(const unsigned long long value) -> std::uint8_t {
85-
return underlying::details::cast_integer_literal<std::uint8_t>(value);
86-
}
87-
88-
consteval auto operator""_u16(const unsigned long long value) -> std::uint16_t {
89-
return underlying::details::cast_integer_literal<std::uint16_t>(value);
90-
}
91-
92-
consteval auto operator""_u32(const unsigned long long value) -> std::uint32_t {
93-
return underlying::details::cast_integer_literal<std::uint32_t>(value);
94-
}
95-
96-
consteval auto operator""_u64(const unsigned long long value) -> std::uint64_t {
97-
return underlying::details::cast_integer_literal<std::uint64_t>(value);
98-
}
99-
100-
consteval auto operator""_size(const unsigned long long value) -> std::size_t {
101-
return underlying::details::cast_integer_literal<std::size_t>(value);
102-
}
103-
104-
consteval auto operator""_diff(const unsigned long long value)
105-
-> std::ptrdiff_t {
106-
return underlying::details::cast_integer_literal<std::ptrdiff_t>(value);
107-
}
108-
109-
consteval auto operator""_i8(const unsigned long long value) -> std::int8_t {
110-
return underlying::details::cast_integer_literal<std::int8_t>(value);
111-
}
112-
113-
consteval auto operator""_i16(const unsigned long long value) -> std::int16_t {
114-
return underlying::details::cast_integer_literal<std::int16_t>(value);
115-
}
116-
117-
consteval auto operator""_i32(const unsigned long long value) -> std::int32_t {
118-
return underlying::details::cast_integer_literal<std::int32_t>(value);
119-
}
120-
121-
consteval auto operator""_i64(const unsigned long long value) -> std::int64_t {
122-
return underlying::details::cast_integer_literal<std::int64_t>(value);
123-
}
124-
125-
consteval auto operator""_f32(const unsigned long long value) -> float {
126-
return underlying::details::cast_floating_literal<float>(
127-
static_cast<long double>(value));
128-
}
129-
130-
consteval auto operator""_f32(const long double value) -> float {
131-
return underlying::details::cast_floating_literal<float>(value);
132-
}
133-
134-
consteval auto operator""_f64(const unsigned long long value) -> double {
135-
return underlying::details::cast_floating_literal<double>(
136-
static_cast<long double>(value));
137-
}
138-
139-
consteval auto operator""_f64(const long double value) -> double {
140-
return underlying::details::cast_floating_literal<double>(value);
141-
}
142-
143-
consteval auto operator""_f80(const unsigned long long value) -> long double {
144-
return underlying::details::cast_floating_literal<long double>(
145-
static_cast<long double>(value));
146-
}
147-
148-
consteval auto operator""_f80(const long double value) -> long double {
149-
return underlying::details::cast_floating_literal<long double>(value);
150-
}
151-
152-
} // namespace mcpplibs::primitives::literals

src/underlying/literals.cppm

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
module;
2+
3+
#include <concepts>
4+
#include <cstddef> // NOLINT
5+
#include <cstdint>
6+
#include <limits>
7+
#include <stdexcept>
8+
#include <type_traits>
9+
10+
export module mcpplibs.primitives.underlying.literals;
11+
12+
import mcpplibs.primitives.algorithms.limits;
13+
import mcpplibs.primitives.conversion.traits;
14+
import mcpplibs.primitives.underlying;
15+
16+
namespace mcpplibs::primitives::underlying::details {
17+
18+
template <conversion::risk::kind Kind>
19+
consteval auto throw_literal_risk() -> void {
20+
if constexpr (Kind == conversion::risk::kind::overflow ||
21+
Kind == conversion::risk::kind::underflow) {
22+
throw std::out_of_range{
23+
"numeric literal is out of range for target underlying type"};
24+
} else if constexpr (Kind == conversion::risk::kind::precision_loss) {
25+
throw std::invalid_argument{
26+
"numeric literal loses precision for target underlying type"};
27+
} else if constexpr (Kind == conversion::risk::kind::domain_error) {
28+
throw std::invalid_argument{
29+
"numeric literal must be finite for target underlying type"};
30+
} else {
31+
throw std::invalid_argument{
32+
"numeric literal is not representable for target underlying type"};
33+
}
34+
}
35+
36+
template <std::floating_point T>
37+
consteval auto ordered(T value) -> bool {
38+
return (value < static_cast<T>(0)) || (value >= static_cast<T>(0));
39+
}
40+
41+
template <std::floating_point T>
42+
consteval auto finite(T value) -> bool {
43+
return ordered(value) &&
44+
value >= algorithms::lowest_value<T>() &&
45+
value <= algorithms::max_value<T>();
46+
}
47+
48+
template <std::integral To>
49+
consteval auto checked_integral_literal(unsigned long long value) -> To {
50+
using value_type = std::remove_cv_t<To>;
51+
constexpr auto max_value =
52+
static_cast<unsigned long long>(algorithms::max_value<value_type>());
53+
54+
if (value > max_value) {
55+
throw_literal_risk<conversion::risk::kind::overflow>();
56+
}
57+
58+
return static_cast<value_type>(value);
59+
}
60+
61+
template <std::floating_point To, std_numeric From>
62+
consteval auto checked_floating_literal(From value) -> To {
63+
using source_type = std::remove_cv_t<From>;
64+
using value_type = std::remove_cv_t<To>;
65+
66+
if constexpr (std_floating<source_type>) {
67+
if (!ordered(value)) {
68+
throw_literal_risk<conversion::risk::kind::domain_error>();
69+
}
70+
}
71+
72+
auto const normalized = static_cast<long double>(value);
73+
auto const lowest = static_cast<long double>(algorithms::lowest_value<value_type>());
74+
auto const max = static_cast<long double>(algorithms::max_value<value_type>());
75+
76+
if (normalized < lowest) {
77+
throw_literal_risk<conversion::risk::kind::underflow>();
78+
}
79+
if (normalized > max) {
80+
throw_literal_risk<conversion::risk::kind::overflow>();
81+
}
82+
83+
auto const converted = static_cast<value_type>(value);
84+
85+
if constexpr (std_floating<value_type>) {
86+
if (!finite(converted)) {
87+
if constexpr (std::signed_integral<source_type> || std_floating<source_type>) {
88+
if (value < static_cast<source_type>(0)) {
89+
throw_literal_risk<conversion::risk::kind::underflow>();
90+
}
91+
}
92+
throw_literal_risk<conversion::risk::kind::overflow>();
93+
}
94+
}
95+
96+
if constexpr (std::integral<source_type>) {
97+
if (static_cast<source_type>(converted) != value) {
98+
throw_literal_risk<conversion::risk::kind::precision_loss>();
99+
}
100+
} else {
101+
auto const roundtrip = static_cast<source_type>(converted);
102+
if (!ordered(roundtrip)) {
103+
throw_literal_risk<conversion::risk::kind::domain_error>();
104+
}
105+
if (roundtrip != value) {
106+
if (converted == static_cast<value_type>(0) &&
107+
value != static_cast<source_type>(0)) {
108+
throw_literal_risk<conversion::risk::kind::underflow>();
109+
}
110+
throw_literal_risk<conversion::risk::kind::precision_loss>();
111+
}
112+
}
113+
114+
return converted;
115+
}
116+
117+
template <char... Cs>
118+
consteval auto parse_unsigned_decimal_literal() -> unsigned long long {
119+
constexpr char input[] {Cs..., '\0'};
120+
constexpr auto max_value = std::numeric_limits<unsigned long long>::max();
121+
122+
unsigned long long value {};
123+
for (std::size_t i = 0; i < sizeof...(Cs); ++i) {
124+
auto const ch = input[i];
125+
if (ch < '0' || ch > '9') {
126+
throw std::invalid_argument{"invalid integer literal"};
127+
}
128+
129+
auto const digit = static_cast<unsigned long long>(ch - '0');
130+
if (value > max_value / 10ULL ||
131+
(value == max_value / 10ULL && digit > max_value % 10ULL)) {
132+
throw std::out_of_range{"integer literal is out of range"};
133+
}
134+
135+
value = value * 10ULL + digit;
136+
}
137+
138+
return value;
139+
}
140+
141+
template <std::integral To, char... Cs>
142+
consteval auto literal_integral() -> To {
143+
return checked_integral_literal<To>(parse_unsigned_decimal_literal<Cs...>());
144+
}
145+
146+
} // namespace mcpplibs::primitives::underlying::details
147+
148+
export namespace mcpplibs::primitives::literals {
149+
150+
consteval auto operator""_uchar(const char value) -> unsigned char {
151+
return static_cast<unsigned char>(value);
152+
}
153+
154+
consteval auto operator""_char8(const char8_t value) -> char8_t { return value; }
155+
156+
consteval auto operator""_char16(const char16_t value) -> char16_t { return value; }
157+
158+
consteval auto operator""_char32(const char32_t value) -> char32_t { return value; }
159+
160+
consteval auto operator""_wchar(const wchar_t value) -> wchar_t { return value; }
161+
162+
template <char... Cs>
163+
consteval auto operator""_u8() -> std::uint8_t {
164+
return underlying::details::literal_integral<std::uint8_t, Cs...>();
165+
}
166+
167+
template <char... Cs>
168+
consteval auto operator""_u16() -> std::uint16_t {
169+
return underlying::details::literal_integral<std::uint16_t, Cs...>();
170+
}
171+
172+
template <char... Cs>
173+
consteval auto operator""_u32() -> std::uint32_t {
174+
return underlying::details::literal_integral<std::uint32_t, Cs...>();
175+
}
176+
177+
template <char... Cs>
178+
consteval auto operator""_u64() -> std::uint64_t {
179+
return underlying::details::literal_integral<std::uint64_t, Cs...>();
180+
}
181+
182+
template <char... Cs>
183+
consteval auto operator""_size() -> std::size_t {
184+
return underlying::details::literal_integral<std::size_t, Cs...>();
185+
}
186+
187+
template <char... Cs>
188+
consteval auto operator""_diff() -> std::ptrdiff_t {
189+
return underlying::details::literal_integral<std::ptrdiff_t, Cs...>();
190+
}
191+
192+
template <char... Cs>
193+
consteval auto operator""_i8() -> std::int8_t {
194+
return underlying::details::literal_integral<std::int8_t, Cs...>();
195+
}
196+
197+
template <char... Cs>
198+
consteval auto operator""_i16() -> std::int16_t {
199+
return underlying::details::literal_integral<std::int16_t, Cs...>();
200+
}
201+
202+
template <char... Cs>
203+
consteval auto operator""_i32() -> std::int32_t {
204+
return underlying::details::literal_integral<std::int32_t, Cs...>();
205+
}
206+
207+
template <char... Cs>
208+
consteval auto operator""_i64() -> std::int64_t {
209+
return underlying::details::literal_integral<std::int64_t, Cs...>();
210+
}
211+
212+
consteval auto operator""_f32(const unsigned long long value) -> float {
213+
return underlying::details::checked_floating_literal<float>(value);
214+
}
215+
216+
consteval auto operator""_f32(const long double value) -> float {
217+
return underlying::details::checked_floating_literal<float>(value);
218+
}
219+
220+
consteval auto operator""_f64(const unsigned long long value) -> double {
221+
return underlying::details::checked_floating_literal<double>(value);
222+
}
223+
224+
consteval auto operator""_f64(const long double value) -> double {
225+
return underlying::details::checked_floating_literal<double>(value);
226+
}
227+
228+
consteval auto operator""_f80(const unsigned long long value) -> long double {
229+
return underlying::details::checked_floating_literal<long double>(value);
230+
}
231+
232+
consteval auto operator""_f80(const long double value) -> long double {
233+
return underlying::details::checked_floating_literal<long double>(value);
234+
}
235+
236+
} // namespace mcpplibs::primitives::literals

0 commit comments

Comments
 (0)