Skip to content

Commit 805e85f

Browse files
authored
bitmask as a range, sparse enum handling in bitmask (#31)
1 parent 6d0eccc commit 805e85f

10 files changed

Lines changed: 378 additions & 46 deletions

File tree

include/simple_enum/core.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <concepts>
77
#include <type_traits>
88

9-
#define SIMPLE_ENUM_NAME_VERSION "0.9.2"
9+
#define SIMPLE_ENUM_NAME_VERSION "0.9.3"
1010

1111
namespace simple_enum::inline v0_9
1212
{

include/simple_enum/enum_bitfield.h

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
#pragma once
55

66
#include <simple_enum/enum_index.hpp>
7+
#include <optional>
78
#include "detail/static_call_operator_prolog.h"
89

910
namespace simple_enum::inline v0_9
1011
{
11-
template<enum_concept enum_type_t>
12+
template<enum_concept enumeration>
1213
struct enum_bitfield_t;
1314

1415
namespace detail
@@ -30,29 +31,23 @@ namespace detail
3031
return uint64_t{};
3132
}
3233

33-
template<enum_concept enum_type_t>
34-
using select_storage_for_enum_t = decltype(select_storage_type<enum_size_v<enum_type_t>>());
34+
template<enum_concept enumeration>
35+
using select_storage_for_enum_t = decltype(select_storage_type<enum_size_v<enumeration>>());
36+
37+
constexpr bool test_bit(auto bits, size_t ix) noexcept { return ((bits >> ix) & 1u) != 0; }
3538

3639
///@brief A proxy object to get/set a specific bit in the parent bitfield.
37-
template<typename parent_type, enum_concept enum_type_t>
40+
template<typename parent_type, enum_concept enumeration>
3841
struct bit_proxy_t
3942
{
40-
using storage_t = typename enum_bitfield_t<enum_type_t>::storage_t;
43+
using storage_t = typename enum_bitfield_t<enumeration>::storage_t;
4144

4245
parent_type & parent_;
43-
enum_type_t value_;
44-
45-
[[nodiscard]]
46-
static constexpr auto get_offset(enum_type_t const value) noexcept -> std::size_t
47-
{
48-
// enum_size_v/select_storage_for_enum_t would not compile for invalid enum so testing for expected is redudant
49-
// here
50-
return *enum_index(value);
51-
}
46+
enumeration value_;
5247

5348
constexpr auto operator=(std::same_as<bool> auto const bit_value) noexcept -> bit_proxy_t &
5449
{
55-
auto const offset = get_offset(value_);
50+
auto const offset = *enum_index(value_);
5651
auto const mask = storage_t(1) << offset;
5752

5853
if(bit_value)
@@ -66,7 +61,8 @@ namespace detail
6661
[[nodiscard]]
6762
constexpr operator bool() const noexcept
6863
{
69-
return ((parent_.bits_ >> get_offset(value_)) & 1u) != 0;
64+
return test_bit(parent_.bits_, *enum_index(value_));
65+
// return ((parent_.bits_ >> *enum_index(value_)) & 1u) != 0;
7066
}
7167
};
7268

@@ -85,14 +81,118 @@ namespace detail
8581

8682
template<std::unsigned_integral T, std::size_t N>
8783
inline constexpr T bitmask_v = bitmask_t<T, N>::value;
84+
85+
template<enum_concept enumeration>
86+
struct enum_mask_impl_t
87+
{
88+
using mask_type = select_storage_for_enum_t<enumeration>;
89+
static constexpr size_t size{enum_size_v<enumeration>};
90+
91+
template<size_t N>
92+
static consteval auto op(mask_type init) noexcept -> mask_type
93+
{
94+
if constexpr(N != 0)
95+
{
96+
if constexpr(is_valid_enumeration_index_v<enumeration>(N - 1))
97+
init |= mask_type(mask_type(1u) << (N - 1));
98+
init |= op<N - 1>(init);
99+
}
100+
return init;
101+
}
102+
103+
static constexpr mask_type value{op<size>(0)};
104+
};
105+
106+
template<enum_concept enumeration>
107+
inline constexpr auto enum_mask_v = enum_mask_impl_t<enumeration>::value;
108+
109+
template<enum_concept enum_type_t>
110+
struct enum_bitfield_traits_t
111+
{
112+
using storage_t = detail::select_storage_for_enum_t<enum_type_t>;
113+
// mask with eanbled bits of all valid mapped enumerations indexes, it does not enable indexes for holes in enum
114+
static constexpr storage_t bits_mask{detail::enum_mask_v<enum_type_t>};
115+
};
116+
117+
template<enum_concept enumeration>
118+
struct enum_bitfield_iterator_t
119+
{
120+
using traits_type = detail::enum_bitfield_traits_t<enumeration>;
121+
using storage_t = traits_type::storage_t;
122+
static constexpr storage_t bits_mask{traits_type::bits_mask};
123+
124+
using iterator_category = std::forward_iterator_tag;
125+
using value_type = enumeration;
126+
using difference_type = std::ptrdiff_t;
127+
using pointer = enumeration const *;
128+
using reference = enumeration const &;
129+
130+
storage_t bits_{};
131+
std::optional<enumeration> current_{};
132+
133+
constexpr enum_bitfield_iterator_t() noexcept = default;
134+
135+
constexpr explicit enum_bitfield_iterator_t(storage_t bits) noexcept : bits_{bits}, current_{}
136+
{
137+
// move to first
138+
static constexpr size_t size{enum_size_v<enumeration>};
139+
for(size_t ix{}; ix != size; ++ix)
140+
if(test_bit(bits_, ix)) // no need to test if it is valid as mask will always maintain that
141+
{
142+
current_ = *emum_index_to_enumeration<enumeration>(ix);
143+
break;
144+
}
145+
}
146+
147+
constexpr auto operator*() const noexcept -> reference { return *current_; }
148+
149+
constexpr auto operator->() const noexcept -> pointer { return current_->value(); }
150+
151+
constexpr auto operator++() noexcept -> enum_bitfield_iterator_t &
152+
{
153+
if(current_)
154+
{
155+
static constexpr size_t size{enum_size_v<enumeration>};
156+
size_t ix{*enum_index(*current_)};
157+
current_.reset();
158+
for(++ix; ix != size; ++ix)
159+
if(test_bit(bits_, ix)) // no need to test if it is valid as mask will always maintain that
160+
{
161+
current_ = *emum_index_to_enumeration<enumeration>(ix);
162+
break;
163+
}
164+
}
165+
return *this;
166+
}
167+
168+
constexpr auto operator++(int) noexcept -> enum_bitfield_iterator_t
169+
{
170+
enum_bitfield_iterator_t tmp{*this};
171+
++(*this);
172+
return tmp;
173+
}
174+
175+
constexpr auto operator==(enum_bitfield_iterator_t const & other) const noexcept -> bool = default;
176+
};
88177
} // namespace detail
89178

179+
enum struct enum_bitfield_full_e
180+
{
181+
};
182+
90183
/// @brief A template struct providing a bitfield with enum indexing.
91-
template<enum_concept enum_type_t>
184+
template<enum_concept enumeration>
92185
struct enum_bitfield_t
93186
{
94-
using storage_t = detail::select_storage_for_enum_t<enum_type_t>;
95-
static constexpr storage_t bits_mask{detail::bitmask_v<storage_t, enum_size_v<enum_type_t>>};
187+
using traits_type = detail::enum_bitfield_traits_t<enumeration>;
188+
using storage_t = traits_type::storage_t;
189+
static constexpr storage_t bits_mask{traits_type::bits_mask};
190+
using iterator = detail::enum_bitfield_iterator_t<enumeration>;
191+
192+
struct sentinel_t
193+
{
194+
constexpr auto operator==(iterator const & it) const noexcept { return not it.current_.has_value(); }
195+
};
96196

97197
storage_t bits_{0};
98198

@@ -102,31 +202,38 @@ struct enum_bitfield_t
102202

103203
explicit constexpr enum_bitfield_t(std::same_as<storage_t> auto bits) noexcept : bits_{bits} {}
104204

105-
template<std::same_as<enum_type_t>... Args>
205+
// constructs will full bits enabled
206+
explicit constexpr enum_bitfield_t(enum_bitfield_full_e) noexcept : bits_{bits_mask} {}
207+
208+
template<std::same_as<enumeration>... Args>
106209
constexpr explicit enum_bitfield_t(Args &&... args) noexcept
107210
{
108211
set_values(std::forward<Args>(args)...);
109212
}
110213

214+
constexpr auto begin() const noexcept -> iterator { return iterator{bits_}; }
215+
216+
constexpr auto end() const noexcept -> sentinel_t { return sentinel_t{}; }
217+
111218
/**
112219
* @brief Accesses a bit corresponding to an index of enumeration value.
113220
* @returns A proxy object to manipulate the bit.
114221
*/
115222
template<typename Self>
116223
[[nodiscard]]
117-
constexpr auto operator[](this Self && self, enum_type_t const value) noexcept
224+
constexpr auto operator[](this Self && self, enumeration const value) noexcept
118225
{
119226
return detail::bit_proxy_t{self, value};
120227
}
121228

122-
template<std::same_as<enum_type_t>... Args>
123-
constexpr void set_values(enum_type_t const & arg, Args &&... args) noexcept
229+
template<std::same_as<enumeration>... Args>
230+
constexpr void set_values(enumeration const & arg, Args &&... args) noexcept
124231
{
125232
detail::bit_proxy_t{*this, arg} = true;
126233
set_values(std::forward<Args>(args)...);
127234
}
128235

129-
constexpr void set_values(enum_type_t const & arg) noexcept { detail::bit_proxy_t{*this, arg} = true; }
236+
constexpr void set_values(enumeration const & arg) noexcept { detail::bit_proxy_t{*this, arg} = true; }
130237

131238
[[nodiscard]]
132239
constexpr auto operator==(enum_bitfield_t const &) const noexcept -> bool
@@ -175,11 +282,11 @@ struct enum_bitfield_t
175282
}
176283
};
177284

178-
template<enum_concept enum_type_t>
179-
enum_bitfield_t(enum_type_t const & arg) -> enum_bitfield_t<enum_type_t>;
285+
template<enum_concept enumeration>
286+
enum_bitfield_t(enumeration const & arg) -> enum_bitfield_t<enumeration>;
180287

181-
template<enum_concept enum_type_t, typename... Args>
182-
enum_bitfield_t(enum_type_t const & arg, Args &&... args) -> enum_bitfield_t<enum_type_t>;
288+
template<enum_concept enumeration, typename... Args>
289+
enum_bitfield_t(enumeration const & arg, Args &&... args) -> enum_bitfield_t<enumeration>;
183290

184291
} // namespace simple_enum::inline v0_9
185292

