diff --git a/include/simple_enum/core.hpp b/include/simple_enum/core.hpp index b14a38d..4783a4c 100644 --- a/include/simple_enum/core.hpp +++ b/include/simple_enum/core.hpp @@ -6,7 +6,7 @@ #include #include -#define SIMPLE_ENUM_NAME_VERSION "0.9.2" +#define SIMPLE_ENUM_NAME_VERSION "0.9.3" namespace simple_enum::inline v0_9 { diff --git a/include/simple_enum/enum_bitfield.h b/include/simple_enum/enum_bitfield.h index 1106cd1..8e7dc84 100644 --- a/include/simple_enum/enum_bitfield.h +++ b/include/simple_enum/enum_bitfield.h @@ -4,11 +4,12 @@ #pragma once #include +#include #include "detail/static_call_operator_prolog.h" namespace simple_enum::inline v0_9 { -template +template struct enum_bitfield_t; namespace detail @@ -30,29 +31,23 @@ namespace detail return uint64_t{}; } - template - using select_storage_for_enum_t = decltype(select_storage_type>()); + template + using select_storage_for_enum_t = decltype(select_storage_type>()); + + constexpr bool test_bit(auto bits, size_t ix) noexcept { return ((bits >> ix) & 1u) != 0; } ///@brief A proxy object to get/set a specific bit in the parent bitfield. - template + template struct bit_proxy_t { - using storage_t = typename enum_bitfield_t::storage_t; + using storage_t = typename enum_bitfield_t::storage_t; parent_type & parent_; - enum_type_t value_; - - [[nodiscard]] - static constexpr auto get_offset(enum_type_t const value) noexcept -> std::size_t - { - // enum_size_v/select_storage_for_enum_t would not compile for invalid enum so testing for expected is redudant - // here - return *enum_index(value); - } + enumeration value_; constexpr auto operator=(std::same_as auto const bit_value) noexcept -> bit_proxy_t & { - auto const offset = get_offset(value_); + auto const offset = *enum_index(value_); auto const mask = storage_t(1) << offset; if(bit_value) @@ -66,7 +61,8 @@ namespace detail [[nodiscard]] constexpr operator bool() const noexcept { - return ((parent_.bits_ >> get_offset(value_)) & 1u) != 0; + return test_bit(parent_.bits_, *enum_index(value_)); + // return ((parent_.bits_ >> *enum_index(value_)) & 1u) != 0; } }; @@ -85,14 +81,118 @@ namespace detail template inline constexpr T bitmask_v = bitmask_t::value; + + template + struct enum_mask_impl_t + { + using mask_type = select_storage_for_enum_t; + static constexpr size_t size{enum_size_v}; + + template + static consteval auto op(mask_type init) noexcept -> mask_type + { + if constexpr(N != 0) + { + if constexpr(is_valid_enumeration_index_v(N - 1)) + init |= mask_type(mask_type(1u) << (N - 1)); + init |= op(init); + } + return init; + } + + static constexpr mask_type value{op(0)}; + }; + + template + inline constexpr auto enum_mask_v = enum_mask_impl_t::value; + + template + struct enum_bitfield_traits_t + { + using storage_t = detail::select_storage_for_enum_t; + // mask with eanbled bits of all valid mapped enumerations indexes, it does not enable indexes for holes in enum + static constexpr storage_t bits_mask{detail::enum_mask_v}; + }; + + template + struct enum_bitfield_iterator_t + { + using traits_type = detail::enum_bitfield_traits_t; + using storage_t = traits_type::storage_t; + static constexpr storage_t bits_mask{traits_type::bits_mask}; + + using iterator_category = std::forward_iterator_tag; + using value_type = enumeration; + using difference_type = std::ptrdiff_t; + using pointer = enumeration const *; + using reference = enumeration const &; + + storage_t bits_{}; + std::optional current_{}; + + constexpr enum_bitfield_iterator_t() noexcept = default; + + constexpr explicit enum_bitfield_iterator_t(storage_t bits) noexcept : bits_{bits}, current_{} + { + // move to first + static constexpr size_t size{enum_size_v}; + for(size_t ix{}; ix != size; ++ix) + if(test_bit(bits_, ix)) // no need to test if it is valid as mask will always maintain that + { + current_ = *emum_index_to_enumeration(ix); + break; + } + } + + constexpr auto operator*() const noexcept -> reference { return *current_; } + + constexpr auto operator->() const noexcept -> pointer { return current_->value(); } + + constexpr auto operator++() noexcept -> enum_bitfield_iterator_t & + { + if(current_) + { + static constexpr size_t size{enum_size_v}; + size_t ix{*enum_index(*current_)}; + current_.reset(); + for(++ix; ix != size; ++ix) + if(test_bit(bits_, ix)) // no need to test if it is valid as mask will always maintain that + { + current_ = *emum_index_to_enumeration(ix); + break; + } + } + return *this; + } + + constexpr auto operator++(int) noexcept -> enum_bitfield_iterator_t + { + enum_bitfield_iterator_t tmp{*this}; + ++(*this); + return tmp; + } + + constexpr auto operator==(enum_bitfield_iterator_t const & other) const noexcept -> bool = default; + }; } // namespace detail +enum struct enum_bitfield_full_e + { + }; + /// @brief A template struct providing a bitfield with enum indexing. -template +template struct enum_bitfield_t { - using storage_t = detail::select_storage_for_enum_t; - static constexpr storage_t bits_mask{detail::bitmask_v>}; + using traits_type = detail::enum_bitfield_traits_t; + using storage_t = traits_type::storage_t; + static constexpr storage_t bits_mask{traits_type::bits_mask}; + using iterator = detail::enum_bitfield_iterator_t; + + struct sentinel_t + { + constexpr auto operator==(iterator const & it) const noexcept { return not it.current_.has_value(); } + }; storage_t bits_{0}; @@ -102,31 +202,38 @@ struct enum_bitfield_t explicit constexpr enum_bitfield_t(std::same_as auto bits) noexcept : bits_{bits} {} - template... Args> + // constructs will full bits enabled + explicit constexpr enum_bitfield_t(enum_bitfield_full_e) noexcept : bits_{bits_mask} {} + + template... Args> constexpr explicit enum_bitfield_t(Args &&... args) noexcept { set_values(std::forward(args)...); } + constexpr auto begin() const noexcept -> iterator { return iterator{bits_}; } + + constexpr auto end() const noexcept -> sentinel_t { return sentinel_t{}; } + /** * @brief Accesses a bit corresponding to an index of enumeration value. * @returns A proxy object to manipulate the bit. */ template [[nodiscard]] - constexpr auto operator[](this Self && self, enum_type_t const value) noexcept + constexpr auto operator[](this Self && self, enumeration const value) noexcept { return detail::bit_proxy_t{self, value}; } - template... Args> - constexpr void set_values(enum_type_t const & arg, Args &&... args) noexcept + template... Args> + constexpr void set_values(enumeration const & arg, Args &&... args) noexcept { detail::bit_proxy_t{*this, arg} = true; set_values(std::forward(args)...); } - constexpr void set_values(enum_type_t const & arg) noexcept { detail::bit_proxy_t{*this, arg} = true; } + constexpr void set_values(enumeration const & arg) noexcept { detail::bit_proxy_t{*this, arg} = true; } [[nodiscard]] constexpr auto operator==(enum_bitfield_t const &) const noexcept -> bool @@ -175,11 +282,11 @@ struct enum_bitfield_t } }; -template -enum_bitfield_t(enum_type_t const & arg) -> enum_bitfield_t; +template +enum_bitfield_t(enumeration const & arg) -> enum_bitfield_t; -template -enum_bitfield_t(enum_type_t const & arg, Args &&... args) -> enum_bitfield_t; +template +enum_bitfield_t(enumeration const & arg, Args &&... args) -> enum_bitfield_t; } // namespace simple_enum::inline v0_9 diff --git a/include/simple_enum/enum_index.hpp b/include/simple_enum/enum_index.hpp index 94bb562..873c2ef 100644 --- a/include/simple_enum/enum_index.hpp +++ b/include/simple_enum/enum_index.hpp @@ -79,6 +79,28 @@ consteval auto consteval_enum_index() -> std::size_t return enum_index(value).or_else([](auto &&) { throw; }); } +/// @brief enumeration value for 0 - based index +template +struct emum_index_to_enumeration_t + { + [[nodiscard]] + static_call_operator constexpr auto operator()(std::size_t index) static_call_operator_const noexcept + -> cxx23::expected + { + using enum_meta_info = detail::enum_meta_info_t; + auto const requested_value{enum_meta_info::first_index() + std::underlying_type_t(index)}; + + if(requested_value <= enum_meta_info::last_index()) [[likely]] + return static_cast(requested_value); + else + return cxx23::unexpected{enum_index_error::out_of_range}; + } + }; + +/// @brief enumeration value for 0 - based index +template +inline constexpr emum_index_to_enumeration_t emum_index_to_enumeration; + /// @brief Provides compile time information of length of enumeration (including holes). template struct enum_size_t @@ -90,6 +112,51 @@ struct enum_size_t template inline constexpr std::size_t enum_size_v = enum_size_t::value; +namespace detail + { + [[nodiscard]] + constexpr bool isdigit(char src) noexcept + { + unsigned c{static_cast(src)}; + return c >= 48u && c <= 57u; + } + } // namespace detail + +template +struct is_valid_enumeration_index_t + { + static_call_operator constexpr auto operator()(size_t index) static_call_operator_const noexcept -> bool + { + using enum_meta_info = detail::enum_meta_info_t; + + if(index < enum_meta_info::size()) + { + detail::meta_name const & res{enum_meta_info::meta_data[index]}; + return res.is_valid; + } + else + return false; + } + }; + +template +inline constexpr is_valid_enumeration_index_t is_valid_enumeration_index_v; + +struct is_valid_enumeration_value_t + { + template + static_call_operator constexpr auto operator()(enum_type value) static_call_operator_const noexcept -> bool + { + using enum_meta_info = detail::enum_meta_info_t; + auto const requested_value{simple_enum::detail::to_underlying(value)}; + if(requested_value >= enum_meta_info::first_index()) + return is_valid_enumeration_index_t{}(size_t(requested_value - enum_meta_info::first_index())); + else + return false; + } + }; + +inline constexpr is_valid_enumeration_value_t is_valid_enumeration_value_v; } // namespace simple_enum::inline v0_9 #include "detail/static_call_operator_epilog.h" diff --git a/include/simple_enum/simple_enum.hpp b/include/simple_enum/simple_enum.hpp index 48a1dda..01bfae3 100644 --- a/include/simple_enum/simple_enum.hpp +++ b/include/simple_enum/simple_enum.hpp @@ -130,7 +130,7 @@ enum struct verify_ennum_ constexpr size_t find_enumeration_offset() { auto const func{std::string_view{f()}}; -#if defined(_MSC_VER) +#if defined(_MSC_VER) and not defined(__clang__) size_t pos = func.find('<'); if(pos == std::string_view::npos) throw; @@ -156,7 +156,8 @@ namespace detail struct meta_name { char const * data; - size_t size; + uint32_t size; + bool is_valid; constexpr operator std::string_view() const noexcept { return std::string_view{data, size}; } @@ -252,7 +253,8 @@ namespace detail #endif // Calculate the size and set the result result.data = prev_colon; - result.size = size_t(current_colon - prev_colon); + result.size = uint32_t(current_colon - prev_colon); + result.is_valid = true; } template @@ -278,7 +280,10 @@ namespace detail #endif char const * const func{se::f()}; char const * end_of_name{func + se::initial_offset}; + res.is_valid = *(end_of_name + 1) != '('; + char const * last_colon{end_of_name}; + #ifdef _MSC_VER size_t was_undefined{}; #endif @@ -297,7 +302,8 @@ namespace detail #endif res.data = last_colon + 1; - res.size = size_t(end_of_name - res.data); + res.size = uint32_t(end_of_name - res.data); + #ifdef __clang__ #pragma clang unsafe_buffer_usage end #endif @@ -315,15 +321,18 @@ namespace detail #pragma clang unsafe_buffer_usage begin #endif char const * const func{se::f()}; + res.is_valid = *(func + se::initial_offset + 1) != '('; char const * end_of_name{func + enum_beg}; char const * enumeration_name{end_of_name}; - while(*end_of_name != se::end_of_enumeration_name) - ++end_of_name; // for other enumerations we only need to find end of string + if(res.is_valid) + while(*end_of_name != se::end_of_enumeration_name) + ++end_of_name; // for other enumerations we only need to find end of string + #ifdef __clang__ #pragma clang unsafe_buffer_usage end #endif res.data = enumeration_name; - res.size = size_t(end_of_name - res.data); + res.size = uint32_t(end_of_name - res.data); } template @@ -335,7 +344,7 @@ namespace detail } template - consteval auto prepare_enum_meta_info() noexcept -> std::array + constexpr auto prepare_enum_meta_info() noexcept -> std::array { constexpr std::size_t size_{last_index - first_index + 1}; std::array meta; diff --git a/module/simple_enum.cxx b/module/simple_enum.cxx index aeb950e..077dc45 100644 --- a/module/simple_enum.cxx +++ b/module/simple_enum.cxx @@ -48,6 +48,13 @@ using simple_enum::enum_index_t; using simple_enum::enum_size_t; using simple_enum::enum_size_v; +using simple_enum::emum_index_to_enumeration; +using simple_enum::emum_index_to_enumeration_t; +using simple_enum::is_valid_enumeration_index_t; +using simple_enum::is_valid_enumeration_index_v; +using simple_enum::is_valid_enumeration_value_t; +using simple_enum::is_valid_enumeration_value_v; + using simple_enum::enum_cast; using simple_enum::enum_cast_error; using simple_enum::enum_cast_t; @@ -72,5 +79,15 @@ using simple_enum::begin; using simple_enum::end; using simple_enum::enum_names; +namespace detail + { + using simple_enum::detail::bit_proxy_t; + using simple_enum::detail::bitmask_t; + using simple_enum::detail::enum_bitfield_iterator_t; + using simple_enum::detail::enum_bitfield_traits_t; + using simple_enum::detail::enum_mask_v; + } // namespace detail + +using simple_enum::enum_bitfield_full_e; using simple_enum::enum_bitfield_t; } // namespace simple_enum diff --git a/tests/diagnostics.cc b/tests/diagnostics.cc index 320dbe5..a3ad58e 100644 --- a/tests/diagnostics.cc +++ b/tests/diagnostics.cc @@ -79,7 +79,7 @@ enum struct strong_typed : uint8_t { v1 = 1, v2, - v3, + v3 = 4, first = v1, last = v3 }; @@ -87,7 +87,7 @@ enum struct strong_untyped { v1 = 1, v2, - v3, + v3 = 4, first = v1, last = v3 }; @@ -140,9 +140,13 @@ int main() { using std::cout, std::endl; print_compiler_info(); + std::cout << "\t" << se::f() << std::endl; + std::cout << "\t" << se::f() << std::endl; std::cout << "\t" << se::f() << std::endl; std::cout << "\t" << se::f() << std::endl; + std::cout << "\t" << se::f() << std::endl; std::cout << "\t" << se::f() << std::endl; + std::cout << "\t" << se::f() << std::endl; std::cout << "\t" << se::f() << std::endl; std::cout << "\t" << se::f() << std::endl; std::cout << "\t" << se::f() << std::endl; diff --git a/tests/diagnostics_data.txt b/tests/diagnostics_data.txt index 8122b30..297195d 100644 --- a/tests/diagnostics_data.txt +++ b/tests/diagnostics_data.txt @@ -1,3 +1,19 @@ +C++ Standard: 199711 +Compiler: MSVC 194435208 +Standard Library: MSVC STL 143 + auto __cdecl se::f(void) noexcept + auto __cdecl se::f<(enum strong_typed)0x3>(void) noexcept + auto __cdecl se::f<(enum strong_typed)0x0>(void) noexcept + auto __cdecl se::f<(enum strong_untyped)0x0>(void) noexcept + auto __cdecl se::f(void) noexcept + auto __cdecl se::f<(enum weak_untyped_e)0x0>(void) noexcept + auto __cdecl se::f(void) noexcept + auto __cdecl se::f<(enum test::strong_typed_2_e)0x0>(void) noexcept + auto __cdecl se::f<(enum test::subnamespace::example_3_e)0x0>(void) noexcept + auto __cdecl se::f<(enum test::subnamespace::detail::`anonymous-namespace'::example_4_e)0x0>(void) noexcept + auto __cdecl se::f<(enum test::subnamespace::v2_0::`anonymous-namespace'::example_5_e)0x0>(void) noexcept + auto __cdecl se::f<(enum test::subnamespace::v2_0::`anonymous-namespace'::example_5_e)0x0>(void) noexcept + C++ Standard: 199711 Compiler: MSVC 193632537 Standard Library: MSVC STL 143 diff --git a/tests/enum_bitfield_ut.cc b/tests/enum_bitfield_ut.cc index cea149c..cd49bd1 100644 --- a/tests/enum_bitfield_ut.cc +++ b/tests/enum_bitfield_ut.cc @@ -5,7 +5,9 @@ #include #endif #include "simple_enum_tests.hpp" +#include +using simple_enum::detail::enum_mask_v; using simple_enum::enum_bitfield_t; using simple_enum::enum_size_v; @@ -22,15 +24,23 @@ enum struct medium_enum_t { v0 = 5, v1, v2, v3, v4, v5, v6, v7, v8, first = v0, // Enum that should use uint64_t (40 values) enum struct large_enum_t { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, - v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v10, v11, v12, /*v13,*/ v14=14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, - v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v30, v31, v32, /*v33,*/ v34 = 34, v35, v36, v37, v38, v39, first = v0, last = v39 }; + +static_assert( enum_mask_v == 0b1111); +static_assert( enum_mask_v == 0b1000000010001); +static_assert( enum_mask_v == 0b11111101'11111111'11111111'11011111'11111111 +); // clang-format on int main() { + auto obj{enum_mask_v}; + std::cout << "-->[" << std::format("{:b}", obj) << std::endl; + "enum_bitfield"_test = [] { "bitfield_logic"_test = [] @@ -146,6 +156,13 @@ int main() expect(large_bf[v1]); expect(not large_bf[v2]); + + enum_bitfield_t neg{~large_bf}; + enum_bitfield_t exp_neg{v0, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v14, v15, v16, v18, v19, v20, + v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v32, v34, v36, v37, v38, v39}; + expect(neg == exp_neg); + enum_bitfield_t neg2{~neg}; + expect(large_bf == neg2); } }; @@ -297,6 +314,45 @@ int main() }; static_assert(fn_b() == enum_bitfield_t{v21, v16}); }; + + "range"_test = [] + { + using enum large_enum_t; + { + enum_bitfield_t a{v36}; + auto it{a.begin()}; + expect(*it == v36); + ++it; + expect(a.end() == it); + expect(it == a.end()); + } + { + enum_bitfield_t a{}; + auto it{a.begin()}; + expect(a.end() == it); + expect(it == a.end()); + } + { + enum_bitfield_t a{v12, v23, v38, v39}; + auto it{a.begin()}; + expect(eq(*it, v12)); + ++it; + expect(eq(*it, v23)); + ++it; + expect(eq(*it, v38)); + ++it; + expect(eq(*it, v39)); + ++it; + expect(a.end() == it); + expect(it == a.end()); + } + { + enum_bitfield_t a{v12, v23, v38, v39}; + std::vector res; + std::ranges::copy(a, std::back_inserter(res)); + expect(std::ranges::equal(res, std::array{v12, v23, v38, v39})); + } + }; }; } diff --git a/tests/enum_index_ut.cc b/tests/enum_index_ut.cc index b293ca5..fb290d9 100644 --- a/tests/enum_index_ut.cc +++ b/tests/enum_index_ut.cc @@ -6,9 +6,11 @@ #endif #include "simple_enum_tests.hpp" +using simple_enum::emum_index_to_enumeration; using simple_enum::enum_index; using simple_enum::enum_index_error; using simple_enum::enum_size_v; +using simple_enum::is_valid_enumeration_index_v; enum struct E1 : uint8_t { @@ -30,13 +32,43 @@ enum struct E2 : uint8_t first = _1, last = _5 }; + +enum struct E3 : int8_t + { + _1 = -3, + _2, // -2 ix1 + _3, // -1 ix2 + _4, // 0 ix3 + // _5, hole in enum ix4 + _6 = 2, // 2 ix5 + first = _1, + last = _6 + }; + static_assert(enum_size_v == 16); static_assert(enum_size_v == 3); static_assert(enum_size_v == 5); static_assert(enum_size_v == 5); +static_assert(enum_size_v == 6); + +static_assert(emum_index_to_enumeration(0).value() == E3::_1); +static_assert(emum_index_to_enumeration(2).value() == E3::_3); +static_assert(emum_index_to_enumeration(5).value() == E3::_6); +static_assert(emum_index_to_enumeration(15).error() == enum_index_error::out_of_range); + +static_assert(is_valid_enumeration_index_v(0)); +static_assert(is_valid_enumeration_index_v(1)); +static_assert(is_valid_enumeration_index_v(5)); +// static_assert(not is_valid_enumeration_index_v(4)); +static_assert(not is_valid_enumeration_index_v(7)); int main() { + // auto data{ simple_enum::detail::enum_meta_info_t::meta_data}; + expect(not is_valid_enumeration_index_v(4)); + + (void)is_valid_enumeration_index_v(4); + "enum_index valid"_test = [] { using std::to_underlying; diff --git a/tests/test_simple_enum.cc b/tests/test_simple_enum.cc index 63b5601..e945dc4 100644 --- a/tests/test_simple_enum.cc +++ b/tests/test_simple_enum.cc @@ -29,6 +29,7 @@ consteval auto adl_enum_bounds(enum_bounded) { return simple_enum::adl_info{enum #ifndef SIMPLE_ENUM_CXX_MODULE static_assert(simple_enum::detail::enum_meta_info_t::first() == enum_bounded::v1); static_assert(simple_enum::detail::enum_meta_info_t::last() == enum_bounded::v3); + #endif static_assert(simple_enum::limits::min() == enum_bounded::v1); @@ -258,19 +259,42 @@ enum struct one_elem_untyped }; enum struct sparse_untyped { - v1 = 1, - v3 = 3, + v1 = 1, // ix 0 + // 2 ix 1 + v3 = 3, // ix 2 first = v1, last = v3 }; +#ifndef SIMPLE_ENUM_CXX_MODULE +static_assert(simple_enum::detail::enum_meta_info_t::first() == sparse_untyped::v1); +static_assert(simple_enum::detail::enum_meta_info_t::last() == sparse_untyped::v3); +static_assert(simple_enum::detail::enum_meta_info_t::meta_data[0].is_valid); +static_assert(not simple_enum::detail::enum_meta_info_t::meta_data[1].is_valid); +static_assert(simple_enum::detail::enum_meta_info_t::meta_data[2].is_valid); +#endif enum struct sparse_offseted_untyped { - unknown = -1, - v1 = 1, - v3 = 3, + unknown = -1, // ix 0 + // 0 ix 1 + v1 = 1, // ix 2 + // 2 ix 3 + v3 = 3, // ix 4 first = unknown, // simulate counting below the range last = v3 }; + +#ifndef SIMPLE_ENUM_CXX_MODULE +static_assert( + simple_enum::detail::enum_meta_info_t::first() == sparse_offseted_untyped::unknown +); +static_assert(simple_enum::detail::enum_meta_info_t::last() == sparse_offseted_untyped::v3); +static_assert(simple_enum::detail::enum_meta_info_t::meta_data[0].is_valid); +static_assert(not simple_enum::detail::enum_meta_info_t::meta_data[1].is_valid); +static_assert(simple_enum::detail::enum_meta_info_t::meta_data[2].is_valid); +static_assert(not simple_enum::detail::enum_meta_info_t::meta_data[3].is_valid); +static_assert(simple_enum::detail::enum_meta_info_t::meta_data[4].is_valid); +#endif + #ifndef SIMPLE_ENUM_CXX_MODULE using detail::cont_pass; using detail::first_pass;