1+ #pragma once
2+ #include <meta>
3+ #include <ranges>
4+ #include <type_traits>
5+ #include <rsl/meta_traits>
6+
7+ namespace rsl {
8+ inline namespace annotations {
9+ struct FlagEnumTag {};
10+ constexpr inline FlagEnumTag flag_enum;
11+
12+ } // namespace annotations
13+
14+ template <typename T>
15+ concept is_flag_enum =
16+ std::is_scoped_enum_v<T> && meta::has_annotation(^^T, ^^annotations::FlagEnumTag);
17+
18+ template <typename T>
19+ concept fixed_enum = std::is_enum_v<T> and requires { T{0}; };
20+
21+ consteval bool has_fixed_underlying_type(std::meta::info r) {
22+ return is_type(r) and is_enum_type(r) and extract<bool>(substitute(^^fixed_enum, {r}));
23+ }
24+
25+ template <is_flag_enum E>
26+ constexpr bool has_flag(E flags, E needle) {
27+ using U = std::underlying_type_t<E>;
28+ return (static_cast<U>(flags) & static_cast<U>(needle)) == static_cast<U>(needle);
29+ }
30+
31+ template <is_flag_enum E>
32+ constexpr bool has_flag(E flags, std::underlying_type_t<E> needle) {
33+ using U = std::underlying_type_t<E>;
34+ return (static_cast<U>(flags) & needle) == needle;
35+ }
36+
37+ template <typename T>
38+ requires std::is_enum_v<T>
39+ constexpr bool is_named_enumerator(T value) {
40+ constexpr static auto named_enumerators =
41+ define_static_array(enumerators_of(^^T) | std::views::transform([](std::meta::info r) {
42+ return extract<T>(constant_of(r));
43+ }));
44+ return std::ranges::contains(named_enumerators, value);
45+ }
46+
47+ namespace _impl {
48+ template <typename E>
49+ struct enum_range {
50+ using underlying = std::underlying_type_t<E>;
51+ struct Range {
52+ underlying min;
53+ underlying max;
54+ };
55+
56+ private:
57+ static consteval Range get_enumerator_range() {
58+ auto enumerators = enumerators_of(^^E);
59+ if (enumerators.empty()) {
60+ return {0, 0};
61+ } else {
62+ auto transformed = enumerators | std::views::transform(std::meta::extract<E>);
63+ auto [min_it, max_it] = std::ranges::minmax_element(transformed);
64+ return {static_cast<underlying>(*min_it), static_cast<underlying>(*max_it)};
65+ }
66+ }
67+
68+ static constexpr int get_bit_width() {
69+ using unsigned_underlying = std::make_unsigned_t<underlying>;
70+
71+ if (range.min < 0) {
72+ auto min_val = static_cast<unsigned_underlying>(~range.min);
73+ auto max_val = static_cast<unsigned_underlying>(range.max < 0 ? ~range.max : range.max);
74+ return 1 + std::max(std::bit_width(min_val), std::bit_width(max_val));
75+ }
76+ return std::max(1, std::bit_width(unsigned_underlying(range.max)));
77+ }
78+
79+ public:
80+ static constexpr auto range = get_enumerator_range();
81+ static constexpr auto bit_width = get_bit_width();
82+ static constexpr auto min_value = range.min < 0 ? -(1ULL << (bit_width - 1)) : 0;
83+ static constexpr auto max_value = range.min < 0 ? -(min_value + 1)
84+ : std::numeric_limits<uint64_t>::max() >>
85+ (bit_width >= 64 ? 0 : 64 - bit_width);
86+ };
87+ } // namespace _impl
88+
89+ template <typename T>
90+ struct numeric_limits;
91+
92+ template <typename T>
93+ requires std::numeric_limits<T>::is_specialized
94+ struct numeric_limits<T> : std::numeric_limits<T> {};
95+
96+ template <typename E>
97+ requires std::is_enum_v<E> && (not fixed_enum<E>)
98+ struct numeric_limits<E> {
99+ using type = E;
100+ static constexpr const bool is_specialized = false;
101+ [[nodiscard]] static constexpr type min() noexcept {
102+ return type(_impl::enum_range<E>::min_value);
103+ }
104+ [[nodiscard]] static constexpr type max() noexcept {
105+ return type(_impl::enum_range<E>::max_value);
106+ }
107+ [[nodiscard]] static constexpr type lowest() noexcept { return min(); }
108+
109+ static constexpr const int digits = _impl::enum_range<E>::bit_width;
110+ static constexpr const int digits10 = digits * 3 / 10;
111+ static constexpr const int max_digits10 = 0;
112+ static constexpr const bool is_signed = std::is_signed_v<std::underlying_type_t<E>>;
113+ static constexpr const bool is_integer = true;
114+ static constexpr const bool is_exact = true;
115+ static constexpr const int radix = 2;
116+ [[nodiscard]] static constexpr type epsilon() noexcept { return type(); }
117+ [[nodiscard]] static constexpr type round_error() noexcept { return type(); }
118+
119+ static constexpr const int min_exponent = 0;
120+ static constexpr const int min_exponent10 = 0;
121+ static constexpr const int max_exponent = 0;
122+ static constexpr const int max_exponent10 = 0;
123+
124+ static constexpr const bool has_infinity = false;
125+ static constexpr const bool has_quiet_NaN = false;
126+ static constexpr const bool has_signaling_NaN = false;
127+ [[deprecated]] static constexpr const std::float_denorm_style has_denorm = std::denorm_absent;
128+ [[deprecated]] static constexpr const bool has_denorm_loss = false;
129+ [[nodiscard]] static constexpr type infinity() noexcept { return type(); }
130+ [[nodiscard]] static constexpr type quiet_NaN() noexcept { return type(); }
131+ [[nodiscard]] static constexpr type signaling_NaN() noexcept { return type(); }
132+ [[nodiscard]] static constexpr type denorm_min() noexcept { return type(); }
133+
134+ static constexpr const bool is_iec559 = false;
135+ static constexpr const bool is_bounded = true;
136+ static constexpr const bool is_modulo = false;
137+
138+ static constexpr const bool traps = std::numeric_limits<std::underlying_type_t<E>>::traps;
139+ static constexpr const bool tinyness_before = false;
140+ static constexpr const std::float_round_style round_style = std::round_toward_zero;
141+ };
142+
143+ template <fixed_enum E>
144+ struct numeric_limits<E> : std::numeric_limits<std::underlying_type_t<E>> {};
145+ } // namespace rsl
146+
147+ #ifdef RSL_POISON_STD
148+ template <typename E>
149+ requires std::is_enum_v<E>
150+ struct std::numeric_limits<E> : rsl::numeric_limits<E> {};
151+ #endif
152+
153+ template <rsl::is_flag_enum E>
154+ constexpr E operator~(E v) noexcept {
155+ using U = std::underlying_type_t<E>;
156+ return static_cast<E>(~static_cast<U>(v));
157+ }
158+
159+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
160+ constexpr bool operator==(E lhs, T rhs) noexcept {
161+ using U = std::underlying_type_t<E>;
162+ return static_cast<U>(lhs) == static_cast<U>(rhs);
163+ }
164+
165+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
166+ constexpr bool operator==(T lhs, E rhs) noexcept {
167+ using U = std::underlying_type_t<E>;
168+ return static_cast<U>(lhs) == static_cast<U>(rhs);
169+ }
170+
171+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
172+ constexpr bool operator!=(E lhs, T rhs) noexcept {
173+ using U = std::underlying_type_t<E>;
174+ return static_cast<U>(lhs) != static_cast<U>(rhs);
175+ }
176+
177+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
178+ constexpr bool operator!=(T lhs, E rhs) noexcept {
179+ using U = std::underlying_type_t<E>;
180+ return static_cast<U>(lhs) != static_cast<U>(rhs);
181+ }
182+
183+ template <rsl::is_flag_enum E>
184+ constexpr E operator|(E lhs, E rhs) noexcept {
185+ using U = std::underlying_type_t<E>;
186+ return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
187+ }
188+
189+ template <rsl::is_flag_enum E>
190+ constexpr E operator&(E lhs, E rhs) noexcept {
191+ using U = std::underlying_type_t<E>;
192+ return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
193+ }
194+
195+ template <rsl::is_flag_enum E>
196+ constexpr E operator^(E lhs, E rhs) noexcept {
197+ using U = std::underlying_type_t<E>;
198+ return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
199+ }
200+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
201+ constexpr E operator|(E lhs, T rhs) noexcept {
202+ using U = std::underlying_type_t<E>;
203+ return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
204+ }
205+
206+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
207+ constexpr E operator&(E lhs, T rhs) noexcept {
208+ using U = std::underlying_type_t<E>;
209+ return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
210+ }
211+
212+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
213+ constexpr E operator^(E lhs, T rhs) noexcept {
214+ using U = std::underlying_type_t<E>;
215+ return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
216+ }
217+
218+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
219+ constexpr E operator|(T lhs, E rhs) noexcept {
220+ using U = std::underlying_type_t<E>;
221+ return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
222+ }
223+
224+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
225+ constexpr E operator&(T lhs, E rhs) noexcept {
226+ using U = std::underlying_type_t<E>;
227+ return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
228+ }
229+
230+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
231+ constexpr E operator^(T lhs, E rhs) noexcept {
232+ using U = std::underlying_type_t<E>;
233+ return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
234+ }
235+
236+ template <rsl::is_flag_enum E>
237+ constexpr E& operator|=(E& lhs, E rhs) noexcept {
238+ return lhs = lhs | rhs;
239+ }
240+
241+ template <rsl::is_flag_enum E>
242+ constexpr E& operator&=(E& lhs, E rhs) noexcept {
243+ return lhs = lhs & rhs;
244+ }
245+
246+ template <rsl::is_flag_enum E>
247+ constexpr E& operator^=(E& lhs, E rhs) noexcept {
248+ return lhs = lhs ^ rhs;
249+ }
250+
251+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
252+ constexpr E& operator|=(E& lhs, T rhs) noexcept {
253+ return lhs = lhs | rhs;
254+ }
255+
256+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
257+ constexpr E& operator&=(E& lhs, T rhs) noexcept {
258+ return lhs = lhs & rhs;
259+ }
260+
261+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
262+ constexpr E& operator^=(E& lhs, T rhs) noexcept {
263+ return lhs = lhs ^ rhs;
264+ }
265+
266+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
267+ constexpr T& operator|=(T& lhs, E rhs) noexcept {
268+ return lhs = static_cast<uint8_t>(lhs | rhs);
269+ }
270+
271+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
272+ constexpr T& operator&=(T& lhs, E rhs) noexcept {
273+ return lhs = static_cast<uint8_t>(lhs & rhs);
274+ }
275+
276+ template <rsl::is_flag_enum E, std::convertible_to<std::underlying_type_t<E>> T>
277+ constexpr T& operator^=(T& lhs, E rhs) noexcept {
278+ return lhs = static_cast<uint8_t>(lhs ^ rhs);
279+ }
0 commit comments