5
5
*/
6
6
7
7
/* *
8
- * @brief Implements encoding and validation of enumeration values using SCALE
9
- * encoding.
8
+ * @brief Implements encoding and validation of enumeration values using SCALE.
10
9
*
11
10
* This file provides utilities for handling enumerations in SCALE encoding.
12
11
* It allows defining constraints on enum values via `enum_traits`
13
12
* specializations, ensuring that only valid values are encoded or decoded.
14
- * There are two ways to specialize an enumeration type:
15
13
*
16
- * 1. **Defining a range of valid values** using `min_value` and `max_value`.
17
- * 2. **Providing an explicit list of valid values** using `valid_values`.
14
+ * There are two ways to specialize an enumeration type:
15
+ * - Define a range using `min_value` and `max_value`.
16
+ * - Provide an explicit list using `valid_values`.
18
17
*
19
- * The validation ensures that any decoded value belongs to the expected set ,
20
- * reducing the risk of unexpected errors when processing SCALE-encoded data.
18
+ * Validation guarantees decoded values are within expected bounds ,
19
+ * reducing risk when handling SCALE-encoded data.
21
20
*/
22
21
23
22
#pragma once
24
23
24
+ #include < algorithm>
25
+ #include < limits>
26
+ #include < string_view>
25
27
#include < type_traits>
26
28
27
29
#include < scale/detail/tagged.hpp>
28
30
#include < scale/outcome/outcome_throw.hpp>
29
31
#include < scale/scale_error.hpp>
30
- #include < scale/types.hpp>
31
32
32
33
namespace scale {
33
34
34
35
namespace detail ::enumerations {
35
36
36
- template <typename T>
37
- concept Enumeration = std::is_enum_v<std::remove_cvref_t <T>>;
37
+ #define ENUM_NAME_PRETTY_FUNCTION __PRETTY_FUNCTION__
38
+ #if defined(__clang__)
39
+ // clang-format off
40
+ // Invalid:
41
+ // "std::string_view scale::detail::enumerations::enum_name_impl() [V = (Baz)-128]"
42
+ // Valid:
43
+ // "std::string_view scale::detail::enumerations::enum_name_impl() [V = Enum_ValidatingByReflection_I8_Test::TestBody()::Baz::A]"
44
+ // clang-format on
45
+ constexpr std::string_view enum_prefix = " EnumValue = " ;
46
+ constexpr std::string_view enum_suffix = " ]" ;
47
+ #elif defined(__GNUC__)
48
+ // clang-format off
49
+ // Invalid:
50
+ // "std::string_view scale::detail::enumerations::enum_name_impl() [with auto V = (Enum_ValidatingByReflection_I8_Test::TestBody::Baz)-128; std::string_view = std::basic_string_view<char>]"
51
+ // Valid:
52
+ // "std::string_view scale::detail::enumerations::enum_name_impl() [with auto V = Enum_ValidatingByReflection_I8_Test::TestBody::Baz::A; std::string_view = std::basic_string_view<char>]"
53
+ // clang-format on
54
+ constexpr std::string_view enum_prefix = " EnumValue = " ;
55
+ constexpr std::string_view enum_suffix = " ; " ;
56
+ #else
57
+ #error Unsupported compiler
58
+ #endif
38
59
39
60
/* *
40
- * @brief Traits for enum validation.
41
- *
42
- * Provides two specialization choices:
43
- * - `min_value` and `max_value` convertible to `std::underlying_type_t<E>`.
44
- * - A container of `std::underlying_type_t<E>` named `valid_values`,
45
- * listing valid values.
46
- *
47
- * @note Check the macros below for specialization convenience.
48
- * @tparam E The enum type.
61
+ * @brief Extracts enumeration name from the compiler-specific string.
62
+ * @tparam V The enum value.
49
63
*/
50
- template <typename E>
51
- requires std::is_enum_v<E>
52
- struct [[deprecated(
53
- " Check the doc comment to see the specialization options" )]] //
54
- enum_traits final {
55
- // / Used to detect an unspecialized enum_traits
56
- static constexpr bool is_default = true ;
57
- };
64
+ template <auto EnumValue>
65
+ consteval std::string_view enum_name_impl () {
66
+ constexpr std::string_view func = ENUM_NAME_PRETTY_FUNCTION;
67
+ constexpr std::size_t prefix_pos = func.find (enum_prefix);
68
+ static_assert (prefix_pos != std::string_view::npos);
69
+ constexpr std::size_t start = prefix_pos + enum_prefix.size ();
70
+ if (func[start] == ' (' )
71
+ return {}; // Invalid: __PRETTY_FUNCTION__ prints invalid value of enum
72
+ // as number C-style-casted into enum type, i.e. `(Enum)-1`.
73
+ constexpr std::size_t end = func.find (enum_suffix, start);
74
+ constexpr std::string_view full = func.substr (start, end - start);
75
+ constexpr std::size_t colons = full.rfind (" ::" );
76
+ if (colons == std::string_view::npos)
77
+ return {}; // Invalid: __PRETTY_FUNCTION__ always prints valid value
78
+ // with type, i.e. `Enum::value`
79
+ return full.substr (colons + 2 );
80
+ }
58
81
59
82
/* *
60
- * @brief Checks if a given value is within a defined range of valid enum
61
- * values.
62
- * @tparam T The input type (expected to be an enum or convertible
63
- * underlying type).
64
- * @param value The value to check.
65
- * @return True if the value is within the range, false otherwise.
83
+ * @brief Concept that checks if a type is an enumeration.
66
84
*/
67
- template <typename T,
68
- typename E = std::decay_t <T>,
69
- typename E_traits = enum_traits<E>,
70
- std::underlying_type_t <E> Min = E_traits::min_value,
71
- std::underlying_type_t <E> Max = E_traits::max_value>
72
- constexpr bool is_valid_enum_value (
73
- std::underlying_type_t <E> value) noexcept {
74
- return value >= Min && value <= Max;
75
- }
85
+ template <typename T>
86
+ concept Enumeration = std::is_enum_v<std::remove_cvref_t <T>>;
76
87
77
88
/* *
78
- * @brief Checks if a given value is within an explicitly defined set of
79
- * valid enum values.
80
- * @tparam T The input type (expected to be an enum or convertible
81
- * underlying type).
82
- * @param value The value to check.
83
- * @return True if the value is listed in `valid_values`, false otherwise.
89
+ * @brief Traits struct to be specialized for enumeration validation.
84
90
*/
85
- template <typename T,
86
- typename E = std::decay_t <T>,
87
- typename E_traits = enum_traits<E>,
88
- typename = decltype (E_traits::valid_values)>
89
- constexpr bool is_valid_enum_value (
90
- std::underlying_type_t <E> value) noexcept {
91
- const auto &valid_values = E_traits::valid_values;
92
- return std::find (std::begin (valid_values),
93
- std::end (valid_values),
94
- static_cast <E>(value))
95
- != std::end (valid_values);
96
- }
91
+ template <typename E>
92
+ struct enum_traits ; // default not specialized
93
+
94
+ namespace detail_impl {
95
+
96
+ template <typename E>
97
+ concept HasValidValues = requires {
98
+ { enum_traits<E>::valid_values } -> std::ranges::range;
99
+ };
100
+
101
+ template <typename E>
102
+ concept HasMinMax = requires {
103
+ {
104
+ enum_traits<E>::min_value
105
+ } -> std::convertible_to<std::underlying_type_t <E>>;
106
+ {
107
+ enum_traits<E>::max_value
108
+ } -> std::convertible_to<std::underlying_type_t <E>>;
109
+ };
110
+
111
+ template <typename E, typename U = std::underlying_type_t <E>, U... Vs>
112
+ constexpr bool is_valid_enum_value_by_reflection_impl (
113
+ U value, std::integer_sequence<U, Vs...>) {
114
+ return ((enum_name_impl<static_cast <E>(Vs)>().size () > 0
115
+ && static_cast <U>(static_cast <E>(Vs)) == value)
116
+ || ...);
117
+ }
118
+
119
+ template <typename E, int ... Is>
120
+ constexpr bool call_with_casted_signed_range (
121
+ std::underlying_type_t <E> value, std::integer_sequence<int , Is...>) {
122
+ using U = std::underlying_type_t <E>;
123
+ constexpr int min = -128 ;
124
+ return is_valid_enum_value_by_reflection_impl<E>(
125
+ value, std::integer_sequence<U, static_cast <U>(min + Is)...>{});
126
+ }
127
+
128
+ template <typename E, int ... Is>
129
+ constexpr bool call_with_casted_unsigned_range (
130
+ std::underlying_type_t <E> value, std::integer_sequence<int , Is...>) {
131
+ using U = std::underlying_type_t <E>;
132
+ return is_valid_enum_value_by_reflection_impl<E>(
133
+ value, std::integer_sequence<U, static_cast <U>(Is)...>{});
134
+ }
135
+
136
+ /* *
137
+ * @brief Fallback validation using reflection for enums of size 1 byte.
138
+ */
139
+ template <typename E>
140
+ requires (sizeof (std::underlying_type_t <E>) == 1 )
141
+ constexpr bool is_valid_enum_value_by_reflection (
142
+ std::underlying_type_t <E> value) {
143
+ using U = std::underlying_type_t <E>;
144
+
145
+ if constexpr (std::is_signed_v<U>) {
146
+ constexpr int min = -128 ;
147
+ constexpr int max = 127 ;
148
+ return call_with_casted_signed_range<E>(
149
+ value, std::make_integer_sequence<int , max - min + 1 >{});
150
+ } else {
151
+ constexpr int max = 255 ;
152
+ return call_with_casted_unsigned_range<E>(
153
+ value, std::make_integer_sequence<int , max + 1 >{});
154
+ }
155
+ }
156
+
157
+ /* *
158
+ * @brief Validates enum value using min/max range.
159
+ */
160
+ template <typename T>
161
+ requires HasMinMax<std::decay_t <T>>
162
+ constexpr bool is_valid_enum_value_range (
163
+ std::underlying_type_t <std::decay_t <T>> value) noexcept {
164
+ using E = std::decay_t <T>;
165
+ constexpr auto Min = enum_traits<E>::min_value;
166
+ constexpr auto Max = enum_traits<E>::max_value;
167
+ return value >= Min && value <= Max;
168
+ }
169
+
170
+ /* *
171
+ * @brief Validates enum value against list of valid values.
172
+ */
173
+ template <typename T>
174
+ requires HasValidValues<std::decay_t <T>>
175
+ constexpr bool is_valid_enum_value_list (
176
+ std::underlying_type_t <std::decay_t <T>> value) noexcept {
177
+ using E = std::decay_t <T>;
178
+ const auto &valid_values = enum_traits<E>::valid_values;
179
+ return std::find (valid_values.begin (),
180
+ valid_values.end (),
181
+ static_cast <E>(value))
182
+ != valid_values.end ();
183
+ }
184
+
185
+ } // namespace detail_impl
186
+
187
+ template <typename T>
188
+ constexpr bool CannotValidateEnum = false ;
97
189
98
190
/* *
99
- * @brief Default case for unspecialized enum types.
100
- *
101
- * This function always returns `true`, but a `static_assert` ensures that
102
- * an explicit specialization of `enum_traits` is required.
103
- *
104
- * @tparam T The input type (expected to be an enum).
105
- * @return Always true, but triggers a compilation error if used.
191
+ * @brief Central enum validation entry point.
192
+ * @tparam T Enum type.
193
+ * @param value The underlying integer value.
194
+ * @return true if value is valid.
106
195
*/
107
- template <typename T>
108
- requires enum_traits<std::decay_t <T>>::is_default
109
- [[deprecated(
110
- " Please specialize scale::enum_traits for your enum so it can be "
111
- " validated during decoding" )]]
196
+ template <Enumeration T>
112
197
constexpr bool is_valid_enum_value (
113
- std::underlying_type_t <std::decay_t <T>>) noexcept {
114
- return true ;
198
+ std::underlying_type_t <std::decay_t <T>> value) noexcept {
199
+ using E = std::decay_t <T>;
200
+
201
+ if constexpr (detail_impl::HasValidValues<E>) {
202
+ return detail_impl::is_valid_enum_value_list<T>(value);
203
+ } else if constexpr (detail_impl::HasMinMax<E>) {
204
+ return detail_impl::is_valid_enum_value_range<T>(value);
205
+ } else if constexpr (sizeof (std::underlying_type_t <E>) == 1 ) {
206
+ return detail_impl::is_valid_enum_value_by_reflection<E>(value);
207
+ } else {
208
+ static_assert (CannotValidateEnum<T>,
209
+ " Cannot validate enum: "
210
+ " define enum_traits<> with min/max or valid_values." );
211
+ return true ;
212
+ }
115
213
}
214
+
116
215
} // namespace detail::enumerations
117
216
118
217
using detail::enumerations::enum_traits;
119
218
using detail::enumerations::Enumeration;
120
219
using detail::enumerations::is_valid_enum_value;
121
220
122
221
/* *
123
- * @brief Encodes an enumeration into its underlying type.
124
- * @param enumeration The enumeration value to encode.
125
- * @param encoder SCALE encoder.
222
+ * @brief Encodes an enum value using SCALE encoding.
223
+ * @tparam T Enum type.
224
+ * @param enumeration Enum value to encode.
225
+ * @param encoder Encoder instance.
126
226
*/
127
227
void encode (const Enumeration auto &enumeration, Encoder &encoder)
128
228
requires NoTagged<decltype(enumeration)>
@@ -133,9 +233,10 @@ namespace scale {
133
233
}
134
234
135
235
/* *
136
- * @brief Decodes an enumeration from its underlying type.
137
- * @param v The enumeration value to decode into.
138
- * @param decoder SCALE decoder.
236
+ * @brief Decodes an enum value using SCALE decoding.
237
+ * @tparam T Enum type.
238
+ * @param v Enum variable to store the decoded value.
239
+ * @param decoder Decoder instance.
139
240
*/
140
241
void decode (Enumeration auto &v, Decoder &decoder)
141
242
requires NoTagged<decltype(v)>
@@ -153,12 +254,12 @@ namespace scale {
153
254
} // namespace scale
154
255
155
256
/* *
156
- * @brief Defines a valid range for an enumeration.
157
- * @note You should use this macro only in the global namespace
158
- * @param enum_namespace The namespace of the enum .
159
- * @param enum_name The enum type.
160
- * @param min The minimum valid value.
161
- * @param max The maximum valid value.
257
+ * @def SCALE_DEFINE_ENUM_VALUE_RANGE
258
+ * @brief Defines a valid value range for an enum.
259
+ * @param enum_namespace Namespace where enum is defined .
260
+ * @param enum_name Enum type name .
261
+ * @param min Minimum valid value.
262
+ * @param max Maximum valid value.
162
263
*/
163
264
#define SCALE_DEFINE_ENUM_VALUE_RANGE (enum_namespace, enum_name, min, max ) \
164
265
template <> \
@@ -169,10 +270,11 @@ namespace scale {
169
270
};
170
271
171
272
/* *
172
- * @brief Defines a valid list of values for an enumeration.
173
- * @param enum_namespace The namespace of the enum.
174
- * @param enum_name The enum type.
175
- * @param ... The valid values.
273
+ * @def SCALE_DEFINE_ENUM_VALUE_LIST
274
+ * @brief Defines an explicit list of valid enum values.
275
+ * @param enum_namespace Namespace where enum is defined.
276
+ * @param enum_name Enum type name.
277
+ * @param ... List of valid values.
176
278
*/
177
279
#define SCALE_DEFINE_ENUM_VALUE_LIST (enum_namespace, enum_name, ...) \
178
280
template <> \
0 commit comments