Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/simple_enum/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <concepts>
#include <type_traits>

#define SIMPLE_ENUM_NAME_VERSION "0.9.2"
#define SIMPLE_ENUM_NAME_VERSION "0.9.3"

namespace simple_enum::inline v0_9
{
Expand Down
163 changes: 135 additions & 28 deletions include/simple_enum/enum_bitfield.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
#pragma once

#include <simple_enum/enum_index.hpp>
#include <optional>
#include "detail/static_call_operator_prolog.h"

namespace simple_enum::inline v0_9
{
template<enum_concept enum_type_t>
template<enum_concept enumeration>
struct enum_bitfield_t;

namespace detail
Expand All @@ -30,29 +31,23 @@ namespace detail
return uint64_t{};
}

template<enum_concept enum_type_t>
using select_storage_for_enum_t = decltype(select_storage_type<enum_size_v<enum_type_t>>());
template<enum_concept enumeration>
using select_storage_for_enum_t = decltype(select_storage_type<enum_size_v<enumeration>>());

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<typename parent_type, enum_concept enum_type_t>
template<typename parent_type, enum_concept enumeration>
struct bit_proxy_t
{
using storage_t = typename enum_bitfield_t<enum_type_t>::storage_t;
using storage_t = typename enum_bitfield_t<enumeration>::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<bool> 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)
Expand All @@ -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;
}
};

Expand All @@ -85,14 +81,118 @@ namespace detail

template<std::unsigned_integral T, std::size_t N>
inline constexpr T bitmask_v = bitmask_t<T, N>::value;

template<enum_concept enumeration>
struct enum_mask_impl_t
{
using mask_type = select_storage_for_enum_t<enumeration>;
static constexpr size_t size{enum_size_v<enumeration>};

template<size_t N>
static consteval auto op(mask_type init) noexcept -> mask_type
{
if constexpr(N != 0)
{
if constexpr(is_valid_enumeration_index_v<enumeration>(N - 1))
init |= mask_type(mask_type(1u) << (N - 1));
init |= op<N - 1>(init);
}
return init;
}

static constexpr mask_type value{op<size>(0)};
};

template<enum_concept enumeration>
inline constexpr auto enum_mask_v = enum_mask_impl_t<enumeration>::value;

template<enum_concept enum_type_t>
struct enum_bitfield_traits_t
{
using storage_t = detail::select_storage_for_enum_t<enum_type_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<enum_type_t>};
};

template<enum_concept enumeration>
struct enum_bitfield_iterator_t
{
using traits_type = detail::enum_bitfield_traits_t<enumeration>;
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<enumeration> 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<enumeration>};
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<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<enumeration>};
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<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<enum_concept enum_type_t>
template<enum_concept enumeration>
struct enum_bitfield_t
{
using storage_t = detail::select_storage_for_enum_t<enum_type_t>;
static constexpr storage_t bits_mask{detail::bitmask_v<storage_t, enum_size_v<enum_type_t>>};
using traits_type = detail::enum_bitfield_traits_t<enumeration>;
using storage_t = traits_type::storage_t;
static constexpr storage_t bits_mask{traits_type::bits_mask};
using iterator = detail::enum_bitfield_iterator_t<enumeration>;

struct sentinel_t
{
constexpr auto operator==(iterator const & it) const noexcept { return not it.current_.has_value(); }
};

storage_t bits_{0};

Expand All @@ -102,31 +202,38 @@ struct enum_bitfield_t

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

template<std::same_as<enum_type_t>... Args>
// constructs will full bits enabled
explicit constexpr enum_bitfield_t(enum_bitfield_full_e) noexcept : bits_{bits_mask} {}

template<std::same_as<enumeration>... Args>
constexpr explicit enum_bitfield_t(Args &&... args) noexcept
{
set_values(std::forward<Args>(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<typename Self>
[[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<std::same_as<enum_type_t>... Args>
constexpr void set_values(enum_type_t const & arg, Args &&... args) noexcept
template<std::same_as<enumeration>... Args>
constexpr void set_values(enumeration const & arg, Args &&... args) noexcept
{
detail::bit_proxy_t{*this, arg} = true;
set_values(std::forward<Args>(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
Expand Down Expand Up @@ -175,11 +282,11 @@ struct enum_bitfield_t
}
};

template<enum_concept enum_type_t>
enum_bitfield_t(enum_type_t const & arg) -> enum_bitfield_t<enum_type_t>;
template<enum_concept enumeration>
enum_bitfield_t(enumeration const & arg) -> enum_bitfield_t<enumeration>;

template<enum_concept enum_type_t, typename... Args>
enum_bitfield_t(enum_type_t const & arg, Args &&... args) -> enum_bitfield_t<enum_type_t>;
template<enum_concept enumeration, typename... Args>
enum_bitfield_t(enumeration const & arg, Args &&... args) -> enum_bitfield_t<enumeration>;

} // namespace simple_enum::inline v0_9

Expand Down
67 changes: 67 additions & 0 deletions include/simple_enum/enum_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<enum_concept enum_type>
struct emum_index_to_enumeration_t
{
[[nodiscard]]
static_call_operator constexpr auto operator()(std::size_t index) static_call_operator_const noexcept
-> cxx23::expected<enum_type, enum_index_error>
{
using enum_meta_info = detail::enum_meta_info_t<enum_type>;
auto const requested_value{enum_meta_info::first_index() + std::underlying_type_t<enum_type>(index)};

if(requested_value <= enum_meta_info::last_index()) [[likely]]
return static_cast<enum_type>(requested_value);
else
return cxx23::unexpected{enum_index_error::out_of_range};
}
};

/// @brief enumeration value for 0 - based index
template<enum_concept enum_type>
inline constexpr emum_index_to_enumeration_t<enum_type> emum_index_to_enumeration;

/// @brief Provides compile time information of length of enumeration (including holes).
template<enum_concept enum_type>
struct enum_size_t
Expand All @@ -90,6 +112,51 @@ struct enum_size_t
template<enum_concept enum_type>
inline constexpr std::size_t enum_size_v = enum_size_t<enum_type>::value;

namespace detail
{
[[nodiscard]]
constexpr bool isdigit(char src) noexcept
{
unsigned c{static_cast<unsigned>(src)};
return c >= 48u && c <= 57u;
}
} // namespace detail

template<enum_concept enum_type>
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<enum_type>;

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<enum_concept enum_type>
inline constexpr is_valid_enumeration_index_t<enum_type> is_valid_enumeration_index_v;

struct is_valid_enumeration_value_t
{
template<enum_concept enum_type>
static_call_operator constexpr auto operator()(enum_type value) static_call_operator_const noexcept -> bool
{
using enum_meta_info = detail::enum_meta_info_t<enum_type>;
auto const requested_value{simple_enum::detail::to_underlying(value)};
if(requested_value >= enum_meta_info::first_index())
return is_valid_enumeration_index_t<enum_type>{}(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"
Loading