mirror of
https://github.com/Neargye/magic_enum.git
synced 2026-01-09 23:34:23 +00:00
Enable wchar_t as string_view value_type (#272)
This commit is contained in:
parent
fba99305d2
commit
8f6c9905fd
5 changed files with 190 additions and 22 deletions
|
|
@ -134,6 +134,23 @@ MAGIC_ENUM_USING_ALIAS_STRING
|
|||
using std::string;
|
||||
#endif
|
||||
|
||||
using char_type = string_view::value_type;
|
||||
static_assert(std::is_same_v<string_view::value_type, string::value_type>, "magic_enum::customize requires same string_view::value_type and string::value_type");
|
||||
static_assert([] {
|
||||
if constexpr (std::is_same_v<char_type, wchar_t>) {
|
||||
constexpr const char c[] = "abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
constexpr const wchar_t wc[] = L"abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
static_assert(std::size(c) == std::size(wc), "magic_enum::customize identifier characters are multichars in wchar_t.");
|
||||
|
||||
for (std::size_t i = 0; i < std::size(c); ++i) {
|
||||
if (c[i] != wc[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} (), "magic_enum::customize wchar_t is not compatible with ASCII.");
|
||||
|
||||
namespace customize {
|
||||
|
||||
// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128.
|
||||
|
|
@ -163,7 +180,7 @@ enum class customize_tag {
|
|||
class customize_t : public std::pair<detail::customize_tag, string_view> {
|
||||
public:
|
||||
constexpr customize_t(string_view srt) : std::pair<detail::customize_tag, string_view>{detail::customize_tag::custom_tag, srt} {}
|
||||
constexpr customize_t(const char* srt) : customize_t{string_view{srt}} {}
|
||||
constexpr customize_t(const char_type* srt) : customize_t{string_view{srt}} {}
|
||||
constexpr customize_t(detail::customize_tag tag) : std::pair<detail::customize_tag, string_view>{tag, string_view{}} {
|
||||
assert(tag != detail::customize_tag::custom_tag);
|
||||
}
|
||||
|
|
@ -186,8 +203,6 @@ constexpr customize_t enum_type_name() noexcept {
|
|||
return default_tag;
|
||||
}
|
||||
|
||||
static_assert(std::is_same_v<string_view::value_type, string::value_type>, "magic_enum::customize requires same string_view::value_type and string::value_type");
|
||||
|
||||
} // namespace magic_enum::customize
|
||||
|
||||
namespace detail {
|
||||
|
|
@ -244,7 +259,7 @@ class static_str {
|
|||
assert(str.size() == N);
|
||||
}
|
||||
|
||||
constexpr const char* data() const noexcept { return chars_; }
|
||||
constexpr const char_type* data() const noexcept { return chars_; }
|
||||
|
||||
constexpr std::uint16_t size() const noexcept { return N; }
|
||||
|
||||
|
|
@ -252,9 +267,12 @@ class static_str {
|
|||
|
||||
private:
|
||||
template <std::uint16_t... I>
|
||||
constexpr static_str(const char* str, std::integer_sequence<std::uint16_t, I...>) noexcept : chars_{str[I]..., '\0'} {}
|
||||
constexpr static_str(const char* str, std::integer_sequence<std::uint16_t, I...>) noexcept : chars_{static_cast<char_type>(str[I])..., '\0'} {}
|
||||
|
||||
char chars_[static_cast<std::size_t>(N) + 1];
|
||||
template <std::uint16_t... I>
|
||||
constexpr static_str(string_view str, std::integer_sequence<std::uint16_t, I...>) noexcept : chars_{str[I]..., '\0'} {}
|
||||
|
||||
char_type chars_[static_cast<std::size_t>(N) + 1];
|
||||
};
|
||||
|
||||
template <>
|
||||
|
|
@ -266,7 +284,7 @@ class static_str<0> {
|
|||
|
||||
constexpr explicit static_str(string_view) noexcept {}
|
||||
|
||||
constexpr const char* data() const noexcept { return nullptr; }
|
||||
constexpr const char_type* data() const noexcept { return nullptr; }
|
||||
|
||||
constexpr std::uint16_t size() const noexcept { return 0; }
|
||||
|
||||
|
|
@ -275,18 +293,18 @@ class static_str<0> {
|
|||
|
||||
template <typename Op = std::equal_to<>>
|
||||
class case_insensitive {
|
||||
static constexpr char to_lower(char c) noexcept {
|
||||
return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + ('a' - 'A')) : c;
|
||||
static constexpr char_type to_lower(char_type c) noexcept {
|
||||
return (c >= 'A' && c <= 'Z') ? static_cast<char_type>(c + ('a' - 'A')) : c;
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename L, typename R>
|
||||
constexpr auto operator()(L lhs,R rhs) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<L>, char> && std::is_same_v<std::decay_t<R>, char>, bool> {
|
||||
constexpr auto operator()(L lhs,R rhs) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<L>, char_type> && std::is_same_v<std::decay_t<R>, char_type>, bool> {
|
||||
return Op{}(to_lower(lhs), to_lower(rhs));
|
||||
}
|
||||
};
|
||||
|
||||
constexpr std::size_t find(string_view str, char c) noexcept {
|
||||
constexpr std::size_t find(string_view str, char_type c) noexcept {
|
||||
#if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__)
|
||||
// https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc
|
||||
// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html
|
||||
|
|
@ -317,7 +335,7 @@ constexpr bool is_default_predicate() noexcept {
|
|||
template <typename BinaryPredicate>
|
||||
constexpr bool is_nothrow_invocable() {
|
||||
return is_default_predicate<BinaryPredicate>() ||
|
||||
std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>;
|
||||
std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char_type, char_type>;
|
||||
}
|
||||
|
||||
template <typename BinaryPredicate>
|
||||
|
|
@ -785,7 +803,7 @@ struct enable_if_enum<true, R> {
|
|||
};
|
||||
|
||||
template <typename T, typename R, typename BinaryPredicate = std::equal_to<>, typename D = std::decay_t<T>>
|
||||
using enable_if_t = typename enable_if_enum<std::is_enum_v<D> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type;
|
||||
using enable_if_t = typename enable_if_enum<std::is_enum_v<D> && std::is_invocable_r_v<bool, BinaryPredicate, char_type, char_type>, R>::type;
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_enum_v<std::decay_t<T>>, int> = 0>
|
||||
using enum_concept = T;
|
||||
|
|
@ -1225,7 +1243,7 @@ template <detail::enum_subtype S, typename E>
|
|||
// Returns name from enum-flags value.
|
||||
// If enum-flags value does not have name or value out of range, returns empty string.
|
||||
template <typename E>
|
||||
[[nodiscard]] auto enum_flags_name(E value, char sep = '|') -> detail::enable_if_t<E, string> {
|
||||
[[nodiscard]] auto enum_flags_name(E value, char_type sep = '|') -> detail::enable_if_t<E, string> {
|
||||
using D = std::decay_t<E>;
|
||||
using U = underlying_type_t<D>;
|
||||
constexpr auto S = detail::enum_subtype::flags;
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ struct name_sort_impl<void, OP> {
|
|||
struct FullCmp : S {};
|
||||
|
||||
template <typename S>
|
||||
struct FullCmp<S, std::enable_if_t<!std::is_invocable_v<S, string_view, string_view> && std::is_invocable_v<S, char, char>>> {
|
||||
struct FullCmp<S, std::enable_if_t<!std::is_invocable_v<S, string_view, string_view> && std::is_invocable_v<S, char_type, char_type>>> {
|
||||
[[nodiscard]] constexpr inline bool operator()(string_view s1, string_view s2) const noexcept { return lexicographical_compare<S>(s1, s2); }
|
||||
};
|
||||
|
||||
|
|
@ -546,7 +546,7 @@ class bitset {
|
|||
}
|
||||
}
|
||||
|
||||
constexpr explicit bitset(detail::raw_access_t, string_view sv, string_view::size_type pos = 0, string_view::size_type n = string_view::npos, char zero = '0', char one = '1')
|
||||
constexpr explicit bitset(detail::raw_access_t, string_view sv, string_view::size_type pos = 0, string_view::size_type n = string_view::npos, char_type zero = '0', char_type one = '1')
|
||||
: a{{}} {
|
||||
std::size_t i{};
|
||||
for (auto c : sv.substr(pos, n)) {
|
||||
|
|
@ -562,8 +562,8 @@ class bitset {
|
|||
}
|
||||
}
|
||||
|
||||
constexpr explicit bitset(detail::raw_access_t, const char* str, std::size_t n = ~std::size_t{}, char zero = '0', char one = '1')
|
||||
: bitset(std::string_view{str, (std::min)(std::char_traits<char>::length(str), n)}, 0, n, zero, one) {}
|
||||
constexpr explicit bitset(detail::raw_access_t, const char_type* str, std::size_t n = ~std::size_t{}, char_type zero = '0', char_type one = '1')
|
||||
: bitset(std::string_view{str, (std::min)(std::char_traits<char_type>::length(str), n)}, 0, n, zero, one) {}
|
||||
|
||||
constexpr bitset(std::initializer_list<E> starters) : a{{}} {
|
||||
if constexpr (magic_enum::detail::subtype_v<E> == magic_enum::detail::enum_subtype::flags) {
|
||||
|
|
@ -591,7 +591,7 @@ class bitset {
|
|||
}
|
||||
|
||||
template <typename Cmp = std::equal_to<>>
|
||||
constexpr explicit bitset(string_view sv, Cmp&& cmp = {}, char sep = '|') {
|
||||
constexpr explicit bitset(string_view sv, Cmp&& cmp = {}, char_type sep = '|') {
|
||||
for (std::size_t to{}; (to = magic_enum::detail::find(sv, sep)) != string_view::npos; sv.remove_prefix(to + 1)) {
|
||||
if (auto v = enum_cast<E>(sv.substr(0, to), cmp)) {
|
||||
set(v);
|
||||
|
|
@ -756,7 +756,7 @@ class bitset {
|
|||
return res;
|
||||
}
|
||||
|
||||
[[nodiscard]] string to_string(char sep = '|') const {
|
||||
[[nodiscard]] string to_string(char_type sep = '|') const {
|
||||
string name;
|
||||
|
||||
for (auto& e : enum_values<E>()) {
|
||||
|
|
@ -771,7 +771,7 @@ class bitset {
|
|||
return name;
|
||||
}
|
||||
|
||||
[[nodiscard]] string to_string(detail::raw_access_t, char zero = '0', char one = '1') const {
|
||||
[[nodiscard]] string to_string(detail::raw_access_t, char_type zero = '0', char_type one = '1') const {
|
||||
string name;
|
||||
name.reserve(size());
|
||||
for (std::size_t i{}; i < size(); ++i) {
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ make_test(test.cpp test-cpp17 c++17)
|
|||
make_test(test_flags.cpp test_flags-cpp17 c++17)
|
||||
make_test(test_aliases.cpp test_aliases-cpp17 c++17)
|
||||
make_test(test_containers.cpp test_containers-cpp17 c++17)
|
||||
make_test(test_wchar_t.cpp test_wchar_t-cpp17 c++17)
|
||||
|
||||
if(MAGIC_ENUM_OPT_ENABLE_NONASCII)
|
||||
make_test(test_nonascii.cpp test_nonascii-cpp17 c++17)
|
||||
|
|
@ -48,6 +49,7 @@ if(HAS_CPP20_FLAG)
|
|||
make_test(test_flags.cpp test_flags-cpp20 c++20)
|
||||
make_test(test_aliases.cpp test_aliases-cpp20 c++20)
|
||||
make_test(test_containers.cpp test_containers-cpp20 c++20)
|
||||
make_test(test_wchar_t.cpp test_wchar_t-cpp20 c++20)
|
||||
if(MAGIC_ENUM_OPT_ENABLE_NONASCII)
|
||||
make_test(test_nonascii.cpp test_nonascii-cpp20 c++20)
|
||||
endif()
|
||||
|
|
@ -58,6 +60,7 @@ if(HAS_CPP23_FLAG)
|
|||
make_test(test_flags.cpp test_flags-cpp23 c++23)
|
||||
make_test(test_aliases.cpp test_aliases-cpp23 c++23)
|
||||
make_test(test_containers.cpp test_containers-cpp23 c++23)
|
||||
make_test(test_wchar_t.cpp test_wchar_t-cpp23 c++23)
|
||||
if(MAGIC_ENUM_OPT_ENABLE_NONASCII)
|
||||
make_test(test_nonascii.cpp test_nonascii-cpp23 c++23)
|
||||
endif()
|
||||
|
|
@ -68,6 +71,7 @@ if(HAS_CPPLATEST_FLAG)
|
|||
make_test(test_flags.cpp test_flags-cpplatest c++latest)
|
||||
make_test(test_aliases.cpp test_aliases-cpplatest c++latest)
|
||||
make_test(test_containers.cpp test_containers-cpplatest c++latest)
|
||||
make_test(test_wchar_t.cpp test_wchar_t-cpplatest c++latest)
|
||||
if(MAGIC_ENUM_OPT_ENABLE_NONASCII)
|
||||
make_test(test_nonascii.cpp test_nonascii-cpplatest c++latest)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -447,7 +447,6 @@ TEST_CASE("enum_count") {
|
|||
|
||||
constexpr auto s6 = enum_count<MaxUsedAsInvalid>();
|
||||
REQUIRE(s6 == 2);
|
||||
|
||||
}
|
||||
|
||||
enum lt1 { s1, loooooooooooooooooooong1 };
|
||||
|
|
|
|||
147
test/test_wchar_t.cpp
Normal file
147
test/test_wchar_t.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// Copyright (c) 2019 - 2023 Daniil Goncharov <neargye@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#define MAGIC_ENUM_USING_ALIAS_STRING_VIEW using string_view = std::wstring_view;
|
||||
#define MAGIC_ENUM_USING_ALIAS_STRING using string = std::wstring;
|
||||
#include <magic_enum.hpp>
|
||||
#include <magic_enum_iostream.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
#include <string_view>
|
||||
#include <sstream>
|
||||
|
||||
enum class Color { RED = -12, GREEN = 7, BLUE = 15 };
|
||||
template <>
|
||||
constexpr magic_enum::customize::customize_t magic_enum::customize::enum_name<Color>(Color value) noexcept {
|
||||
switch (value) {
|
||||
case Color::RED:
|
||||
return L"red";
|
||||
default:
|
||||
return default_tag;
|
||||
}
|
||||
}
|
||||
|
||||
using namespace magic_enum;
|
||||
|
||||
static_assert(is_magic_enum_supported, "magic_enum: Unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
|
||||
|
||||
TEST_CASE("enum_cast") {
|
||||
SECTION("string") {
|
||||
constexpr auto cr = enum_cast<Color>(L"red");
|
||||
REQUIRE(cr.value() == Color::RED);
|
||||
REQUIRE(enum_cast<Color&>(L"GREEN").value() == Color::GREEN);
|
||||
REQUIRE(enum_cast<Color>(L"blue", [](wchar_t lhs, wchar_t rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
|
||||
REQUIRE_FALSE(enum_cast<Color>(L"None").has_value());
|
||||
}
|
||||
|
||||
SECTION("integer") {
|
||||
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
|
||||
constexpr auto cr = enum_cast<Color>(-12);
|
||||
REQUIRE(cr.value() == Color::RED);
|
||||
REQUIRE(enum_cast<Color&>(7).value() == Color::GREEN);
|
||||
REQUIRE(enum_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE);
|
||||
REQUIRE_FALSE(enum_cast<Color>(0).has_value());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("enum_values") {
|
||||
REQUIRE(std::is_same_v<decltype(magic_enum::enum_values<Color>()), const std::array<Color, 3>&>);
|
||||
|
||||
constexpr auto& s1 = enum_values<Color&>();
|
||||
REQUIRE(s1 == std::array<Color, 3>{{Color::RED, Color::GREEN, Color::BLUE}});
|
||||
}
|
||||
|
||||
TEST_CASE("enum_count") {
|
||||
constexpr auto s1 = enum_count<Color&>();
|
||||
REQUIRE(s1 == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("enum_name") {
|
||||
SECTION("automatic storage") {
|
||||
constexpr Color cr = Color::RED;
|
||||
constexpr auto cr_name = enum_name(cr);
|
||||
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
|
||||
Color cb = Color::BLUE;
|
||||
REQUIRE(cr_name == L"red");
|
||||
REQUIRE(enum_name<Color&>(cb) == L"BLUE");
|
||||
REQUIRE(enum_name<as_flags<false>>(cm[1]) == L"GREEN");
|
||||
REQUIRE(enum_name<as_common<true>>(cm[1]) == L"GREEN");
|
||||
REQUIRE(enum_name<as_flags<false>>(static_cast<Color>(0)).empty());
|
||||
}
|
||||
|
||||
SECTION("static storage") {
|
||||
constexpr Color cr = Color::RED;
|
||||
constexpr auto cr_name = enum_name<cr>();
|
||||
constexpr Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
|
||||
REQUIRE(cr_name == L"red");
|
||||
REQUIRE(enum_name<Color::BLUE>() == L"BLUE");
|
||||
REQUIRE(enum_name<cm[1]>() == L"GREEN");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("enum_names") {
|
||||
REQUIRE(std::is_same_v<decltype(magic_enum::enum_names<Color>()), const std::array<std::wstring_view, 3>&>);
|
||||
|
||||
constexpr auto& s1 = enum_names<Color&>();
|
||||
REQUIRE(s1 == std::array<std::wstring_view, 3>{{L"red", L"GREEN", L"BLUE"}});
|
||||
}
|
||||
|
||||
TEST_CASE("enum_entries") {
|
||||
REQUIRE(std::is_same_v<decltype(magic_enum::enum_entries<Color>()), const std::array<std::pair<Color, std::wstring_view>, 3>&>);
|
||||
|
||||
constexpr auto& s1 = enum_entries<Color&>();
|
||||
REQUIRE(s1 == std::array<std::pair<Color, std::wstring_view>, 3>{{{Color::RED, L"red"}, {Color::GREEN, L"GREEN"}, {Color::BLUE, L"BLUE"}}});
|
||||
}
|
||||
|
||||
TEST_CASE("ostream_operators") {
|
||||
auto test_ostream = [](auto e, std::wstring name) {
|
||||
using namespace magic_enum::ostream_operators;
|
||||
std::wstringstream ss;
|
||||
ss << e;
|
||||
REQUIRE(ss);
|
||||
REQUIRE(ss.str() == name);
|
||||
};
|
||||
|
||||
test_ostream(std::make_optional(Color::RED), L"red");
|
||||
test_ostream(Color::GREEN, L"GREEN");
|
||||
test_ostream(Color::BLUE, L"BLUE");
|
||||
test_ostream(static_cast<Color>(0), L"0");
|
||||
test_ostream(std::make_optional(static_cast<Color>(0)), L"0");
|
||||
}
|
||||
|
||||
TEST_CASE("istream_operators") {
|
||||
auto test_istream = [](const auto e, std::wstring name) {
|
||||
using namespace magic_enum::istream_operators;
|
||||
std::wistringstream ss(name);
|
||||
std::decay_t<decltype(e)> v;
|
||||
ss >> v;
|
||||
REQUIRE(ss);
|
||||
REQUIRE(v == e);
|
||||
};
|
||||
|
||||
test_istream(Color::GREEN, L"GREEN");
|
||||
test_istream(Color::BLUE, L"BLUE");
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue