From 38f86e4d093cfc9034a140d37de2168e3951bef3 Mon Sep 17 00:00:00 2001 From: Komachin <81040416+uruha-komachin@users.noreply.github.com> Date: Mon, 9 Aug 2021 15:44:30 +0100 Subject: [PATCH] Added support for non-ASCII characters (UNIX/Linux) (#95) --- CMakeLists.txt | 3 +- doc/reference.md | 6 ++ example/CMakeLists.txt | 10 +- example/example_nonascii_name.cpp | 44 ++++++++ include/magic_enum.hpp | 6 ++ test/CMakeLists.txt | 10 +- test/test.cpp | 174 ++++++++++++++++++++++++++++++ test/test_flags.cpp | 143 ++++++++++++++++++++++++ 8 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 example/example_nonascii_name.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 859d020..381d393 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 3.14) project(magic_enum VERSION "0.7.3" LANGUAGES CXX) @@ -8,6 +8,7 @@ else() set(IS_TOPLEVEL_PROJECT FALSE) endif() +option(MAGIC_ENUM_OPT_ENABLE_NONASCII "Enable support for non-ASCII enumeration identifier" ${IS_TOPLEVEL_PROJECT}) option(MAGIC_ENUM_OPT_BUILD_EXAMPLES "Build magic_enum examples" ${IS_TOPLEVEL_PROJECT}) option(MAGIC_ENUM_OPT_BUILD_TESTS "Build and perform magic_enum tests" ${IS_TOPLEVEL_PROJECT}) option(MAGIC_ENUM_OPT_INSTALL "Generate and install magic_enum target" ${IS_TOPLEVEL_PROJECT}) diff --git a/doc/reference.md b/doc/reference.md index 94eca62..b84ce8f 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -56,6 +56,12 @@ #define MAGIC_ENUM_RANGE_MAX 255 ``` +* To add support for non-ASCII enumeration identifier, use special macros: + ```cpp + #define MAGIC_ENUM_ENABLE_NONASCII + #include + ``` + ## `enum_cast` ```cpp diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 53996dd..b6e47a7 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,4 +1,4 @@ -include(CheckCXXCompilerFlag) +include(CheckCXXCompilerFlag) if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) set(OPTIONS -Wall -Wextra -pedantic-errors -Werror) @@ -20,3 +20,11 @@ endfunction() make_example(example) make_example(enum_flag_example) make_example(example_custom_name) +if(MAGIC_ENUM_OPT_ENABLE_NONASCII) + if(((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + set(OPTIONS ${OPTIONS} -DMAGIC_ENUM_ENABLE_NONASCII) + make_example(example_nonascii_name) + elseif((CMAKE_CXX_COMPILER_ID MATCHES "MSVC")) + message(WARNING "Non-ASCII feature on Windows is not supported yet") + endif() +endif() diff --git a/example/example_nonascii_name.cpp b/example/example_nonascii_name.cpp new file mode 100644 index 0000000..d7cf2de --- /dev/null +++ b/example/example_nonascii_name.cpp @@ -0,0 +1,44 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2020 - 2021 Uruha Komachin . +// +// 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. + +#include + +#include + +enum class Language : int { + 日本語 = 10, + 한국어 = 20, + English = 30, + 😃 = 40, +}; + +int main() { + std::cout << magic_enum::enum_name(Language::日本語) << std::endl; // Japanese + std::cout << magic_enum::enum_name(Language::한국어) << std::endl; // Korean + std::cout << magic_enum::enum_name(Language::English) << std::endl; // English + std::cout << magic_enum::enum_name(Language::😃) << std::endl; // Emoji + + std::cout << std::boolalpha; + std::cout << (magic_enum::enum_cast("日本語").value() == Language::日本語) << std::endl; // true + + return 0; +} diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index 8ed6b6d..85aae45 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -202,6 +202,9 @@ constexpr string_view pretty_name(string_view name) noexcept { if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || (name[i - 1] >= 'a' && name[i - 1] <= 'z') || (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + (name[i - 1] & 0x80) || +#endif (name[i - 1] == '_'))) { name.remove_prefix(i); break; @@ -210,6 +213,9 @@ constexpr string_view pretty_name(string_view name) noexcept { if (name.size() > 0 && ((name.front() >= 'a' && name.front() <= 'z') || (name.front() >= 'A' && name.front() <= 'Z') || +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + (name.front() & 0x80) || +#endif (name.front() == '_'))) { return name; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9cf50bc..208d266 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,4 @@ -include(CheckCXXCompilerFlag) +include(CheckCXXCompilerFlag) set(SOURCES test.cpp) @@ -18,6 +18,14 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") check_cxx_compiler_flag(-std=c++20 HAS_CPP20_FLAG) endif() +if(MAGIC_ENUM_OPT_ENABLE_NONASCII) + if(((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) + set(OPTIONS ${OPTIONS} -DMAGIC_ENUM_ENABLE_NONASCII) + elseif((CMAKE_CXX_COMPILER_ID MATCHES "MSVC")) + message(WARNING "Non-ASCII feature on Windows is not supported yet") + endif() +endif() + function(make_test src target std) add_executable(${target} ${src}) target_compile_options(${target} PRIVATE ${OPTIONS}) diff --git a/test/test.cpp b/test/test.cpp index 5a864c4..a464f05 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -40,6 +40,10 @@ enum class Numbers : int { one = 1, two, three, many = 127 }; enum Directions { Up = 85, Down = -42, Right = 120, Left = -120 }; +#if defined(MAGIC_ENUM_ENABLE_NONASCII) +enum class Language : int { 日本語 = 10, 한국어 = 20, English = 30, 😃 = 40 }; +#endif + enum number : unsigned long { one = 100, two = 200, @@ -121,6 +125,15 @@ TEST_CASE("enum_cast") { REQUIRE(enum_cast("Left").value() == Directions::Left); REQUIRE_FALSE(enum_cast("None").has_value()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_cast("日本語"); + REQUIRE(enum_cast("한국어").value() == Language::한국어); + REQUIRE(enum_cast("English").value() == Language::English); + REQUIRE(lang.value() == Language::日本語); + REQUIRE(enum_cast("😃").value() == Language::😃); + REQUIRE_FALSE(enum_cast("Französisch").has_value()); +#endif + constexpr auto nt = enum_cast("three"); REQUIRE(enum_cast("one").value() == number::one); REQUIRE(enum_cast("two").value() == number::two); @@ -151,6 +164,15 @@ TEST_CASE("enum_cast") { REQUIRE(enum_cast(-120).value() == Directions::Left); REQUIRE_FALSE(enum_cast(0).has_value()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_cast(10); + REQUIRE(enum_cast(20).value() == Language::한국어); + REQUIRE(enum_cast(30).value() == Language::English); + REQUIRE(lang.value() == Language::日本語); + REQUIRE(enum_cast(40).value() == Language::😃); + REQUIRE_FALSE(enum_cast(0).has_value()); +#endif + constexpr auto nt = enum_cast(300); REQUIRE(enum_cast(100).value() == number::one); REQUIRE(enum_cast(200).value() == number::two); @@ -184,6 +206,16 @@ TEST_CASE("enum_integer") { REQUIRE(dr == 120); REQUIRE(enum_integer(static_cast(0)) == 0); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_integer(Language::日本語); + Language korean = Language::한국어; + REQUIRE(enum_integer(korean) == 20); + REQUIRE(enum_integer(Language::English) == 30); + REQUIRE(enum_integer(Language::😃) == 40); + REQUIRE(lang == 10); + REQUIRE(enum_integer(static_cast(0)) == 0); +#endif + constexpr auto nt = enum_integer(number::three); REQUIRE(enum_integer(number::one) == 100); REQUIRE(enum_integer(number::two) == 200); @@ -216,6 +248,16 @@ TEST_CASE("enum_index") { REQUIRE(dr.value() == 3); REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_index(Language::日本語); + Language korean = Language::한국어; + REQUIRE(enum_index(korean) == 1); + REQUIRE(enum_index(Language::English).value() == 2); + REQUIRE(enum_index(Language::😃) == 3); + REQUIRE(lang.value() == 0); + REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); +#endif + constexpr auto nt = enum_index(number::three); REQUIRE(enum_index(number::one).value() == 0); REQUIRE(enum_index(number::two).value() == 1); @@ -249,6 +291,16 @@ TEST_CASE("enum_contains") { REQUIRE(dr); REQUIRE_FALSE(enum_contains(static_cast(0))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_contains(Language::日本語); + Language korean = Language::한국어; + REQUIRE(enum_contains(korean)); + REQUIRE(enum_contains(Language::English)); + REQUIRE(enum_contains(Language::😃)); + REQUIRE(lang); + REQUIRE_FALSE(enum_contains(static_cast(0))); +#endif + constexpr auto nt = enum_contains(number::three); REQUIRE(enum_contains(number::one)); REQUIRE(enum_contains(number::two)); @@ -277,6 +329,14 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains(Directions::Up)); REQUIRE_FALSE(enum_contains(static_cast(0))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_integer(Language::日本語); + REQUIRE(enum_contains(lang)); + REQUIRE(enum_contains(Language::한국어)); + REQUIRE(enum_contains(Language::😃)); + REQUIRE_FALSE(enum_contains(static_cast(0))); +#endif + constexpr auto nt = enum_contains(number::three); REQUIRE(enum_contains(number::one)); REQUIRE(enum_contains(100)); @@ -310,6 +370,15 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains("Left")); REQUIRE_FALSE(enum_contains("None")); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + auto lang = std::string{"日本語"}; + REQUIRE(enum_contains("한국어")); + REQUIRE(enum_contains("English")); + REQUIRE(enum_contains(lang)); + REQUIRE(enum_contains("😃")); + REQUIRE_FALSE(enum_contains("None")); +#endif + constexpr auto nt = enum_contains("three"); REQUIRE(enum_contains("one")); REQUIRE(enum_contains("two")); @@ -336,6 +405,14 @@ TEST_CASE("enum_value") { REQUIRE(enum_value(2) == Directions::Up); REQUIRE(dr == Directions::Right); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_value(3); + REQUIRE(enum_value(0) == Language::日本語); + REQUIRE(enum_value(1) == Language::한국어); + REQUIRE(enum_value(2) == Language::English); + REQUIRE(lang == Language::😃); +#endif + constexpr auto nt = enum_value(2); REQUIRE(enum_value(0) == number::one); REQUIRE(enum_value(1) == number::two); @@ -362,6 +439,11 @@ TEST_CASE("enum_values") { constexpr auto& s6 = enum_values(); REQUIRE(s6 == std::array{{MaxUsedAsInvalid::ONE, MaxUsedAsInvalid::TWO}}); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto& s7 = enum_values(); + REQUIRE(s7 == std::array{{Language::日本語, Language::한국어, Language::English, Language::😃}}); +#endif } TEST_CASE("enum_count") { @@ -382,6 +464,11 @@ TEST_CASE("enum_count") { constexpr auto s6 = enum_count(); REQUIRE(s6 == 2); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto s7 = enum_count(); + REQUIRE(s7 == 4); +#endif } TEST_CASE("enum_name") { @@ -412,6 +499,17 @@ TEST_CASE("enum_name") { REQUIRE(enum_name(Directions::Left) == "Left"); REQUIRE(enum_name(static_cast(0)).empty()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr Language lang = Language::日本語; + constexpr auto lang_name = enum_name(lang); + Language lk = Language::한국어; + REQUIRE(enum_name(lk) == "한국어"); + REQUIRE(enum_name(Language::English) == "English"); + REQUIRE(lang_name == "日本語"); + REQUIRE(enum_name(Language::😃) == "😃"); + REQUIRE(enum_name(static_cast(0)).empty()); +#endif + constexpr number nt = number::three; constexpr auto nt_name = enum_name(nt); REQUIRE(enum_name(number::one) == "one"); @@ -443,6 +541,15 @@ TEST_CASE("enum_name") { REQUIRE(dr_name == "Right"); REQUIRE(enum_name() == "Left"); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr Language lang = Language::日本語; + constexpr auto lang_name = enum_name(); + REQUIRE(enum_name() == "한국어"); + REQUIRE(enum_name() == "English"); + REQUIRE(lang_name == "日本語"); + REQUIRE(enum_name() == "😃"); +#endif + constexpr number nt = number::three; constexpr auto nt_name = enum_name(); REQUIRE(enum_name() == "one"); @@ -469,6 +576,11 @@ TEST_CASE("enum_names") { constexpr auto& s4 = enum_names(); REQUIRE(s4 == std::array{{"one", "two", "three"}}); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto& s5 = enum_names(); + REQUIRE(s5 == std::array{{"日本語", "한국어", "English", "😃"}}); +#endif } TEST_CASE("enum_entries") { @@ -485,6 +597,11 @@ TEST_CASE("enum_entries") { constexpr auto& s4 = enum_entries(); REQUIRE(s4 == std::array, 3>{{{number::one, "one"}, {number::two, "two"}, {number::three, "three"}}}); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto& s5 = enum_entries(); + REQUIRE(s5 == std::array, 4>{{{Language::日本語, "日本語"}, {Language::한국어, "한국어"}, {Language::English, "English"}, {Language::😃, "😃"}}}); +#endif } TEST_CASE("ostream_operators") { @@ -515,6 +632,15 @@ TEST_CASE("ostream_operators") { test_ostream(static_cast(0), "0"); test_ostream(std::make_optional(static_cast(0)), "0"); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + test_ostream(std::make_optional(Language::日本語), "日本語"); + test_ostream(Language::한국어, "한국어"); + test_ostream(Language::English, "English"); + test_ostream(Language::😃, "😃"); + test_ostream(static_cast(0), "0"); + test_ostream(std::make_optional(static_cast(0)), "0"); +#endif + test_ostream(std::make_optional(number::one), "one"); test_ostream(number::two, "two"); test_ostream(number::three, "three"); @@ -530,6 +656,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(~Color::RED) == ~enum_integer(Color::RED)); REQUIRE(enum_integer(~Numbers::one) == ~enum_integer(Numbers::one)); REQUIRE(enum_integer(~Directions::Up) == ~enum_integer(Directions::Up)); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(~Language::日本語) == ~enum_integer(Language::日本語)); +#endif REQUIRE(enum_integer(~number::one) == ~enum_integer(number::one)); } @@ -537,6 +666,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(Color::RED | Color::BLUE) == (enum_integer(Color::RED) | enum_integer(Color::BLUE))); REQUIRE(enum_integer(Numbers::one | Numbers::two) == (enum_integer(Numbers::one) | enum_integer(Numbers::two))); REQUIRE(enum_integer(Directions::Up | Directions::Down) == (enum_integer(Directions::Up) | enum_integer(Directions::Down))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(Language::日本語 | Language::한국어) == (enum_integer(Language::日本語) | enum_integer(Language::한국어))); +#endif REQUIRE(enum_integer(number::one | number::two) == (enum_integer(number::one) | enum_integer(number::two))); } @@ -544,6 +676,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(Color::RED & Color::BLUE) == (enum_integer(Color::RED) & enum_integer(Color::BLUE))); REQUIRE(enum_integer(Numbers::one & Numbers::two) == (enum_integer(Numbers::one) & enum_integer(Numbers::two))); REQUIRE(enum_integer(Directions::Up & Directions::Down) == (enum_integer(Directions::Up) & enum_integer(Directions::Down))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(Language::日本語 & Language::한국어) == (enum_integer(Language::日本語) & enum_integer(Language::한국어))); +#endif REQUIRE(enum_integer(number::one & number::two) == (enum_integer(number::one) & enum_integer(number::two))); } @@ -551,6 +686,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(Color::RED ^ Color::BLUE) == (enum_integer(Color::RED) ^ enum_integer(Color::BLUE))); REQUIRE(enum_integer(Numbers::one ^ Numbers::two) == (enum_integer(Numbers::one) ^ enum_integer(Numbers::two))); REQUIRE(enum_integer(Directions::Up ^ Directions::Down) == (enum_integer(Directions::Up) ^ enum_integer(Directions::Down))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(Language::日本語 ^ Language::한국어) == (enum_integer(Language::日本語) ^ enum_integer(Language::한국어))); +#endif REQUIRE(enum_integer(number::one ^ number::two) == (enum_integer(number::one) ^ enum_integer(number::two))); } @@ -570,6 +708,12 @@ TEST_CASE("bitwise_operators") { number x4 = number::one; x4 |= number::two; REQUIRE(enum_integer(x4) == (enum_integer(number::one) | enum_integer(number::two))); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + Language x5 = Language::日本語; + x5 |= Language::한국어; + REQUIRE(enum_integer(x5) == (enum_integer(Language::日本語) | enum_integer(Language::한국어))); +#endif } SECTION("operator&=") { @@ -588,6 +732,12 @@ TEST_CASE("bitwise_operators") { number x4 = number::one; x4 &= number::two; REQUIRE(enum_integer(x4) == (enum_integer(number::one) & enum_integer(number::two))); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + Language x5 = Language::日本語; + x5 &= Language::한국어; + REQUIRE(enum_integer(x5) == (enum_integer(Language::日本語) & enum_integer(Language::한국어))); +#endif } SECTION("operator^=") { @@ -606,6 +756,12 @@ TEST_CASE("bitwise_operators") { number x4 = number::one; x4 ^= number::two; REQUIRE(enum_integer(x4) == (enum_integer(number::one) ^ enum_integer(number::two))); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + Language x5 = Language::日本語; + x5 ^= Language::한국어; + REQUIRE(enum_integer(x5) == (enum_integer(Language::日本語) ^ enum_integer(Language::한국어))); +#endif } } @@ -613,6 +769,9 @@ TEST_CASE("type_traits") { REQUIRE_FALSE(is_unscoped_enum_v); REQUIRE_FALSE(is_unscoped_enum_v); REQUIRE(is_unscoped_enum_v); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE_FALSE(is_unscoped_enum_v); +#endif REQUIRE(is_unscoped_enum_v); REQUIRE(is_scoped_enum_v); @@ -625,6 +784,9 @@ TEST_CASE("enum_type_name") { REQUIRE(enum_type_name() == "Color"); REQUIRE(enum_type_name() == "Numbers"); REQUIRE(enum_type_name() == "Directions"); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_type_name() == "Language"); +#endif REQUIRE(enum_type_name() == "number"); } @@ -683,6 +845,12 @@ TEST_CASE("extrema") { REQUIRE(magic_enum::detail::reflected_min_v == MAGIC_ENUM_RANGE_MIN); REQUIRE(magic_enum::detail::min_v == -120); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(magic_enum::customize::enum_range::min == MAGIC_ENUM_RANGE_MIN); + REQUIRE(magic_enum::detail::reflected_min_v == MAGIC_ENUM_RANGE_MIN); + REQUIRE(magic_enum::detail::min_v == 10); +#endif + REQUIRE(magic_enum::customize::enum_range::min == 100); REQUIRE(magic_enum::detail::reflected_min_v == 100); REQUIRE(magic_enum::detail::min_v == 100); @@ -711,6 +879,12 @@ TEST_CASE("extrema") { REQUIRE(magic_enum::detail::reflected_max_v == MAGIC_ENUM_RANGE_MAX); REQUIRE(magic_enum::detail::max_v == 120); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(magic_enum::customize::enum_range::max == MAGIC_ENUM_RANGE_MAX); + REQUIRE(magic_enum::detail::reflected_max_v == MAGIC_ENUM_RANGE_MAX); + REQUIRE(magic_enum::detail::max_v == 40); +#endif + REQUIRE(magic_enum::customize::enum_range::max == 300); REQUIRE(magic_enum::detail::reflected_max_v == 300); REQUIRE(magic_enum::detail::max_v == 300); diff --git a/test/test_flags.cpp b/test/test_flags.cpp index 1a574d9..a3f8ea8 100644 --- a/test/test_flags.cpp +++ b/test/test_flags.cpp @@ -55,6 +55,15 @@ enum Directions : std::uint64_t { Right = std::uint64_t{1} << 63, }; +#if defined(MAGIC_ENUM_ENABLE_NONASCII) +enum class Language : int { + 日本語 = 1 << 1, + 한국어 = 1 << 2, + English = 1 << 3, + 😃 = 1 << 4 +}; +#endif + enum number : unsigned long { one = 1 << 1, two = 1 << 2, @@ -106,6 +115,15 @@ TEST_CASE("enum_cast") { REQUIRE(enum_cast("Left").value() == Directions::Left); REQUIRE_FALSE(enum_cast("None").has_value()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_cast("日本語"); + REQUIRE(enum_cast("한국어").value() == Language::한국어); + REQUIRE(enum_cast("English").value() == Language::English); + REQUIRE(lang.value() == Language::日本語); + REQUIRE(enum_cast("😃").value() == Language::😃); + REQUIRE_FALSE(enum_cast("None").has_value()); +#endif + constexpr auto nto = enum_cast("three|one"); REQUIRE(enum_cast("one").value() == number::one); REQUIRE(enum_cast("two").value() == number::two); @@ -141,6 +159,15 @@ TEST_CASE("enum_cast") { REQUIRE(enum_cast(std::uint64_t{1} << 10).value() == Directions::Left); REQUIRE_FALSE(enum_cast(0).has_value()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_cast(1 << 1); + REQUIRE(enum_cast(1 << 2).value() == Language::한국어); + REQUIRE(enum_cast(1 << 3).value() == Language::English); + REQUIRE(lang.value() == Language::日本語); + REQUIRE(enum_cast(1 << 4).value() == Language::😃); + REQUIRE_FALSE(enum_cast(0).has_value()); +#endif + constexpr auto nto = enum_cast(2 | 8); REQUIRE(enum_cast(1 << 1).value() == number::one); REQUIRE(enum_cast(1 << 2).value() == number::two); @@ -178,6 +205,16 @@ TEST_CASE("enum_index") { REQUIRE(dr.value() == 3); REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_index(Language::日本語); + Language korean = Language::한국어; + REQUIRE(enum_index(korean).value() == 1); + REQUIRE(enum_index(Language::English).value() == 2); + REQUIRE(enum_index(Language::😃).value() == 3); + REQUIRE(lang.value() == 0); + REQUIRE_FALSE(enum_index(static_cast(0)).has_value()); +#endif + constexpr auto nto = enum_index(number::three | number::one); REQUIRE(enum_index(number::one).value() == 0); REQUIRE(enum_index(number::two).value() == 1); @@ -215,6 +252,16 @@ TEST_CASE("enum_contains") { REQUIRE(dr); REQUIRE_FALSE(enum_contains(static_cast(0))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_index(Language::日本語); + Language korean = Language::한국어; + REQUIRE(enum_contains(korean)); + REQUIRE(enum_contains(Language::English)); + REQUIRE(enum_contains(Language::😃)); + REQUIRE(lang); + REQUIRE_FALSE(enum_contains(static_cast(0))); +#endif + constexpr auto nto = enum_contains(number::three | number::one); REQUIRE(enum_contains(number::one)); REQUIRE(enum_contains(number::two)); @@ -246,6 +293,15 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains(std::uint64_t{1} << 31)); REQUIRE_FALSE(enum_contains(static_cast(0))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_contains(1 << 1); + REQUIRE(lang); + REQUIRE(enum_contains(1 << 2)); + REQUIRE(enum_contains(1 << 3)); + REQUIRE(enum_contains(1 << 4)); + REQUIRE_FALSE(enum_contains(static_cast(0))); +#endif + constexpr auto nto = enum_contains(8 | 2); REQUIRE(enum_contains(1 << 1)); REQUIRE(enum_contains(1 << 2)); @@ -283,6 +339,15 @@ TEST_CASE("enum_contains") { REQUIRE(enum_contains("Left")); REQUIRE_FALSE(enum_contains("None")); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + auto lang = std::string{"日本語"}; + REQUIRE(enum_contains("한국어")); + REQUIRE(enum_contains("English")); + REQUIRE(enum_contains(lang)); + REQUIRE(enum_contains("😃")); + REQUIRE_FALSE(enum_contains("None")); +#endif + constexpr auto nto = enum_contains("three|one"); REQUIRE(enum_contains("one")); REQUIRE(enum_contains("two")); @@ -311,6 +376,14 @@ TEST_CASE("enum_value") { REQUIRE(enum_value(2) == Directions::Up); REQUIRE(dr == Directions::Right); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto lang = enum_value(3); + REQUIRE(enum_value(0) == Language::日本語); + REQUIRE(enum_value(1) == Language::한국어); + REQUIRE(enum_value(2) == Language::English); + REQUIRE(lang == Language::😃); +#endif + constexpr auto nt = enum_value(2); REQUIRE(enum_value(0) == number::one); REQUIRE(enum_value(1) == number::two); @@ -332,6 +405,11 @@ TEST_CASE("enum_values") { constexpr auto& s4 = enum_values(); REQUIRE(s4 == std::array{{number::one, number::two, number::three, number::four}}); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto& s5 = enum_values(); + REQUIRE(s5 == std::array{{Language::日本語, Language::한국어, Language::English, Language::😃}}); +#endif } TEST_CASE("enum_count") { @@ -346,6 +424,11 @@ TEST_CASE("enum_count") { constexpr auto s4 = enum_count(); REQUIRE(s4 == 4); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto s5 = enum_count(); + REQUIRE(s5 == 4); +#endif } TEST_CASE("enum_name") { @@ -381,6 +464,17 @@ TEST_CASE("enum_name") { REQUIRE(enum_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down) == "Left|Down|Up|Right"); REQUIRE(enum_name(static_cast(0)).empty()); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr Language lang = Language::日本語; + auto lang_name = enum_name(lang); + Language lk = Language::한국어; + REQUIRE(enum_name(lk) == "한국어"); + REQUIRE(enum_name(Language::English) == "English"); + REQUIRE(lang_name == "日本語"); + REQUIRE(enum_name(Language::😃) == "😃"); + REQUIRE(enum_name(static_cast(0)).empty()); +#endif + constexpr number nto = number::three | number::one; auto nto_name = enum_name(nto); REQUIRE(enum_name(number::one) == "one"); @@ -406,6 +500,11 @@ TEST_CASE("enum_names") { constexpr auto& s4 = enum_names(); REQUIRE(s4 == std::array{{"one", "two", "three", "four"}}); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto& s5 = enum_names(); + REQUIRE(s5 == std::array{{"日本語", "한국어", "English", "😃"}}); +#endif } TEST_CASE("enum_entries") { @@ -422,6 +521,11 @@ TEST_CASE("enum_entries") { constexpr auto& s4 = enum_entries(); REQUIRE(s4 == std::array, 4>{{{number::one, "one"}, {number::two, "two"}, {number::three, "three"}, {number::four, "four"}}}); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + constexpr auto& s5 = enum_entries(); + REQUIRE(s5 == std::array, 4>{{{Language::日本語, "日本語"}, {Language::한국어, "한국어"}, {Language::English, "English"}, {Language::😃, "😃"}}}); +#endif } TEST_CASE("ostream_operators") { @@ -455,6 +559,15 @@ TEST_CASE("ostream_operators") { test_ostream(static_cast(0), "0"); test_ostream(std::make_optional(static_cast(0)), "0"); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + test_ostream(std::make_optional(Language::日本語), "日本語"); + test_ostream(Language::한국어, "한국어"); + test_ostream(Language::English, "English"); + test_ostream(Language::😃, "😃"); + test_ostream(static_cast(0), "0"); + test_ostream(std::make_optional(static_cast(0)), "0"); +#endif + test_ostream(std::make_optional(number::one), "one"); test_ostream(number::two, "two"); test_ostream(number::three, "three"); @@ -469,6 +582,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(~Color::RED) == ~enum_integer(Color::RED)); REQUIRE(enum_integer(~Numbers::one) == ~enum_integer(Numbers::one)); REQUIRE(enum_integer(~Directions::Up) == ~enum_integer(Directions::Up)); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(~Language::日本語) == ~enum_integer(Language::日本語)); +#endif REQUIRE(enum_integer(~number::one) == ~enum_integer(number::one)); } @@ -476,6 +592,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(Color::RED | Color::BLUE) == (enum_integer(Color::RED) | enum_integer(Color::BLUE))); REQUIRE(enum_integer(Numbers::one | Numbers::two) == (enum_integer(Numbers::one) | enum_integer(Numbers::two))); REQUIRE(enum_integer(Directions::Up | Directions::Down) == (enum_integer(Directions::Up) | enum_integer(Directions::Down))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(Language::日本語 | Language::한국어) == (enum_integer(Language::日本語) | enum_integer(Language::한국어))); +#endif REQUIRE(enum_integer(number::one | number::two) == (enum_integer(number::one) | enum_integer(number::two))); } @@ -483,6 +602,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(Color::RED & Color::BLUE) == (enum_integer(Color::RED) & enum_integer(Color::BLUE))); REQUIRE(enum_integer(Numbers::one & Numbers::two) == (enum_integer(Numbers::one) & enum_integer(Numbers::two))); REQUIRE(enum_integer(Directions::Up & Directions::Down) == (enum_integer(Directions::Up) & enum_integer(Directions::Down))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(Language::日本語 & Language::한국어) == (enum_integer(Language::日本語) & enum_integer(Language::한국어))); +#endif REQUIRE(enum_integer(number::one & number::two) == (enum_integer(number::one) & enum_integer(number::two))); } @@ -490,6 +612,9 @@ TEST_CASE("bitwise_operators") { REQUIRE(enum_integer(Color::RED ^ Color::BLUE) == (enum_integer(Color::RED) ^ enum_integer(Color::BLUE))); REQUIRE(enum_integer(Numbers::one ^ Numbers::two) == (enum_integer(Numbers::one) ^ enum_integer(Numbers::two))); REQUIRE(enum_integer(Directions::Up ^ Directions::Down) == (enum_integer(Directions::Up) ^ enum_integer(Directions::Down))); +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + REQUIRE(enum_integer(Language::日本語 ^ Language::한국어) == (enum_integer(Language::日本語) ^ enum_integer(Language::한국어))); +#endif REQUIRE(enum_integer(number::one ^ number::two) == (enum_integer(number::one) ^ enum_integer(number::two))); } @@ -509,6 +634,12 @@ TEST_CASE("bitwise_operators") { number x4 = number::one; x4 |= number::two; REQUIRE(enum_integer(x4) == (enum_integer(number::one) | enum_integer(number::two))); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + Language x5 = Language::日本語; + x5 |= Language::한국어; + REQUIRE(enum_integer(x5) == (enum_integer(Language::日本語) | enum_integer(Language::한국어))); +#endif } SECTION("operator&=") { @@ -527,6 +658,12 @@ TEST_CASE("bitwise_operators") { number x4 = number::one; x4 &= number::two; REQUIRE(enum_integer(x4) == (enum_integer(number::one) & enum_integer(number::two))); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + Language x5 = Language::日本語; + x5 &= Language::한국어; + REQUIRE(enum_integer(x5) == (enum_integer(Language::日本語) & enum_integer(Language::한국어))); +#endif } SECTION("operator^=") { @@ -545,5 +682,11 @@ TEST_CASE("bitwise_operators") { number x4 = number::one; x4 ^= number::two; REQUIRE(enum_integer(x4) == (enum_integer(number::one) ^ enum_integer(number::two))); + +#if defined(MAGIC_ENUM_ENABLE_NONASCII) + Language x5 = Language::日本語; + x5 ^= Language::한국어; + REQUIRE(enum_integer(x5) == (enum_integer(Language::日本語) ^ enum_integer(Language::한국어))); +#endif } }