Skip to content

Commit

Permalink
introduce bitmask support
Browse files Browse the repository at this point in the history
  • Loading branch information
mguludag committed Mar 31, 2024
1 parent 0e5b19f commit c72800f
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 12 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ Converting (scoped)enum values to/from string names written in C++>=11.
## Features
* Supports `enum` and `enum class`
* Supports enums in namespaces, classes or structs even templated or not
* Supports compile-time as much as possible using with C++14 and later
* Supports compile-time as much as possible using with C++17 and later
* Changing enum range with template parameter <sub>(default range: `[0, 256)`)</sub> on each call or with your special function for types or adding specialized `enum_range<Enum>` struct
* Supports and automatically overloaded `operator<<` for Enum types to direct using with ostream objects
* Supports custom enum name output by explicit specialization of `constexpr inline auto mgutility::detail::enum_type::name<Enum, EnumValue>() noexcept` function
* Supports bitmasked enums and auto detect them
* Supports iterate over enum (names and values) with `mgutility::enum_for_each<T>()` class and it is compatible with standard ranges and views

## Limitations
Expand Down
110 changes: 99 additions & 11 deletions include/enum_name.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#if defined(_MSC_VER) && _MSC_VER < 1910
#error "Requires MSVC 2017 or newer!"
Expand Down Expand Up @@ -72,6 +71,13 @@ struct is_scoped_enum {
std::is_enum<E>::value &&
!std::is_convertible<E, typename std::underlying_type<E>::type>::value;
};

template <typename T, typename = void>
struct has_bit_or : std::false_type {};

template <typename T>
struct has_bit_or<T, decltype((T{} | T{}), void())> : std::true_type {};

#if MG_ENUM_NAME_CPLUSPLUS > 201103L
template <typename E>
static constexpr bool is_scoped_enum_v = is_scoped_enum<E>::value;
Expand Down Expand Up @@ -131,8 +137,14 @@ class basic_string_view {
size_t len) const noexcept {
return basic_string_view<Char>(data_ + begin, len);
}
constexpr inline size_t rfind(Char c, size_t pos = 0) const noexcept {
return c == data_[pos] ? pos : rfind(c, --pos);
constexpr inline size_t rfind(Char c, size_t pos = npos) const noexcept {
return (pos == npos ? pos = size_ : pos = pos), c == data_[pos] ? pos
: pos == 0U
? npos
: rfind(c, --pos);
}
constexpr inline size_t find(Char c, size_t pos = 0) const noexcept {
return c == data_[pos] ? pos : pos < size_ ? find(c, ++pos) : npos;
}

constexpr friend inline bool operator==(
Expand Down Expand Up @@ -168,6 +180,8 @@ class basic_string_view {
return os;
}

static constexpr auto npos = -1;

private:
size_t size_;
const Char* data_;
Expand Down Expand Up @@ -297,6 +311,11 @@ using enable_if_t = std::enable_if_t<B, T>;

#endif

template <typename T>
using string_or_view_t =
typename std::conditional<has_bit_or<T>::value, std::string,
detail::string_view>::type;

template <typename Enum, Enum...>
struct enum_sequence {};

Expand Down Expand Up @@ -362,20 +381,21 @@ struct enum_type {
};

template <typename Enum>
using enum_pair = std::pair<Enum, detail::string_view>;
using enum_pair = std::pair<Enum, detail::string_or_view_t<Enum>>;

template <typename Enum, Enum... Is>
inline auto get_enum_array(detail::enum_sequence<Enum, Is...>) noexcept
-> std::array<detail::string_view, sizeof...(Is) + 1> {
static std::array<detail::string_view, sizeof...(Is) + 1>
arr{"", enum_type::template name<Enum, Is>()...};
static std::array<detail::string_view, sizeof...(Is) + 1> arr{
"", enum_type::template name<Enum, Is>()...};
return arr;
}

template <typename Enum, int Min, int Max>
inline auto to_enum_impl(detail::string_view str) noexcept
-> detail::optional<Enum> {
auto arr = get_enum_array<Enum>(detail::make_enum_sequence<Enum, Min, Max>());
auto arr =
get_enum_array<Enum>(detail::make_enum_sequence<Enum, Min, Max>());
const auto index{std::find(arr.begin() + 1, arr.end(), str)};
return index == arr.end()
? detail::nullopt
Expand All @@ -384,11 +404,68 @@ inline auto to_enum_impl(detail::string_view str) noexcept
}

template <typename Enum, int Min, int Max>
inline auto to_enum_bitmask_impl(detail::string_view str) noexcept
-> detail::optional<Enum> {
if (str.find('|') == detail::string_view::npos) {
return to_enum_impl<Enum, Min, Max>(str);
} else {
auto index = std::size_t{};
auto result = detail::optional<Enum>{};
for (std::size_t i{}; i < str.size(); ++i) {
auto maybe_enum =
to_enum_impl<Enum, Min, Max>(str.substr(index, i - index));
if (maybe_enum.has_value()) {
result.emplace(result ? static_cast<Enum>(*result | *maybe_enum)
: *maybe_enum);
}

if (str[i] == '|') {
index = i + 1U;
}
}

auto maybe_enum =
to_enum_impl<Enum, Min, Max>(str.substr(index, str.size() - index));
if (maybe_enum.has_value()) {
result.emplace(result ? static_cast<Enum>(*result | *maybe_enum)
: *maybe_enum);
}
return result;
}
}

template <typename Enum, int Min, int Max,
detail::enable_if_t<!detail::has_bit_or<Enum>::value, bool> = true>
inline auto enum_name_impl(Enum e) noexcept -> detail::string_view {
auto arr = get_enum_array<Enum>(detail::make_enum_sequence<Enum, Min, Max>());
auto arr =
get_enum_array<Enum>(detail::make_enum_sequence<Enum, Min, Max>());
const auto index{std::abs(Min) + static_cast<int>(e) + (Min < 0 ? 1 : 1)};
return arr[(index < Min || index > arr.size() - 1) ? 0 : index];
}

template <typename Enum, int Min, int Max,
detail::enable_if_t<detail::has_bit_or<Enum>::value, bool> = true>
inline auto enum_name_impl(Enum e) noexcept -> detail::string_or_view_t<Enum> {
auto arr =
get_enum_array<Enum>(detail::make_enum_sequence<Enum, Min, Max>());
const auto index{std::abs(Min) + static_cast<int>(e) + (Min < 0 ? 1 : 1)};
const auto name = arr[(index < Min || index > arr.size() - 1) ? 0 : index];

if (!name.empty() && !std::isdigit(name[0])) {
return std::string{name};
} else {
auto bitmasked_name = std::string{};

for (auto i{Min}; i < Max; ++i) {
const auto idx = std::abs(Min) + i + (Min < 0 ? 1 : 1);
if (!(idx < Min || idx > arr.size() - 1) && !arr[idx].empty() &&
!std::isdigit(arr[idx][0]) && (e & static_cast<Enum>(i)) == static_cast<Enum>(i)) {
bitmasked_name.append(arr[idx]).append("|");
}
}
return bitmasked_name.substr(0U, bitmasked_name.size() - 1);
}
}
} // namespace detail
} // namespace mgutility

