Skip to content

Commit 42d66a7

Browse files
committed
✨ Allow _c UDL to work with hex, octal, binary literals
1 parent f2d7d7d commit 42d66a7

File tree

3 files changed

+92
-20
lines changed

3 files changed

+92
-20
lines changed

include/stdx/tuple.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ template <std::size_t I> constexpr static index_constant<I> index{};
1919

2020
inline namespace literals {
2121
template <char... Chars> CONSTEVAL auto operator""_idx() {
22-
return index<detail::decimal<std::size_t, Chars...>()>;
22+
return index<parse_literal<std::size_t, Chars...>()>;
2323
}
2424
} // namespace literals
2525

include/stdx/udls.hpp

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,75 @@ namespace stdx {
1111
inline namespace v1 {
1212

1313
namespace detail {
14+
template <char C> constexpr static bool is_digit_sep_v = C == '\'';
1415
template <char C>
1516
constexpr static bool is_decimal_digit_v = C >= '0' and C <= '9';
16-
template <char C> constexpr static bool is_digit_sep_v = C == '\'';
17+
template <char C>
18+
constexpr static bool is_octal_digit_v = C >= '0' and C <= '7';
19+
template <char C>
20+
constexpr static bool is_binary_digit_v = C >= '0' and C <= '1';
1721

18-
template <char C, typename Sum> CONSTEVAL auto maybe_add_digit(Sum s) {
19-
if constexpr (is_decimal_digit_v<C>) {
20-
s *= 10;
21-
s += C - '0';
22+
template <char C>
23+
constexpr static char force_lower_case = static_cast<unsigned char>(C) | 32u;
24+
template <char C>
25+
constexpr static bool is_hex_digit_v =
26+
(C >= '0' and C <= '9') or
27+
(force_lower_case<C> >= 'a' and force_lower_case<C> <= 'f');
28+
29+
template <char C>
30+
constexpr static auto integral_value_v =
31+
is_decimal_digit_v<C> ? C - '0' : force_lower_case<C> - 'a' + 10;
32+
33+
template <auto Base, char C, typename Sum>
34+
CONSTEVAL auto maybe_add_digit(Sum s) {
35+
if constexpr (not is_digit_sep_v<C>) {
36+
s *= Base;
37+
s += integral_value_v<C>;
2238
}
2339
return s;
2440
}
2541

26-
template <typename T, char... Chars> CONSTEVAL auto decimal() -> T {
27-
static_assert(
28-
(... and (is_decimal_digit_v<Chars> or is_digit_sep_v<Chars>)),
29-
"decimal numbers only are supported");
30-
using U = decltype(stdx::to_underlying(std::declval<T>()));
42+
template <auto Base, char... Cs> struct raw_parser {
43+
template <typename T> CONSTEVAL static auto parse() {
44+
using U = decltype(stdx::to_underlying(std::declval<T>()));
45+
auto x = U{};
46+
((x = maybe_add_digit<Base, Cs>(x)), ...);
47+
return T{x};
48+
}
49+
};
50+
51+
template <char... Cs> struct parser : raw_parser<10, Cs...> {
52+
static_assert((... and (is_decimal_digit_v<Cs> or is_digit_sep_v<Cs>)));
53+
};
54+
55+
template <char... Cs> struct parser<'0', Cs...> : raw_parser<8, Cs...> {
56+
static_assert((... and (is_octal_digit_v<Cs> or is_digit_sep_v<Cs>)));
57+
};
58+
59+
template <char... Cs> struct parser<'0', 'x', Cs...> : raw_parser<16, Cs...> {
60+
static_assert((... and (is_hex_digit_v<Cs> or is_digit_sep_v<Cs>)));
61+
};
62+
template <char... Cs>
63+
struct parser<'0', 'X', Cs...> : parser<'0', 'x', Cs...> {};
64+
65+
template <char... Cs> struct parser<'0', 'b', Cs...> : raw_parser<2, Cs...> {
66+
static_assert((... and (is_binary_digit_v<Cs> or is_digit_sep_v<Cs>)));
67+
};
68+
template <char... Cs>
69+
struct parser<'0', 'B', Cs...> : parser<'0', 'b', Cs...> {};
70+
} // namespace detail
3171

32-
auto x = U{};
33-
((x = maybe_add_digit<Chars>(x)), ...);
34-
return T{x};
72+
template <typename T, char... Chars> CONSTEVAL auto parse_literal() -> T {
73+
using parser_t = detail::parser<Chars...>;
74+
return parser_t::template parse<T>();
3575
}
36-
} // namespace detail
3776

3877
template <auto I> using constant = std::integral_constant<decltype(I), I>;
3978
template <auto I> constexpr static constant<I> _c{};
4079

4180
inline namespace literals {
4281
template <char... Chars> CONSTEVAL auto operator""_c() {
43-
return _c<detail::decimal<std::uint32_t, Chars...>()>;
82+
return _c<parse_literal<std::uint32_t, Chars...>()>;
4483
}
4584
} // namespace literals
4685

test/udls.cpp

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,52 @@ TEST_CASE("compile-time named small indices", "[units]") {
4242
}
4343

4444
TEST_CASE("compile-time constant", "[units]") {
45-
using namespace stdx;
46-
static_assert(
47-
std::is_same_v<decltype(_c<0>), std::integral_constant<int, 0> const>);
45+
static_assert(std::is_same_v<decltype(stdx::_c<0>),
46+
std::integral_constant<int, 0> const>);
4847
static_assert(
49-
std::is_same_v<decltype(_c<0u>),
48+
std::is_same_v<decltype(stdx::_c<0u>),
5049
std::integral_constant<unsigned int, 0> const>);
50+
}
51+
52+
TEST_CASE("compile-time literal (decimal)", "[units]") {
53+
using namespace stdx::literals;
5154
static_assert(std::is_same_v<decltype(0_c),
5255
std::integral_constant<std::uint32_t, 0>>);
56+
}
57+
58+
TEST_CASE("compile-time literal supports digit separators", "[units]") {
59+
using namespace stdx::literals;
5360
static_assert(
5461
std::is_same_v<decltype(123'456_c),
5562
std::integral_constant<std::uint32_t, 123'456>>);
5663
}
5764

65+
TEST_CASE("compile-time literal (octal)", "[units]") {
66+
using namespace stdx::literals;
67+
static_assert(std::is_same_v<decltype(010_c),
68+
std::integral_constant<std::uint32_t, 8>>);
69+
static_assert(std::is_same_v<decltype(0'10_c),
70+
std::integral_constant<std::uint32_t, 8>>);
71+
}
72+
73+
TEST_CASE("compile-time literal (binary)", "[units]") {
74+
using namespace stdx::literals;
75+
static_assert(std::is_same_v<decltype(0b11_c),
76+
std::integral_constant<std::uint32_t, 3>>);
77+
static_assert(std::is_same_v<decltype(0b1'1_c),
78+
std::integral_constant<std::uint32_t, 3>>);
79+
}
80+
81+
TEST_CASE("compile-time literal (hex)", "[units]") {
82+
using namespace stdx::literals;
83+
static_assert(std::is_same_v<decltype(0xaa_c),
84+
std::integral_constant<std::uint32_t, 170>>);
85+
static_assert(std::is_same_v<decltype(0xAA_c),
86+
std::integral_constant<std::uint32_t, 170>>);
87+
static_assert(std::is_same_v<decltype(0xA'a_c),
88+
std::integral_constant<std::uint32_t, 170>>);
89+
}
90+
5891
namespace {
5992
enum UnscopedEnum { Value3 = 3 };
6093
enum struct ScopedEnum : char { Value5 = 5 };

0 commit comments

Comments
 (0)