Skip to content

Commit 4d89252

Browse files
Merge pull request #20 from mcpplibs/refactor-alias-factory-literals
Add literal operators, factory functions, and tests for primitives
2 parents bb5e527 + 0041343 commit 4d89252

File tree

7 files changed

+509
-10
lines changed

7 files changed

+509
-10
lines changed

src/primitive/impl.cppm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module;
2+
#include <cstddef>
23
#include <cstdint>
34
#include <tuple>
45
#include <type_traits>
@@ -234,6 +235,20 @@ private:
234235
value_type value_;
235236
};
236237

238+
template <policy::policy_type... Policies, underlying_type T>
239+
constexpr auto with(T value) noexcept(
240+
noexcept(primitive<std::remove_cv_t<T>, Policies...>{value}))
241+
-> primitive<std::remove_cv_t<T>, Policies...> {
242+
return primitive<std::remove_cv_t<T>, Policies...>{value};
243+
}
244+
245+
template <policy::policy_type... Policies, underlying_type T>
246+
constexpr auto with(std::tuple<Policies...>, T value) noexcept(
247+
noexcept(primitive<std::remove_cv_t<T>, Policies...>{value}))
248+
-> primitive<std::remove_cv_t<T>, Policies...> {
249+
return primitive<std::remove_cv_t<T>, Policies...>{value};
250+
}
251+
237252
namespace types {
238253
template <policy::policy_type... Policies>
239254
using Bool = primitive<bool, Policies...>;
@@ -258,6 +273,10 @@ using U32 = primitive<std::uint32_t, Policies...>;
258273
template <policy::policy_type... Policies>
259274
using U64 = primitive<std::uint64_t, Policies...>;
260275
template <policy::policy_type... Policies>
276+
using Size = primitive<std::size_t, Policies...>;
277+
template <policy::policy_type... Policies>
278+
using Diff = primitive<std::ptrdiff_t, Policies...>;
279+
template <policy::policy_type... Policies>
261280
using I8 = primitive<std::int8_t, Policies...>;
262281
template <policy::policy_type... Policies>
263282
using I16 = primitive<std::int16_t, Policies...>;

src/primitive/traits.cppm

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace mcpplibs::primitives::meta::details {
1313

1414
template <typename T> struct primitive_traits_impl;
1515

16+
template <typename T, typename PoliciesTuple> struct make_primitive;
17+
1618
template <underlying_type T, policy::policy_type... Policies>
1719
struct primitive_traits_impl<primitive<T, Policies...>> {
1820
using value_type = T;
@@ -27,21 +29,19 @@ struct primitive_traits_impl<primitive<T, Policies...>> {
2729
policy::resolve_policy_t<policy::category::concurrency, Policies...>;
2830
};
2931

32+
template <underlying_type T, policy::policy_type... Policies>
33+
struct make_primitive<T, std::tuple<Policies...>> {
34+
using type = primitive<T, Policies...>;
35+
};
36+
3037
} // namespace mcpplibs::primitives::meta::details
3138

3239
// Public API exported from this module.
3340
export namespace mcpplibs::primitives::meta {
3441
using policy_category = policy::category;
3542

36-
template <typename T, typename PoliciesTuple> struct make_primitive;
37-
38-
template <underlying_type T, policy::policy_type... Policies>
39-
struct make_primitive<T, std::tuple<Policies...>> {
40-
using type = primitive<T, Policies...>;
41-
};
42-
4343
template <underlying_type T, typename PoliciesTuple>
44-
using make_primitive_t = make_primitive<T, PoliciesTuple>::type;
44+
using make_primitive_t = details::make_primitive<T, PoliciesTuple>::type;
4545

4646
using default_policies =
4747
std::tuple<policy::resolve_policy_t<policy::category::value>,

src/primitives.cppm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export import mcpplibs.primitives.primitive;
88
export import mcpplibs.primitives.operations;
99
export import mcpplibs.primitives.algorithms;
1010
export import mcpplibs.primitives.conversion;
11+
export import mcpplibs.primitives.underlying.literals;

src/underlying/impl.cppm

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,3 @@ struct mcpplibs::primitives::underlying::traits<T> {
3333

3434
static constexpr bool is_valid_rep(rep_type) noexcept { return true; }
3535
};
36-
37-

src/underlying/literals.cppm

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
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 <typename To, typename From>
49+
consteval auto out_of_floating_range(From value) -> bool {
50+
using value_type = std::remove_cv_t<To>;
51+
auto const normalized = static_cast<long double>(value);
52+
auto const lowest =
53+
static_cast<long double>(algorithms::lowest_value<value_type>());
54+
auto const max = static_cast<long double>(algorithms::max_value<value_type>());
55+
return normalized < lowest || normalized > max;
56+
}
57+
58+
template <std::integral To>
59+
consteval auto checked_integral_literal(unsigned long long value) -> To {
60+
using value_type = std::remove_cv_t<To>;
61+
constexpr auto max_value =
62+
static_cast<unsigned long long>(algorithms::max_value<value_type>());
63+
64+
if (value > max_value) {
65+
throw_literal_risk<conversion::risk::kind::overflow>();
66+
}
67+
68+
return static_cast<value_type>(value);
69+
}
70+
71+
template <std::floating_point To, std_numeric From>
72+
consteval auto checked_floating_literal(From value) -> To {
73+
using source_type = std::remove_cv_t<From>;
74+
using value_type = std::remove_cv_t<To>;
75+
76+
if constexpr (std_floating<source_type>) {
77+
if (!ordered(value)) {
78+
throw_literal_risk<conversion::risk::kind::domain_error>();
79+
}
80+
}
81+
82+
if (out_of_floating_range<value_type>(value)) {
83+
if constexpr (std::signed_integral<source_type> || std_floating<source_type>) {
84+
if (value < static_cast<source_type>(0)) {
85+
throw_literal_risk<conversion::risk::kind::underflow>();
86+
}
87+
}
88+
throw_literal_risk<conversion::risk::kind::overflow>();
89+
}
90+
91+
auto const converted = static_cast<value_type>(value);
92+
93+
if constexpr (std_floating<value_type>) {
94+
if (!finite(converted)) {
95+
if constexpr (std::signed_integral<source_type> || std_floating<source_type>) {
96+
if (value < static_cast<source_type>(0)) {
97+
throw_literal_risk<conversion::risk::kind::underflow>();
98+
}
99+
}
100+
throw_literal_risk<conversion::risk::kind::overflow>();
101+
}
102+
}
103+
104+
return converted;
105+
}
106+
107+
template <std::floating_point To, std_numeric From>
108+
consteval auto exact_floating_literal(From value) -> To {
109+
using source_type = std::remove_cv_t<From>;
110+
using value_type = std::remove_cv_t<To>;
111+
112+
auto const converted = checked_floating_literal<value_type>(value);
113+
114+
if constexpr (std::integral<source_type>) {
115+
if (static_cast<source_type>(converted) != value) {
116+
throw_literal_risk<conversion::risk::kind::precision_loss>();
117+
}
118+
} else {
119+
auto const roundtrip = static_cast<source_type>(converted);
120+
if (!ordered(roundtrip)) {
121+
throw_literal_risk<conversion::risk::kind::domain_error>();
122+
}
123+
if (roundtrip != value) {
124+
if (converted == static_cast<value_type>(0) &&
125+
value != static_cast<source_type>(0)) {
126+
throw_literal_risk<conversion::risk::kind::underflow>();
127+
}
128+
throw_literal_risk<conversion::risk::kind::precision_loss>();
129+
}
130+
}
131+
132+
return converted;
133+
}
134+
135+
template <char... Cs>
136+
consteval auto parse_unsigned_decimal_literal() -> unsigned long long {
137+
constexpr char input[] {Cs..., '\0'};
138+
constexpr auto max_value = std::numeric_limits<unsigned long long>::max();
139+
140+
unsigned long long value {};
141+
for (std::size_t i = 0; i < sizeof...(Cs); ++i) {
142+
auto const ch = input[i];
143+
if (ch < '0' || ch > '9') {
144+
throw std::invalid_argument{"invalid integer literal"};
145+
}
146+
147+
auto const digit = static_cast<unsigned long long>(ch - '0');
148+
if (value > max_value / 10ULL ||
149+
(value == max_value / 10ULL && digit > max_value % 10ULL)) {
150+
throw std::out_of_range{"integer literal is out of range"};
151+
}
152+
153+
value = value * 10ULL + digit;
154+
}
155+
156+
return value;
157+
}
158+
159+
template <std::integral To, char... Cs>
160+
consteval auto literal_integral() -> To {
161+
return checked_integral_literal<To>(parse_unsigned_decimal_literal<Cs...>());
162+
}
163+
164+
} // namespace mcpplibs::primitives::underlying::details
165+
166+
export namespace mcpplibs::primitives::literals {
167+
168+
consteval auto operator""_uchar(const char value) -> unsigned char {
169+
return static_cast<unsigned char>(value);
170+
}
171+
172+
consteval auto operator""_char8(const char8_t value) -> char8_t { return value; }
173+
174+
consteval auto operator""_char16(const char16_t value) -> char16_t { return value; }
175+
176+
consteval auto operator""_char32(const char32_t value) -> char32_t { return value; }
177+
178+
consteval auto operator""_wchar(const wchar_t value) -> wchar_t { return value; }
179+
180+
template <char... Cs>
181+
consteval auto operator""_u8() -> std::uint8_t {
182+
return underlying::details::literal_integral<std::uint8_t, Cs...>();
183+
}
184+
185+
template <char... Cs>
186+
consteval auto operator""_u16() -> std::uint16_t {
187+
return underlying::details::literal_integral<std::uint16_t, Cs...>();
188+
}
189+
190+
template <char... Cs>
191+
consteval auto operator""_u32() -> std::uint32_t {
192+
return underlying::details::literal_integral<std::uint32_t, Cs...>();
193+
}
194+
195+
template <char... Cs>
196+
consteval auto operator""_u64() -> std::uint64_t {
197+
return underlying::details::literal_integral<std::uint64_t, Cs...>();
198+
}
199+
200+
template <char... Cs>
201+
consteval auto operator""_size() -> std::size_t {
202+
return underlying::details::literal_integral<std::size_t, Cs...>();
203+
}
204+
205+
template <char... Cs>
206+
consteval auto operator""_diff() -> std::ptrdiff_t {
207+
return underlying::details::literal_integral<std::ptrdiff_t, Cs...>();
208+
}
209+
210+
template <char... Cs>
211+
consteval auto operator""_i8() -> std::int8_t {
212+
return underlying::details::literal_integral<std::int8_t, Cs...>();
213+
}
214+
215+
template <char... Cs>
216+
consteval auto operator""_i16() -> std::int16_t {
217+
return underlying::details::literal_integral<std::int16_t, Cs...>();
218+
}
219+
220+
template <char... Cs>
221+
consteval auto operator""_i32() -> std::int32_t {
222+
return underlying::details::literal_integral<std::int32_t, Cs...>();
223+
}
224+
225+
template <char... Cs>
226+
consteval auto operator""_i64() -> std::int64_t {
227+
return underlying::details::literal_integral<std::int64_t, Cs...>();
228+
}
229+
230+
consteval auto operator""_f32(const unsigned long long value) -> float {
231+
return underlying::details::checked_floating_literal<float>(value);
232+
}
233+
234+
consteval auto operator""_f32(const long double value) -> float {
235+
return underlying::details::checked_floating_literal<float>(value);
236+
}
237+
238+
consteval auto operator""_f32e(const unsigned long long value) -> float {
239+
return underlying::details::exact_floating_literal<float>(value);
240+
}
241+
242+
consteval auto operator""_f32e(const long double value) -> float {
243+
return underlying::details::exact_floating_literal<float>(value);
244+
}
245+
246+
consteval auto operator""_f64(const unsigned long long value) -> double {
247+
return underlying::details::checked_floating_literal<double>(value);
248+
}
249+
250+
consteval auto operator""_f64(const long double value) -> double {
251+
return underlying::details::checked_floating_literal<double>(value);
252+
}
253+
254+
consteval auto operator""_f64e(const unsigned long long value) -> double {
255+
return underlying::details::exact_floating_literal<double>(value);
256+
}
257+
258+
consteval auto operator""_f64e(const long double value) -> double {
259+
return underlying::details::exact_floating_literal<double>(value);
260+
}
261+
262+
consteval auto operator""_f80(const unsigned long long value) -> long double {
263+
return underlying::details::checked_floating_literal<long double>(value);
264+
}
265+
266+
consteval auto operator""_f80(const long double value) -> long double {
267+
return underlying::details::checked_floating_literal<long double>(value);
268+
}
269+
270+
consteval auto operator""_f80e(const unsigned long long value) -> long double {
271+
return underlying::details::exact_floating_literal<long double>(value);
272+
}
273+
274+
consteval auto operator""_f80e(const long double value) -> long double {
275+
return underlying::details::exact_floating_literal<long double>(value);
276+
}
277+
278+
} // namespace mcpplibs::primitives::literals

0 commit comments

Comments
 (0)