include/simple_enum/enum_index.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,28 @@ consteval auto consteval_enum_index() -> std::size_t
7979
return enum_index(value).or_else([](auto &&) { throw; });
8080
}
8181

82+
/// @brief enumeration value for 0 - based index
83+
template<enum_concept enum_type>
84+
struct emum_index_to_enumeration_t
85+
{
86+
[[nodiscard]]
87+
static_call_operator constexpr auto operator()(std::size_t index) static_call_operator_const noexcept
88+
-> cxx23::expected<enum_type, enum_index_error>
89+
{
90+
using enum_meta_info = detail::enum_meta_info_t<enum_type>;
91+
auto const requested_value{enum_meta_info::first_index() + std::underlying_type_t<enum_type>(index)};
92+
93+
if(requested_value <= enum_meta_info::last_index()) [[likely]]
94+
return static_cast<enum_type>(requested_value);
95+
else
96+
return cxx23::unexpected{enum_index_error::out_of_range};
97+
}
98+
};
99+
100+
/// @brief enumeration value for 0 - based index
101+
template<enum_concept enum_type>
102+
inline constexpr emum_index_to_enumeration_t<enum_type> emum_index_to_enumeration;
103+
82104
/// @brief Provides compile time information of length of enumeration (including holes).
83105
template<enum_concept enum_type>
84106
struct enum_size_t
@@ -90,6 +112,51 @@ struct enum_size_t
90112
template<enum_concept enum_type>
91113
inline constexpr std::size_t enum_size_v = enum_size_t<enum_type>::value;
92114

