diff --git a/.vscode/settings.json b/.vscode/settings.json
index abcce64..d1e80c1 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,7 @@
{
- "files.associations": {
- "cmath": "cpp",
- "string_view": "cpp"
- }
-}
\ No newline at end of file
+ "files.associations": {
+ "cmath": "cpp",
+ "string_view": "cpp",
+ "sstream": "cpp"
+ }
+}
diff --git a/README.md b/README.md
index 523b10c..ac10478 100644
--- a/README.md
+++ b/README.md
@@ -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 (default range: `[0, 256)`) on each call or with your special function for types or adding specialized `enum_range` 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() noexcept` function
+* Supports bitmasked enums and auto detect them
* Supports iterate over enum (names and values) with `mgutility::enum_for_each()` class and it is compatible with standard ranges and views
## Limitations
@@ -20,16 +21,32 @@ Converting (scoped)enum values to/from string names written in C++>=11.
* Wider range can increase compile time so user responsible to adjusting for enum's range
-## Usage ([try it!](https://godbolt.org/z/YM5EvY1Y5))
+## Usage ([try it!](https://godbolt.org/z/dfeYxscvT))
```C++
#include
#include "enum_name.hpp"
-num class rgb_color { red, green, blue, unknown = -1 };
+enum class rgb_color {
+ red = 1 << 0,
+ green = 1 << 1,
+ blue = 1 << 2,
+ unknown = -1
+};
+
+auto operator|(rgb_color lhs, rgb_color rhs) -> rgb_color {
+ return static_cast(mgutility::enum_to_underlying(lhs) |
+ mgutility::enum_to_underlying(rhs));
+}
+
+auto operator&(rgb_color lhs, rgb_color rhs) -> rgb_color {
+ return static_cast(mgutility::enum_to_underlying(lhs) &
+ mgutility::enum_to_underlying(rhs));
+}
// specialize rgb_color::unknown to make output "UNKNOWN" instead of "unknown"
template <>
-constexpr auto mgutility::detail::enum_type::name() noexcept
+constexpr inline auto
+mgutility::detail::enum_type::name() noexcept
-> string_view {
return "UNKNOWN";
}
@@ -38,38 +55,41 @@ constexpr auto mgutility::detail::enum_type::name
template <>
struct mgutility::enum_range {
static constexpr auto min = -1;
- static constexpr auto max = 3;
+ static constexpr auto max = 5;
};
// you can specialize enum ranges with overload per enum types (option 2)
-auto enum_name = [](rgb_color c) { return mgutility::enum_name<-1, 3>(c); };
+auto enum_name = [](rgb_color c) { return mgutility::enum_name<-1, 5>(c); };
#if defined(__cpp_lib_print)
#include
#include
#endif
-
int main() {
auto x = rgb_color::blue;
- auto y = mgutility::to_enum("greenn");
+ auto y = mgutility::to_enum("green|red");
#if defined(__cpp_lib_print)
-// enum_for_each can usable with views and range algorithms
-auto colors = mgutility::enum_for_each() |
- std::ranges::views::filter(
- [](auto &&pair) { return pair.second != "UNKNOWN"; });
+ // enum_for_each can usable with views and range algorithms
+ auto colors = mgutility::enum_for_each() |
+ std::ranges::views::filter([](auto &&pair) {
+ return !pair.second.empty() && pair.second != "UNKNOWN";
+ });
std::ranges::for_each(colors, [](auto &&color) {
- std::println("{} \t: {}", color.second, mgutility::enum_to_underlying(color.first));
+ std::println("{} \t: {}", color.second,
+ mgutility::enum_to_underlying(color.first));
});
#else
for (auto&& e : mgutility::enum_for_each()) {
- if(e.second != "UNKNOWN"){
- std::cout << e.second << " \t: " << mgutility::enum_to_underlying(e.first) << '\n';
+ if (!e.second.empty() && e.second != "UNKNOWN") {
+ std::cout << e.second
+ << " \t: " << mgutility::enum_to_underlying(e.first)
+ << '\n';
}
// std::pair {enum_type, name_of_enum}
}
@@ -78,8 +98,6 @@ auto colors = mgutility::enum_for_each() |
// default signature: enum_name(Enum&&) Changing max_value to not too much greater than enum's
// max value, it will compiles faster
- std::cout << mgutility::enum_name(x) << '\n'; // will print "blue" to output
- // or
std::cout << x << '\n'; // will print "blue" to output
// calling specialized enum ranges function for rgb_color type
diff --git a/include/enum_name.hpp b/include/enum_name.hpp
index c7ed6ab..6258929 100644
--- a/include/enum_name.hpp
+++ b/include/enum_name.hpp
@@ -33,7 +33,6 @@
#include
#include
#include
-#include
#if defined(_MSC_VER) && _MSC_VER < 1910
#error "Requires MSVC 2017 or newer!"
@@ -59,6 +58,18 @@
#error "Standards older than C++11 is not supported!"
#endif
+#if MG_ENUM_NAME_CPLUSPLUS > 201703L
+#define MG_ENUM_NAME_CNSTXPR_FUNC constexpr
+#else
+#define MG_ENUM_NAME_CNSTXPR_FUNC
+#endif
+
+#if MG_ENUM_NAME_CPLUSPLUS > 201703L
+#define MG_ENUM_NAME_CNSTEVL consteval
+#else
+#define MG_ENUM_NAME_CNSTEVL
+#endif
+
#if MG_ENUM_NAME_CPLUSPLUS > 201702L
#include
#endif
@@ -72,6 +83,13 @@ struct is_scoped_enum {
std::is_enum::value &&
!std::is_convertible::type>::value;
};
+
+template
+struct has_bit_or : std::false_type {};
+
+template
+struct has_bit_or : std::true_type {};
+
#if MG_ENUM_NAME_CPLUSPLUS > 201103L
template
static constexpr bool is_scoped_enum_v = is_scoped_enum::value;
@@ -131,8 +149,14 @@ class basic_string_view {
size_t len) const noexcept {
return basic_string_view(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==(
@@ -168,6 +192,8 @@ class basic_string_view {
return os;
}
+ static constexpr auto npos = -1;
+
private:
size_t size_;
const Char* data_;
@@ -297,6 +323,11 @@ using enable_if_t = std::enable_if_t;
#endif
+template
+using string_or_view_t =
+ typename std::conditional::value, std::string,
+ detail::string_view>::type;
+
template
struct enum_sequence {};
@@ -347,7 +378,7 @@ struct enum_type {
private:
static constexpr int lastidxenumname[] =
#if defined(_MSC_VER)
- {22, 0, ',', ':', '<'};
+ {21, 0, ',', ':', '<'};
#elif defined(__clang__)
{1, 1, ' ', ':', '('};
#elif defined(__GNUC__)
@@ -362,20 +393,21 @@ struct enum_type {
};
template
-using enum_pair = std::pair;
+using enum_pair = std::pair>;
template
-inline auto get_enum_array(detail::enum_sequence) noexcept
+MG_ENUM_NAME_CNSTXPR inline auto get_enum_array(
+ detail::enum_sequence) noexcept
-> std::array {
- static std::array
- arr{"", enum_type::template name()...};
- return arr;
+ return std::array{
+ "", enum_type::template name()...};
}
template
-inline auto to_enum_impl(detail::string_view str) noexcept
+MG_ENUM_NAME_CNSTXPR inline auto to_enum_impl(detail::string_view str) noexcept
-> detail::optional {
- auto arr = get_enum_array(detail::make_enum_sequence());
+ MG_ENUM_NAME_CNSTXPR auto arr =
+ get_enum_array(detail::make_enum_sequence());
const auto index{std::find(arr.begin() + 1, arr.end(), str)};
return index == arr.end()
? detail::nullopt
@@ -384,11 +416,85 @@ inline auto to_enum_impl(detail::string_view str) noexcept
}
template
-inline auto enum_name_impl(Enum e) noexcept -> detail::string_view {
- auto arr = get_enum_array(detail::make_enum_sequence());
- const auto index{std::abs(Min) + static_cast(e) + (Min < 0 ? 1 : 1)};
+MG_ENUM_NAME_CNSTXPR inline auto to_enum_bitmask_impl(
+ detail::string_view str) noexcept -> detail::optional {
+ if (str.find('|') == detail::string_view::npos) {
+ return to_enum_impl(str);
+ } else {
+ auto index = std::size_t{};
+ auto result = detail::optional{detail::nullopt};
+ for (std::size_t i{}; i < str.size(); ++i) {
+ if (str[i] == '|') {
+ const auto name = str.substr(index, i - index);
+ auto maybe_enum =
+ to_enum_impl(str.substr(index, i - index));
+ if (!name.empty() && maybe_enum) {
+ result.emplace(
+ result ? static_cast(*result | *maybe_enum)
+ : *maybe_enum);
+ }
+
+ index = i + 1U;
+ }
+ }
+ if (result) {
+ auto maybe_enum = to_enum_impl(
+ str.substr(index, str.size() - index));
+ if (maybe_enum) {
+ result.emplace(static_cast(*result | *maybe_enum));
+ } else {
+ result.reset();
+ }
+ }
+
+ return result;
+ }
+}
+
+template ::value, bool> = true>
+MG_ENUM_NAME_CNSTXPR inline auto enum_name_impl(Enum e) noexcept
+ -> detail::string_view {
+ MG_ENUM_NAME_CNSTXPR auto arr =
+ get_enum_array(detail::make_enum_sequence());
+ const auto index{(Min < 0 ? Min * -1 : Min) + static_cast(e) +
+ (Min < 0 ? 1 : 1)};
return arr[(index < Min || index > arr.size() - 1) ? 0 : index];
}
+
+template ::value, bool> = true>
+MG_ENUM_NAME_CNSTXPR_FUNC inline auto enum_name_impl(Enum e) noexcept
+ -> detail::string_or_view_t {
+ MG_ENUM_NAME_CNSTXPR auto arr =
+ get_enum_array(detail::make_enum_sequence());
+ const auto index{(Min < 0 ? Min * -1 : Min) + static_cast(e) +
+ (Min < 0 ? 1 : 1)};
+ const auto name = arr[(index < Min || index > arr.size() - 1) ? 0 : index];
+
+ const auto isdigit = [](char c) {
+ return static_cast(c) < 58 && static_cast(c) > 47;
+ };
+
+ if (!name.empty() && !isdigit(name[0])) {
+ return std::string{name};
+ } else {
+ auto bitmasked_name = std::string{};
+
+ for (auto i{Min}; i < Max; ++i) {
+ const auto idx = (Min < 0 ? Min * -1 : Min) + i + (Min < 0 ? 1 : 1);
+ if (!(idx < Min || idx > arr.size() - 1) && !arr[idx].empty() &&
+ !isdigit(arr[idx][0]) &&
+ (e & static_cast(i)) == static_cast(i)) {
+ bitmasked_name.append(arr[idx]).append("|");
+ }
+ }
+
+ const auto result =
+ bitmasked_name.substr(0U, bitmasked_name.size() - 1U);
+ return result.find('|') != std::string::npos ? result : std::string{""};
+ }
+}
} // namespace detail
} // namespace mgutility
@@ -462,7 +568,7 @@ constexpr inline auto enum_to_underlying(Enum e) noexcept
template
MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept
- -> detail::string_view {
+ -> detail::string_or_view_t {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum::value, "Value is not an Enum type!");
return detail::enum_name_impl(e);
@@ -471,7 +577,7 @@ MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept
template ::min,
int Max = enum_range::max>
MG_ENUM_NAME_CNSTXPR inline auto enum_name(Enum e) noexcept
- -> detail::string_view {
+ -> detail::string_or_view_t {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum::value, "Value is not an Enum type!");
return detail::enum_name_impl(e);
@@ -484,13 +590,24 @@ auto enum_for_each::enum_iter::operator*() const -> value_type {
}
template ::min,
- int Max = enum_range::max>
+ int Max = enum_range::max,
+ detail::enable_if_t::value, bool> = true>
MG_ENUM_NAME_CNSTXPR inline auto to_enum(detail::string_view str) noexcept
-> detail::optional {
static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
static_assert(std::is_enum::value, "Type is not an Enum type!");
return detail::to_enum_impl(str);
}
+
+template ::min,
+ int Max = enum_range::max,
+ detail::enable_if_t::value, bool> = true>
+MG_ENUM_NAME_CNSTXPR inline auto to_enum(detail::string_view str) noexcept
+ -> detail::optional {
+ static_assert(Min < Max - 1, "Max must be greater than (Min + 1)!");
+ static_assert(std::is_enum::value, "Type is not an Enum type!");
+ return detail::to_enum_bitmask_impl(str);
+}
} // namespace mgutility
template std::ostream& {
return os;
}
-#endif // MGUTILITY_ENUM_NAME_HPP
+#if defined(__cpp_lib_format)
+
+#include
+
+template
+ requires std::is_enum_v
+struct std::formatter : formatter {
+ auto constexpr format(Enum e, format_context& ctx) const {
+ return formatter::format(mgutility::enum_name(e),
+ ctx);
+ }
+};
+
+#endif
+
+#endif // MGUTILITY_ENUM_NAME_HPP
\ No newline at end of file