Skip to content

Commit c991123

Browse files
committed
enum serialization
1 parent dfd8aba commit c991123

File tree

13 files changed

+278
-123
lines changed

13 files changed

+278
-123
lines changed

include/rsl/_impl/format/fmt_parser.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
#include <rsl/string_view>
1010
#include <rsl/span>
11+
#include <rsl/serialize>
1112

12-
#include <rsl/_impl/util/to_string.hpp>
1313
#include <rsl/_impl/parser.hpp>
1414

1515
#include "accessor.hpp"
@@ -119,7 +119,7 @@ struct Replacement final : _impl::Parser {
119119
}
120120

121121
out.string += '{';
122-
out.string += _impl::utos(index);
122+
out.string += to_string(index);
123123
if (!specs.empty()) {
124124
out.string += ":";
125125
out.string += specs;
@@ -189,7 +189,7 @@ struct Replacement final : _impl::Parser {
189189
type = remove_cvref(type);
190190
std::meta::info accessor{};
191191
if (std::ranges::all_of(subfield, _impl::is_digit)) {
192-
index = _impl::stou(std::string_view{subfield});
192+
index = _serialize_impl::stou(std::string_view{subfield});
193193
if (is_subscriptable(type)) {
194194
// member access by subscript operator
195195
type = substitute(^^subscript_result, {type});
@@ -273,7 +273,7 @@ struct FormatParser : _impl::Parser {
273273
} break;
274274
case ReplacementType::indexed: {
275275
has_positional = true;
276-
index = _impl::stou(replacement.field);
276+
index = _serialize_impl::stou(replacement.field);
277277
if (direct[index] == -1) {
278278
result.accessors.push_back(get_arg_accessor(index));
279279
direct[index] = int(result.accessors.size() - 1);
@@ -289,7 +289,7 @@ struct FormatParser : _impl::Parser {
289289
throw "implicit 0. not allowed with more than one arg";
290290
}
291291
} else {
292-
index = _impl::stou(replacement.field.substr(0, dot_pos));
292+
index = _serialize_impl::stou(replacement.field.substr(0, dot_pos));
293293
replacement.field = replacement.field.substr(dot_pos + 1);
294294
}
295295
result.accessors.push_back(replacement.get_accessor(index, arg_types[index]));
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
#include <meta>
3+
#include <ranges>
4+
#include <algorithm>
5+
#include <bit>
6+
7+
namespace rsl::_serialize_impl {
8+
template <typename T>
9+
struct Enumerator {
10+
T value;
11+
char const* name;
12+
};
13+
14+
template <typename T>
15+
consteval auto sorted_enum_pairs() {
16+
auto enumerators =
17+
std::vector(std::from_range, enumerators_of(^^T) | std::views::transform([](auto e) {
18+
return Enumerator(extract<T>(constant_of(e)),
19+
define_static_string(identifier_of(e)));
20+
}));
21+
22+
using sort_t = std::make_unsigned_t<std::underlying_type_t<T>>;
23+
std::ranges::stable_sort(enumerators, std::ranges::greater{}, [](auto const& e) {
24+
return std::popcount(static_cast<sort_t>(e.value));
25+
});
26+
27+
return enumerators;
28+
}
29+
30+
} // namespace rsl::_serialize_impl
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <string>
44
#include <rsl/_impl/hash.hpp>
55

6-
namespace rsl::_impl {
6+
namespace rsl::_serialize_impl {
77
constexpr std::string op_to_string(std::meta::operators op) {
88
switch (op) {
99
using enum std::meta::operators;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#include <string>
33
#include <cstdint>
44

5-
namespace rsl::_impl {
5+
namespace rsl::_serialize_impl {
66
constexpr std::string utos(std::uint64_t value) {
77
std::string out{};
88
do {
@@ -19,4 +19,4 @@ constexpr std::uint64_t stou(std::string_view str) {
1919
}
2020
return result;
2121
}
22-
} // namespace rsl::_impl
22+
} // namespace rsl::_serialize_impl

include/rsl/_impl/traits.hpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
#include <type_traits>
3+
4+
namespace rsl::_impl {
5+
template <class T>
6+
inline const bool is_signed_integer_v = false;
7+
template <>
8+
inline const bool is_signed_integer_v<signed char> = true;
9+
template <>
10+
inline const bool is_signed_integer_v<signed short> = true;
11+
template <>
12+
inline const bool is_signed_integer_v<signed int> = true;
13+
template <>
14+
inline const bool is_signed_integer_v<signed long> = true;
15+
template <>
16+
inline const bool is_signed_integer_v<signed long long> = true;
17+
18+
template <class T>
19+
inline const bool is_unsigned_integer_v = false;
20+
template <>
21+
inline const bool is_unsigned_integer_v<unsigned char> = true;
22+
template <>
23+
inline const bool is_unsigned_integer_v<unsigned short> = true;
24+
template <>
25+
inline const bool is_unsigned_integer_v<unsigned int> = true;
26+
template <>
27+
inline const bool is_unsigned_integer_v<unsigned long> = true;
28+
template <>
29+
inline const bool is_unsigned_integer_v<unsigned long long> = true;
30+
31+
template <class T>
32+
concept signed_integer = is_signed_integer_v<T>;
33+
34+
template <class T>
35+
concept unsigned_integer = is_unsigned_integer_v<T>;
36+
37+
template <class T>
38+
concept integer_type = signed_integer<T> or unsigned_integer<T>;
39+
} // namespace rsl::_impl

include/rsl/assert

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
#include <rsl/source_location>
1212
#include <rsl/expect>
1313
#include <rsl/string_view>
14-
#include <rsl/_impl/util/to_string.hpp>
15-
#include "_impl/macros.hpp"
14+
#include <rsl/serialize>
15+
16+
#include <rsl/_impl/macros.hpp>
1617

1718
#define RSL_REVIEW_DEFAULT 1
1819

include/rsl/enum

Lines changed: 2 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <utility>
66
#include <rsl/meta_traits>
77

8+
#include <rsl/_impl/traits.hpp>
9+
810
namespace rsl {
911
inline namespace annotations {
1012
struct FlagEnumTag {};
@@ -127,43 +129,6 @@ struct numeric_limits<E> {
127129
static constexpr const std::float_round_style round_style = std::round_toward_zero;
128130
};
129131

130-
namespace _impl {
131-
template <class T>
132-
inline const bool is_signed_integer_v = false;
133-
template <>
134-
inline const bool is_signed_integer_v<signed char> = true;
135-
template <>
136-
inline const bool is_signed_integer_v<signed short> = true;
137-
template <>
138-
inline const bool is_signed_integer_v<signed int> = true;
139-
template <>
140-
inline const bool is_signed_integer_v<signed long> = true;
141-
template <>
142-
inline const bool is_signed_integer_v<signed long long> = true;
143-
144-
template <class T>
145-
inline const bool is_unsigned_integer_v = false;
146-
template <>
147-
inline const bool is_unsigned_integer_v<unsigned char> = true;
148-
template <>
149-
inline const bool is_unsigned_integer_v<unsigned short> = true;
150-
template <>
151-
inline const bool is_unsigned_integer_v<unsigned int> = true;
152-
template <>
153-
inline const bool is_unsigned_integer_v<unsigned long> = true;
154-
template <>
155-
inline const bool is_unsigned_integer_v<unsigned long long> = true;
156-
157-
template <class T>
158-
concept signed_integer = is_signed_integer_v<T>;
159-
160-
template <class T>
161-
concept unsigned_integer = is_unsigned_integer_v<T>;
162-
163-
template <class T>
164-
concept integer_type = signed_integer<T> or unsigned_integer<T>;
165-
} // namespace _impl
166-
167132
template <is_fixed_enum E>
168133
requires is_fixed_enum<E> and (not is_flag_enum<E>)
169134
struct numeric_limits<E> : std::numeric_limits<std::underlying_type_t<E>> {};
@@ -196,73 +161,6 @@ constexpr bool in_range(U value) noexcept {
196161
std::cmp_greater_equal(static_cast<underlying>(value),
197162
static_cast<underlying>(numeric_limits<T>::min()));
198163
}
199-
200-
namespace _impl {
201-
template <typename T>
202-
struct Enumerator {
203-
T value;
204-
char const* name;
205-
};
206-
207-
template <typename T>
208-
consteval auto enum_pairs() {
209-
auto enumerators =
210-
std::vector(std::from_range, enumerators_of(^^T) | std::views::transform([](auto e) {
211-
return Enumerator(extract<T>(constant_of(e)),
212-
define_static_string(identifier_of(e)));
213-
}));
214-
215-
using sort_t = std::make_unsigned_t<std::underlying_type_t<T>>;
216-
std::ranges::stable_sort(enumerators, std::ranges::greater{}, [](auto const& e) {
217-
return std::popcount(static_cast<sort_t>(e.value));
218-
});
219-
220-
return enumerators;
221-
}
222-
} // namespace _impl
223-
224-
template <typename T>
225-
requires std::is_enum_v<T>
226-
constexpr std::string to_string(T value) {
227-
if constexpr (is_flag_enum<T>) {
228-
// for flag-likes we want to support `A | B` unless we got exact match
229-
constexpr static auto enums = std::define_static_array(_impl::enum_pairs<T>());
230-
231-
auto remainder = std::to_underlying(value);
232-
std::vector<std::string_view> partial;
233-
234-
for (auto [v, name] : enums) {
235-
if (v == 0) {
236-
if (remainder == 0) {
237-
return name;
238-
}
239-
} else if ((remainder & v) == v) {
240-
remainder &= ~v;
241-
partial.push_back(name);
242-
if (remainder == 0) {
243-
break;
244-
}
245-
}
246-
}
247-
248-
auto ret = std::string(std::from_range, partial | std::views::join_with('|'));
249-
if (remainder != 0) {
250-
return ret + identifier_of(^^T) + "(" + to_string(remainder) + ")";
251-
}
252-
return ret;
253-
} else {
254-
// otherwise we can do better - try generating a switch instead
255-
template for (constexpr auto E : define_static_array(enumerators_of(^^T))) {
256-
if (extract<T>(constant_of(E)) == value) {
257-
// at least one enumerator was an exact match, print the first
258-
return std::string{identifier_of(E)};
259-
}
260-
}
261-
// still no match, print as cast using the functional notation
262-
return std::string(identifier_of(^^T)) + "(" + to_string(std::to_underlying(value)) + ")";
263-
}
264-
}
265-
266164
} // namespace rsl
267165

268166
#ifdef RSL_POISON_STD

include/rsl/expect

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
#include <rsl/annotations>
1010
#include <rsl/meta_traits>
11+
#include <rsl/serialize>
1112

1213
#include <rsl/_impl/concepts.hpp>
13-
#include <rsl/_impl/util/operators.hpp>
14-
#include <rsl/_impl/util/to_string.hpp>
14+
#include <rsl/_impl/serialize/operators.hpp>
1515

1616
namespace rsl::_expect_impl {
1717
template <typename T>
@@ -32,7 +32,7 @@ struct Operator {
3232
std::meta::operators op;
3333

3434
Operator() = delete;
35-
constexpr Operator(std::convertible_to<std::string_view> auto str) : op(_impl::to_operator(str)) {}
35+
constexpr Operator(std::convertible_to<std::string_view> auto str) : op(_serialize_impl::to_operator(str)) {}
3636
constexpr Operator(std::meta::operators op) : op(op) {}
3737
constexpr ~Operator() {}
3838

include/rsl/serialize

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
#include <rsl/meta_traits>
55

66
#include <rsl/enum> // for to_string(enum-type)
7-
#include <rsl/_impl/util/operators.hpp>
8-
#include <rsl/_impl/util/to_string.hpp>
7+
#include <rsl/_impl/serialize/operators.hpp>
8+
#include <rsl/_impl/serialize/to_string.hpp>
9+
#include <rsl/_impl/serialize/enum.hpp>
910

1011
#include "serializer/machinery.hpp"
1112
#include "serializer/name.hpp"
@@ -48,10 +49,10 @@ consteval std::meta::info deserialize_type(T&& deserializer, R&& data) {
4849
constexpr std::string to_string(std::integral auto value) {
4950
if constexpr (std::is_signed_v<decltype(value)>) {
5051
if (value < 0) {
51-
return std::string{'-'} + _impl::utos(-value);
52+
return std::string{'-'} + _serialize_impl::utos(-value);
5253
}
5354
}
54-
return _impl::utos(value);
55+
return _serialize_impl::utos(value);
5556
}
5657

5758
// can this be constexpr? fpenv?
@@ -73,7 +74,52 @@ constexpr std::string to_string(std::string_view value) {
7374
}
7475

7576
constexpr std::string to_string(std::meta::operators op) {
76-
return ::rsl::_impl::op_to_string(op);
77+
return _serialize_impl::op_to_string(op);
78+
}
79+
80+
template <typename T>
81+
requires std::is_enum_v<T>
82+
constexpr std::string to_string(T value) {
83+
if constexpr (is_flag_enum<T>) {
84+
// for flag-likes we want to support `A | B` unless we got exact match
85+
constexpr static auto enums = std::define_static_array(_serialize_impl::sorted_enum_pairs<T>());
86+
87+
auto remainder = std::to_underlying(value);
88+
std::vector<std::string_view> partial;
89+
90+
for (auto [v, name] : enums) {
91+
if (v == T(0)) {
92+
if (remainder == 0) {
93+
return name;
94+
}
95+
} else if ((remainder & v) == v) {
96+
remainder &= ~v;
97+
partial.push_back(name);
98+
if (remainder == 0) {
99+
break;
100+
}
101+
}
102+
}
103+
104+
auto ret = std::string(std::from_range, partial | std::views::join_with('|'));
105+
if (remainder != 0) {
106+
if (not ret.empty()) {
107+
ret += '|';
108+
}
109+
return ret + identifier_of(^^T) + '(' + to_string(remainder) + ')';
110+
}
111+
return ret;
112+
} else {
113+
// otherwise we can do better - try generating a switch instead
114+
template for (constexpr auto E : define_static_array(enumerators_of(^^T))) {
115+
if (extract<T>(constant_of(E)) == value) {
116+
// at least one enumerator was an exact match, print the first
117+
return std::string{identifier_of(E)};
118+
}
119+
}
120+
// still no match, print as cast using the functional notation
121+
return std::string(identifier_of(^^T)) + "(" + to_string(std::to_underlying(value)) + ")";
122+
}
77123
}
78124

79125
} // namespace rsl

test/serializer/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ target_sources(rsl-util-test PRIVATE
33
to_owning.cpp
44
)
55

6-
add_subdirectory(repr)
6+
add_subdirectory(repr)
7+
add_subdirectory(to_string)

0 commit comments

Comments
 (0)