1
0
Fork 0
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:
Daniil Goncharov 2023-05-24 19:05:20 +04:00 committed by GitHub
parent fba99305d2
commit 8f6c9905fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 190 additions and 22 deletions

View file

@ -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;

View file

@ -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) {

View file

@ -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()

View file

@ -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
View 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");
}