115+
namespace detail
116+
{
117+
[[nodiscard]]
118+
constexpr bool isdigit(char src) noexcept
119+
{
120+
unsigned c{static_cast<unsigned>(src)};
121+
return c >= 48u && c <= 57u;
122+
}
123+
} // namespace detail
124+
125+
template<enum_concept enum_type>
126+
struct is_valid_enumeration_index_t
127+
{
128+
static_call_operator constexpr auto operator()(size_t index) static_call_operator_const noexcept -> bool
129+
{
130+
using enum_meta_info = detail::enum_meta_info_t<enum_type>;
131+
132+
if(index < enum_meta_info::size())
133+
{
134+
detail::meta_name const & res{enum_meta_info::meta_data[index]};
135+
return res.is_valid;
136+
}
137+
else
138+
return false;
139+
}
140+
};
141+
142+
template<enum_concept enum_type>
143+
inline constexpr is_valid_enumeration_index_t<enum_type> is_valid_enumeration_index_v;
144+
145+
struct is_valid_enumeration_value_t
146+
{
147+
template<enum_concept enum_type>
148+
static_call_operator constexpr auto operator()(enum_type value) static_call_operator_const noexcept -> bool
149+
{
150+
using enum_meta_info = detail::enum_meta_info_t<enum_type>;
151+
auto const requested_value{simple_enum::detail::to_underlying(value)};
152+
if(requested_value >= enum_meta_info::first_index())
153+
return is_valid_enumeration_index_t<enum_type>{}(size_t(requested_value - enum_meta_info::first_index()));
154+
else
155+
return false;
156+
}
157+
};
158+
159+
inline constexpr is_valid_enumeration_value_t is_valid_enumeration_value_v;
93160
} // namespace simple_enum::inline v0_9
94161

95162
#include "detail/static_call_operator_epilog.h"

0 commit comments

Comments
 (0)