Expand Down Expand Up @@ -462,7 +539,7 @@ constexpr inline auto enum_to_underlying(Enum e) noexcept

template <int Min, int Max, typename Enum>
MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept
-> detail::string_view {
-> detail::string_or_view_t<Enum> {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum<Enum>::value, "Value is not an Enum type!");
return detail::enum_name_impl<Enum, Min, Max>(e);
Expand All @@ -471,7 +548,7 @@ MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept
template <typename Enum, int Min = enum_range<Enum>::min,
int Max = enum_range<Enum>::max>
MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept
-> detail::string_view {
-> detail::string_or_view_t<Enum> {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum<Enum>::value, "Value is not an Enum type!");
return detail::enum_name_impl<Enum, Min, Max>(e);
Expand All @@ -484,13 +561,24 @@ auto enum_for_each<Enum>::enum_iter::operator*() const -> value_type {
}

template <typename Enum, int Min = enum_range<Enum>::min,
int Max = enum_range<Enum>::max>
int Max = enum_range<Enum>::max,
detail::enable_if_t<!detail::has_bit_or<Enum>::value, bool> = true>
MG_ENUM_NAME_CNSTXPR inline auto to_enum(detail::string_view str) noexcept
-> detail::optional<Enum> {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum<Enum>::value, "Type is not an Enum type!");
return detail::to_enum_impl<Enum, Min, Max>(str);
}

template <typename Enum, int Min = enum_range<Enum>::min,
int Max = enum_range<Enum>::max,
detail::enable_if_t<detail::has_bit_or<Enum>::value, bool> = true>
MG_ENUM_NAME_CNSTXPR inline auto to_enum(detail::string_view str) noexcept
-> detail::optional<Enum> {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum<Enum>::value, "Type is not an Enum type!");
return detail::to_enum_bitmask_impl<Enum, Min, Max>(str);
}
} // namespace mgutility

template <typename Enum,
Expand Down

0 comments on commit c72800f

Please sign in to comment.