Skip to content

Commit b49b374

Browse files
authored
Merge pull request #81 from iris-cpp/refine-enum
Refine enum utilities
2 parents c76428a + 3519ca0 commit b49b374

8 files changed

Lines changed: 423 additions & 283 deletions

File tree

‎include/iris/colorize_format.hpp‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
#include <iris/config.hpp> // IWYU pragma: keep
77

88
#include <iris/string.hpp>
9-
#include <iris/enum_bitops.hpp>
10-
#include <iris/enum_bitops_algorithm.hpp>
9+
#include <iris/enum/enum.hpp>
1110
#include <iris/fixed_string.hpp>
1211

1312
#include <algorithm>
@@ -474,10 +473,13 @@ static constexpr std::uint8_t emphasis_to_value(emphasis em)
474473
} // ansi_colorize
475474

476475
template<>
477-
struct bitops_enabled<ansi_colorize::detail::emphasis> : std::true_type {
476+
struct enum_traits<ansi_colorize::detail::emphasis>
477+
{
478478
static constexpr int max_bit = 7;
479479
};
480480

481+
static_assert(Enum<ansi_colorize::detail::emphasis>);
482+
481483
namespace ansi_colorize {
482484

483485
struct colorize_style
@@ -672,7 +674,7 @@ struct colorizer
672674
style.fg_color = color;
673675

674676
} else if (auto emphasis = detail::name_to_emphasis(specifier); emphasis != detail::emphasis{}) {
675-
using namespace bitops_operators;
677+
using namespace enum_bitops_operators;
676678
style.emphasis |= emphasis;
677679

678680
} else if (specifier.starts_with("fg:")) {

‎include/iris/enum/enum.hpp‎

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
#ifndef IRIS_ZZ_ENUM_ENUM_HPP
2+
#define IRIS_ZZ_ENUM_ENUM_HPP
3+
4+
// SPDX-License-Identifier: MIT
5+
6+
#include <iris/config.hpp> // IWYU pragma: keep
7+
8+
#include <format>
9+
#include <limits>
10+
#include <ranges>
11+
#include <utility>
12+
#include <array>
13+
#include <concepts>
14+
#include <type_traits>
15+
#include <numbers>
16+
17+
#ifndef NDEBUG
18+
#include <bit>
19+
#endif
20+
21+
#include <cassert>
22+
#include <cstddef> // IWYU pragma: keep
23+
24+
namespace iris {
25+
26+
namespace detail {
27+
28+
struct enum_traits_not_defined {};
29+
30+
} // detail
31+
32+
template<class EnumT>
33+
struct enum_traits : detail::enum_traits_not_defined
34+
{};
35+
36+
template<class EnumT>
37+
concept Enum = std::is_enum_v<EnumT> && requires {
38+
typename enum_traits<EnumT>;
39+
requires !std::derived_from<enum_traits<EnumT>, detail::enum_traits_not_defined>;
40+
};
41+
42+
namespace detail {
43+
44+
template<class EnumT>
45+
concept enum_has_finite_minmax_element = requires {
46+
requires std::same_as<std::remove_const_t<decltype(enum_traits<EnumT>::min_element)>, EnumT>;
47+
requires std::same_as<std::remove_const_t<decltype(enum_traits<EnumT>::max_element)>, EnumT>;
48+
requires std::to_underlying(enum_traits<EnumT>::min_element) <= std::to_underlying(enum_traits<EnumT>::max_element);
49+
};
50+
51+
template<class EnumT>
52+
concept enum_has_min_bit = requires {
53+
requires std::same_as<std::remove_const_t<decltype(enum_traits<EnumT>::min_bit)>, int>;
54+
};
55+
template<class EnumT>
56+
concept enum_has_proper_min_bit = !requires { enum_traits<EnumT>::min_bit; } || enum_has_min_bit<EnumT>;
57+
58+
template<class EnumT>
59+
constexpr int enum_min_bit_v = 0;
60+
61+
template<class EnumT>
62+
requires enum_has_min_bit<EnumT>
63+
constexpr int enum_min_bit_v<EnumT> = enum_traits<EnumT>::min_bit;
64+
65+
66+
template<class EnumT>
67+
concept enum_has_max_bit = requires {
68+
requires std::same_as<std::remove_const_t<decltype(enum_traits<EnumT>::max_bit)>, int>;
69+
};
70+
template<class EnumT>
71+
concept enum_has_proper_max_bit = !requires { enum_traits<EnumT>::max_bit; } || enum_has_max_bit<EnumT>;
72+
73+
template<class EnumT>
74+
concept enum_has_finite_minmax_bit =
75+
std::unsigned_integral<std::underlying_type_t<EnumT>> &&
76+
enum_has_proper_min_bit<EnumT> &&
77+
enum_has_proper_max_bit<EnumT> &&
78+
requires {
79+
requires enum_min_bit_v<EnumT> <= enum_traits<EnumT>::max_bit;
80+
};
81+
82+
template<class EnumT>
83+
struct enum_count_impl;
84+
85+
template<class EnumT>
86+
requires detail::enum_has_finite_minmax_bit<EnumT>
87+
struct enum_count_impl<EnumT>
88+
: std::integral_constant<std::size_t, enum_traits<EnumT>::max_bit + 1 - detail::enum_min_bit_v<EnumT>>
89+
{};
90+
91+
template<class EnumT>
92+
requires detail::enum_has_finite_minmax_element<EnumT> && (!detail::enum_has_finite_minmax_bit<EnumT>)
93+
struct enum_count_impl<EnumT>
94+
: std::integral_constant<
95+
std::size_t,
96+
static_cast<std::size_t>(enum_traits<EnumT>::max_element) + 1uz -
97+
static_cast<std::size_t>(enum_traits<EnumT>::min_element)
98+
>
99+
{};
100+
101+
} // detail
102+
103+
104+
template<class EnumT>
105+
concept FiniteEnum = Enum<EnumT> && (detail::enum_has_finite_minmax_bit<EnumT> || detail::enum_has_finite_minmax_element<EnumT>);
106+
107+
template<FiniteEnum EnumT>
108+
constexpr std::size_t enum_count_v = detail::enum_count_impl<EnumT>::value;
109+
110+
template<class EnumT>
111+
concept BitopsEnabledEnum =
112+
Enum<EnumT> &&
113+
std::unsigned_integral<std::underlying_type_t<EnumT>> &&
114+
detail::enum_has_proper_min_bit<EnumT> &&
115+
detail::enum_has_proper_max_bit<EnumT>;
116+
117+
118+
template<Enum EnumT>
119+
[[nodiscard]] consteval auto enum_values() noexcept
120+
{
121+
static_assert(FiniteEnum<EnumT>);
122+
123+
if constexpr (detail::enum_has_finite_minmax_bit<EnumT>) {
124+
return []<int... Offsets>(std::integer_sequence<int, Offsets...>) noexcept {
125+
return std::array<EnumT, enum_count_v<EnumT>>{
126+
static_cast<EnumT>(1u << (detail::enum_min_bit_v<EnumT> + Offsets))...
127+
};
128+
}(std::make_integer_sequence<int, static_cast<int>(enum_count_v<EnumT>)>{});
129+
130+
} else {
131+
constexpr auto first_val = std::to_underlying(enum_traits<EnumT>::min_element);
132+
return []<class T, auto... Offsets>(std::integer_sequence<T, Offsets...>) noexcept {
133+
return std::array<EnumT, enum_count_v<EnumT>>{
134+
static_cast<EnumT>(first_val + Offsets)...
135+
};
136+
}(std::make_integer_sequence<std::underlying_type_t<EnumT>, enum_count_v<EnumT>>{});
137+
}
138+
}
139+
140+
141+
inline namespace enum_bitops_operators {
142+
143+
template<BitopsEnabledEnum EnumT>
144+
[[nodiscard]] constexpr EnumT operator~(EnumT a) noexcept
145+
{
146+
return static_cast<EnumT>(~std::to_underlying(a));
147+
}
148+
149+
template<BitopsEnabledEnum EnumT>
150+
[[nodiscard]] constexpr EnumT operator|(EnumT a, EnumT b) noexcept
151+
{
152+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
153+
return static_cast<EnumT>(std::to_underlying(a) | std::to_underlying(b));
154+
}
155+
156+
template<BitopsEnabledEnum EnumT>
157+
constexpr EnumT& operator|=(EnumT& a, EnumT b) noexcept
158+
{
159+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
160+
return a = a | b;
161+
}
162+
163+
template<BitopsEnabledEnum EnumT>
164+
[[nodiscard]] constexpr EnumT operator&(EnumT a, EnumT b) noexcept
165+
{
166+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
167+
return static_cast<EnumT>(std::to_underlying(a) & std::to_underlying(b));
168+
}
169+
170+
template<BitopsEnabledEnum EnumT>
171+
constexpr EnumT& operator&=(EnumT& a, EnumT b) noexcept
172+
{
173+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
174+
return a = a & b;
175+
}
176+
177+
template<BitopsEnabledEnum EnumT>
178+
[[nodiscard]] constexpr EnumT operator^(EnumT a, EnumT b) noexcept
179+
{
180+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
181+
return static_cast<EnumT>(std::to_underlying(a) ^ std::to_underlying(b));
182+
}
183+
184+
template<BitopsEnabledEnum EnumT>
185+
constexpr EnumT& operator^=(EnumT& a, EnumT b) noexcept
186+
{
187+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
188+
return a = a ^ b;
189+
}
190+
191+
} // enum_bitops_operators
192+
193+
template<BitopsEnabledEnum EnumT>
194+
[[nodiscard]] constexpr bool contains(EnumT a, EnumT b) noexcept
195+
{
196+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
197+
return (std::to_underlying(a) & std::to_underlying(b)) == std::to_underlying(b);
198+
}
199+
200+
template<BitopsEnabledEnum EnumT>
201+
[[nodiscard]] constexpr bool contains_single_bit(EnumT a, EnumT b) noexcept
202+
{
203+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
204+
assert(std::has_single_bit(std::to_underlying(b)));
205+
return std::to_underlying(a) & std::to_underlying(b);
206+
}
207+
208+
template<BitopsEnabledEnum EnumT>
209+
[[nodiscard]] constexpr bool contains_any_bit(EnumT a, EnumT b) noexcept
210+
{
211+
static_assert(std::is_unsigned_v<std::underlying_type_t<EnumT>>);
212+
return std::to_underlying(a) & std::to_underlying(b);
213+
}
214+
215+
216+
namespace detail {
217+
218+
struct each_bit_fn
219+
{
220+
template<FiniteEnum EnumT>
221+
requires
222+
BitopsEnabledEnum<EnumT> &&
223+
requires {
224+
requires enum_traits<EnumT>::max_bit < std::numeric_limits<std::underlying_type_t<EnumT>>::digits;
225+
}
226+
[[nodiscard]] static constexpr auto operator()(EnumT flags) noexcept
227+
{
228+
return std::views::iota(enum_min_bit_v<EnumT>, enum_traits<EnumT>::max_bit + 1)
229+
| std::views::filter([cat = std::to_underlying(flags)](int i) constexpr noexcept -> bool { return (cat >> i) & 1; })
230+
| std::views::transform([](int i) constexpr noexcept { return static_cast<EnumT>(static_cast<std::underlying_type_t<EnumT>>(1u) << i); });
231+
}
232+
};
233+
234+
} // detail
235+
236+
inline constexpr detail::each_bit_fn each_bit{};
237+
238+
} // iris
239+
240+
template<iris::Enum EnumT, class CharT>
241+
requires requires(EnumT const& val) {
242+
{ to_string(val) } -> std::formattable<CharT>;
243+
}
244+
struct std::formatter<EnumT, CharT> : std::formatter<std::underlying_type_t<EnumT>, CharT>
245+
{
246+
using base_formatter = std::formatter<std::underlying_type_t<EnumT>, CharT>;
247+
248+
template<class Context>
249+
constexpr auto parse(Context& ctx)
250+
{
251+
if (ctx.begin() == ctx.end()) return ctx.end();
252+
has_format_spec_ = true;
253+
return base_formatter::parse(ctx);
254+
}
255+
256+
template<class Context>
257+
auto format(EnumT const& val, Context& ctx) const
258+
{
259+
if (has_format_spec_) {
260+
return base_formatter::format(std::to_underlying(val), ctx);
261+
}
262+
return format_to(ctx.out(), "{}", to_string(val));
263+
}
264+
265+
private:
266+
bool has_format_spec_ = false;
267+
};
268+
269+
#endif

‎include/iris/enum/io.hpp‎

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#ifndef IRIS_ZZ_ENUM_IO_HPP
2+
#define IRIS_ZZ_ENUM_IO_HPP
3+
4+
// SPDX-License-Identifier: MIT
5+
6+
#include <iris/config.hpp> // IWYU pragma: keep
7+
8+
#include <iris/enum/enum.hpp>
9+
#include <iris/string.hpp>
10+
11+
#include <ostream> // IWYU pragma: keep
12+
#include <ranges>
13+
#include <string>
14+
#include <string_view>
15+
#include <utility>
16+
17+
namespace iris {
18+
19+
inline namespace enum_operators {
20+
21+
template<Enum T>
22+
std::ostream& operator<<(std::ostream& os, T const& val)
23+
{
24+
using std::to_string;
25+
26+
if constexpr (requires {
27+
{ os << to_string(val) } -> std::convertible_to<std::ostream&>;
28+
}) {
29+
return os << to_string(val);
30+
} else {
31+
return os << std::to_underlying(val);
32+
}
33+
}
34+
35+
} // enum_operators
36+
37+
template<BitopsEnabledEnum T>
38+
[[nodiscard]] constexpr T parse_flag(StringLike auto const& str) noexcept
39+
{
40+
return enum_traits<T>::parse(std::basic_string_view{str});
41+
}
42+
43+
template<BitopsEnabledEnum T>
44+
[[nodiscard]] constexpr T parse_flags(StringLike auto const& str, StringLike auto const& delim) noexcept
45+
{
46+
using namespace iris::enum_bitops_operators;
47+
48+
std::basic_string_view str_sv{str};
49+
std::basic_string_view delim_sv{delim};
50+
51+
T res{};
52+
53+
for (auto const& r : str_sv | std::views::split(delim_sv)) {
54+
std::basic_string_view const part_str{r.begin(), r.end()};
55+
56+
auto const part = iris::parse_flag<T>(part_str);
57+
if (part == T{}) {
58+
return T{};
59+
}
60+
res |= part;
61+
}
62+
63+
return res;
64+
}
65+
66+
} // iris
67+
68+
#endif

0 commit comments

Comments
 (0)