This commit is contained in:
Ivan
2022-04-05 11:42:28 +03:00
commit 6dc0eb0fcf
5565 changed files with 1200500 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
name: macos
on: [push, pull_request]
jobs:
build:
runs-on: ${{matrix.config.os}}
strategy:
fail-fast: false
matrix:
config:
- { os: macos-10.15 }
- { os: macos-11.0 }
build: [Debug, Release]
name: "${{matrix.config.os}}:${{matrix.build}}"
steps:
- uses: actions/checkout@v2
- name: Configure CMake
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build}}
- name: Build
run: cmake --build . --config ${{matrix.build}}
- name: Tests
run: ctest --output-on-failure -C ${{matrix.build}}

View File

@@ -0,0 +1,30 @@
name: ubuntu
on: [push, pull_request]
jobs:
ubuntu:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
build: [Debug, Release]
compiler:
- { cc: "gcc-9", cxx: "g++-9" }
- { cc: "gcc-10", cxx: "g++-10" }
- { cc: "clang-10", cxx: "clang++-10" }
- { cc: "clang-11", cxx: "clang++-11" }
- { cc: "clang-12", cxx: "clang++-12" }
name: "${{matrix.compiler.cxx}}:${{matrix.build}}"
steps:
- uses: actions/checkout@v2
- name: Configure CMake
run: cmake -DCMAKE_BUILD_TYPE=${{matrix.build}} -DCMAKE_C_COMPILER=${{matrix.compiler.cc}} -DCMAKE_CXX_COMPILER=${{matrix.compiler.cxx}}
- name: Build
run: cmake --build . --config ${{matrix.build}}
- name: Tests
run: ctest --output-on-failure -C ${{matrix.build}}

View File

@@ -0,0 +1,28 @@
name: windows
on: [push, pull_request]
jobs:
build:
runs-on: ${{matrix.config.os}}
strategy:
fail-fast: false
matrix:
config:
- { os: windows-2016, vs: "Visual Studio 2017" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2016-Readme.md#visual-studio-enterprise-2017
- { os: windows-2019, vs: "Visual Studio 2019" } # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#visual-studio-enterprise-2019
build: [Debug, Release]
platform: [Win32, x64]
name: "${{matrix.config.vs}}:${{matrix.platform}}:${{matrix.build}}"
steps:
- uses: actions/checkout@v2
- name: Configure CMake
run: cmake -A ${{matrix.platform}}
- name: Build
run: cmake --build . --config ${{matrix.build}}
- name: Tests
run: ctest --output-on-failure -C ${{matrix.build}}

53
thirdparty/magic_enum/.gitignore vendored Normal file
View File

@@ -0,0 +1,53 @@
build/
.vscode/
.vs/
### C++ gitignore ###
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
### CMake gitignore ###
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
### Bazel build artifacts ###
/bazel-*

63
thirdparty/magic_enum/BUILD.bazel vendored Normal file
View File

@@ -0,0 +1,63 @@
licenses(["notice"])
exports_files(["LICENSE"])
package(default_visibility = ["//visibility:public"])
cc_library(
name = "magic_enum",
hdrs = ["include/magic_enum.hpp"],
includes = ["include"],
)
cc_binary(
name = "example",
srcs = ["example/example.cpp"],
copts = ["-std=c++17"],
deps = [":magic_enum"],
)
cc_binary(
name = "enum_flag_example",
srcs = ["example/enum_flag_example.cpp"],
copts = ["-std=c++17"],
deps = [":magic_enum"],
)
cc_binary(
name = "example_custom_name",
srcs = ["example/example_custom_name.cpp"],
copts = ["-std=c++17"],
deps = [":magic_enum"],
)
cc_library(
name = "catch",
srcs = [],
hdrs = ["test/3rdparty/Catch2/include/catch2/catch.hpp"],
strip_include_prefix = "test/3rdparty/Catch2/include",
)
cc_test(
name = "test",
srcs = [
"test/test.cpp",
],
copts = ["-std=c++17"],
deps = [
":catch",
":magic_enum",
],
)
cc_test(
name = "test_flags",
srcs = [
"test/test_flags.cpp",
],
copts = ["-std=c++17"],
deps = [
":catch",
":magic_enum",
],
)

54
thirdparty/magic_enum/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.14)
project(magic_enum VERSION "0.7.3" LANGUAGES CXX)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set(IS_TOPLEVEL_PROJECT TRUE)
else()
set(IS_TOPLEVEL_PROJECT FALSE)
endif()
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})
if(MAGIC_ENUM_OPT_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(MAGIC_ENUM_OPT_BUILD_TESTS)
enable_testing()
add_subdirectory(test)
endif()
include(CMakePackageConfigHelpers)
add_library(${PROJECT_NAME} INTERFACE)
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
target_include_directories(${PROJECT_NAME}
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT)
if(MAGIC_ENUM_OPT_INSTALL)
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Config)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION lib/cmake/${PROJECT_NAME})
install(EXPORT ${PROJECT_NAME}Config
NAMESPACE ${PROJECT_NAME}::
DESTINATION lib/cmake/${PROJECT_NAME})
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include
DESTINATION .)
export(EXPORT ${PROJECT_NAME}Config
NAMESPACE ${PROJECT_NAME}::)
endif()

21
thirdparty/magic_enum/LICENSE vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 - 2021 Daniil Goncharov
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.

250
thirdparty/magic_enum/README.md vendored Normal file
View File

@@ -0,0 +1,250 @@
```text
__ __ _ ______ _____
| \/ | (_) | ____| / ____|_ _
| \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
| |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
| | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
|_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
__/ |
|___/
```
[![Github releases](https://img.shields.io/github/release/Neargye/magic_enum.svg)](https://github.com/Neargye/magic_enum/releases)
[![Conan package](https://img.shields.io/badge/Conan-package-blueviolet)](https://conan.io/center/magic_enum)
[![Vcpkg package](https://img.shields.io/badge/Vcpkg-package-blueviolet)](https://github.com/microsoft/vcpkg/tree/master/ports/magic-enum)
[![Build2 package](https://img.shields.io/badge/Build2-package-blueviolet)](https://www.cppget.org/magic_enum?q=magic_enum)
[![License](https://img.shields.io/github/license/Neargye/magic_enum.svg)](LICENSE)
[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/CmWdR9oPY4vhWYuH)
[![Compiler explorer](https://img.shields.io/badge/compiler_explorer-online-blue.svg)](https://godbolt.org/z/BxfmsH)
# Magic Enum C++
Header-only C++17 library provides static reflection for enums, work with any enum type without any macro or boilerplate code.
* `enum_cast` obtains enum value from string or integer.
* `enum_value` returns enum value at specified index.
* `enum_values` obtains enum value sequence.
* `enum_count` returns number of enum values.
* `enum_integer` obtains integer value from enum value.
* `enum_name` returns name from enum value.
* `enum_names` obtains string enum name sequence.
* `enum_entries` obtains pair (value enum, string enum name) sequence.
* `enum_index` obtains index in enum value sequence from enum value.
* `enum_contains` checks whether enum contains enumerator with such value.
* `enum_type_name` returns name of enum type.
* `is_unscoped_enum` checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration).
* `is_scoped_enum` checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations).
* `underlying_type` improved UB-free "SFINAE-friendly" [underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type).
* `ostream_operators` ostream operators for enums.
* `bitwise_operators` bitwise operators for enums.
## Documentation
* [Reference](doc/reference.md)
* [Limitations](doc/limitations.md)
* [Integration](#Integration)
## Features
* C++17
* Header-only
* Dependency-free
* Compile-time
* Enum to string
* String to enum
* Iterating over enum
## [Examples](example/example.cpp)
```cpp
// For example color enum.
enum class Color { RED = 2, BLUE = 4, GREEN = 8 };
```
* Enum value to string
```cpp
Color color = Color::RED;
auto color_name = magic_enum::enum_name(color);
// color_name -> "RED"
```
* String to enum value
```cpp
std::string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name);
if (color.has_value()) {
// color.value() -> Color::GREEN
}
```
* Integer to enum value
```cpp
int color_integer = 2;
auto color = magic_enum::enum_cast<Color>(color_integer);
if (color.has_value()) {
// color.value() -> Color::RED
}
```
* Indexed access to enum value
```cpp
std::size_t i = 1;
Color color = magic_enum::enum_value<Color>(i);
// color -> Color::BLUE
```
* Enum value sequence
```cpp
constexpr auto& colors = magic_enum::enum_values<Color>();
// colors -> {Color::RED, Color::BLUE, Color::GREEN}
// colors[0] -> Color::RED
```
* Number of enum elements
```cpp
constexpr std::size_t color_count = magic_enum::enum_count<Color>();
// color_count -> 3
```
* Enum value to integer
```cpp
Color color = Color::RED;
auto color_integer = magic_enum::enum_integer(color);
// color -> 2
```
* Enum names sequence
```cpp
constexpr auto& color_names = magic_enum::enum_names<Color>();
// color_names -> {"RED", "BLUE", "GREEN"}
// color_names[0] -> "RED"
```
* Enum entries sequence
```cpp
constexpr auto& color_entries = magic_enum::enum_entries<Color>();
// color_entries -> {{Color::RED, "RED"}, {Color::BLUE, "BLUE"}, {Color::GREEN, "GREEN"}}
// color_entries[0].first -> Color::RED
// color_entries[0].second -> "RED"
```
* Ostream operator for enum
```cpp
using namespace magic_enum::ostream_operators; // out-of-the-box ostream operators for enums.
Color color = Color::BLUE;
std::cout << color << std::endl; // "BLUE"
```
* Bitwise operator for enum
```cpp
enum class Flags { A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3 };
using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for enums.
// Support operators: ~, |, &, ^, |=, &=, ^=.
Flags flags = Flags::A | Flags::B & ~Flags::C;
```
* Checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration).
```cpp
enum color { red, green, blue };
enum class direction { left, right };
magic_enum::is_unscoped_enum<color>::value -> true
magic_enum::is_unscoped_enum<direction>::value -> false
magic_enum::is_unscoped_enum<int>::value -> false
// Helper variable template.
magic_enum::is_unscoped_enum_v<color> -> true
```
* Checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations).
```cpp
enum color { red, green, blue };
enum class direction { left, right };
magic_enum::is_scoped_enum<color>::value -> false
magic_enum::is_scoped_enum<direction>::value -> true
magic_enum::is_scoped_enum<int>::value -> false
// Helper variable template.
magic_enum::is_scoped_enum_v<direction> -> true
```
* Static storage enum variable to string
This version is much lighter on the compile times and is not restricted to the enum_range [limitation](doc/limitations.md).
```cpp
constexpr Color color = Color::BLUE;
constexpr auto color_name = magic_enum::enum_name<color>();
// color_name -> "BLUE"
```
## Remarks
* `magic_enum` does not pretend to be a silver bullet for reflection for enums, it was originally designed for small enum.
* Before use, read the [limitations](doc/limitations.md) of functionality.
* For the small enum use the API from the namespace `magic_enum`, and for enum-flags use the API from the namespace `magic_enum::flags`.
## Integration
* You should add the required file [magic_enum.hpp](include/magic_enum.hpp).
* If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [magic-enum package](https://github.com/microsoft/vcpkg/tree/master/ports/magic-enum).
* If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `magic_enum/x.y.z` to your conan's requires, where `x.y.z` is the release version you want to use.
* If you are using [Build2](https://build2.org/) to build and manage your dependencies, add `depends: magic_enum ^x.y.z` to the manifest file where `x.y.z` is the release version you want to use. You can then import the target using `magic_enum%lib{magic_enum}`.
* Alternatively, you can use something like [CPM](https://github.com/TheLartians/CPM) which is based on CMake's `Fetch_Content` module.
```cmake
CPMAddPackage(
NAME magic_enum
GITHUB_REPOSITORY Neargye/magic_enum
GIT_TAG x.y.z # Where `x.y.z` is the release version you want to use.
)
```
* Bazel is also supported, simply add to your WORKSPACE file:
```
http_archive(
name = "magic_enum",
strip_prefix = "magic_enum-<commit>",
urls = ["https://github.com/Neargye/magic_enum/archive/<commit>.zip"],
)
```
To use bazel inside the repository it's possible to do:
```
bazel build //...
bazel test //...
bazel run //:example
```
(Note that you must use a supported compiler or specify it with `export CC= <compiler>`.)
## Compiler compatibility
* Clang/LLVM >= 5
* MSVC++ >= 14.11 / Visual Studio >= 2017
* Xcode >= 10
* GCC >= 9
* MinGW >= 9
## Licensed under the [MIT License](LICENSE)

0
thirdparty/magic_enum/WORKSPACE vendored Normal file
View File

109
thirdparty/magic_enum/doc/limitations.md vendored Normal file
View File

@@ -0,0 +1,109 @@
# Limitations
* This library uses a compiler-specific hack (based on `__PRETTY_FUNCTION__` / `__FUNCSIG__`), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9.
* To check is magic_enum supported compiler use macro `MAGIC_ENUM_SUPPORTED` or constexpr constant `magic_enum::is_magic_enum_supported`.</br>
If magic_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `MAGIC_ENUM_NO_CHECK_SUPPORT`.
* Enum can't reflect if the enum is a forward declaration.
* 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`.
* `MAGIC_ENUM_RANGE_MIN` must be less or equals than `0` and must be greater than `INT16_MIN`.
* `MAGIC_ENUM_RANGE_MAX` must be greater than `0` and must be less than `INT16_MAX`.
* If need another range for all enum types by default, redefine the macro `MAGIC_ENUM_RANGE_MIN` and `MAGIC_ENUM_RANGE_MAX`.
```cpp
#define MAGIC_ENUM_RANGE_MIN 0
#define MAGIC_ENUM_RANGE_MAX 256
#include <magic_enum.hpp>
```
* If need another range for specific enum type, add specialization `enum_range` for necessary enum type. Specialization of `enum_range` must be injected in `namespace magic_enum::customize`.
```cpp
#include <magic_enum.hpp>
enum class number { one = 100, two = 200, three = 300 };
namespace magic_enum::customize {
template <>
struct enum_range<number> {
static constexpr int min = 100; // Must be greater than `INT16_MIN`.
static constexpr int max = 300; // Must be less than `INT16_MAX`.
};
} // namespace magic_enum
```
* `magic_enum` [won't work if a value is aliased](https://github.com/Neargye/magic_enum/issues/68). Work with enum-aliases is compiler-implementation-defined.
```cpp
enum ShapeKind {
ConvexBegin = 0,
Box = 0, // Won't work.
Sphere = 1,
ConvexEnd = 2,
Donut = 2, // Won't work too.
Banana = 3,
COUNT = 4
};
// magic_enum::enum_cast<ShapeKind>("Box") -> nullopt
// magic_enum::enum_name(ShapeKind::Box) -> "ConvexBegin"
```
One of the possible workaround the issue:
```cpp
enum ShapeKind {
// Convex shapes, see ConvexBegin and ConvexEnd below.
Box = 0,
Sphere = 1,
// Non-convex shapes.
Donut = 2,
Banana = 3,
COUNT = Banana + 1,
// Non-reflected aliases.
ConvexBegin = Box,
ConvexEnd = Sphere + 1
};
// magic_enum::enum_cast<ShapeKind>("Box") -> ShapeKind::Box
// magic_enum::enum_name(ShapeKind::Box) -> "Box"
// Non-reflected aliases.
// magic_enum::enum_cast<ShapeKind>("ConvexBegin") -> nullopt
// magic_enum::enum_name(ShapeKind::ConvexBegin) -> "Box"
```
On some compiler enum-aliases not supported, [for example Visual Studio 2017](https://github.com/Neargye/magic_enum/issues/36), macro `MAGIC_ENUM_SUPPORTED_ALIASES` will be undefined.
```cpp
enum Number {
one = 1,
ONE = 1
};
// magic_enum::enum_cast<Number>("one") -> nullopt
// magic_enum::enum_name(Number::one) -> ""
// magic_enum::enum_cast<Number>("ONE") -> nullopt
// magic_enum::enum_name(Number::ONE) -> ""
```
* If you hit a message like this:
```text
[...]
note: constexpr evaluation hit maximum step limit; possible infinite loop?
```
Change the limit for the number of constexpr evaluated:
* MSVC `/constexpr:depthN`, `/constexpr:stepsN` <https://docs.microsoft.com/en-us/cpp/build/reference/constexpr-control-constexpr-evaluation>
* Clang `-fconstexpr-depth=N`, `-fconstexpr-steps=N` <https://clang.llvm.org/docs/UsersManual.html#controlling-implementation-limits>
* GCC `-fconstexpr-depth=N`, `-fconstexpr-loop-limit=N`, `-fconstexpr-ops-limit=N` <https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/C_002b_002b-Dialect-Options.html>
* Intellisense Visual Studio may have some problems analyzing `magic_enum`.

433
thirdparty/magic_enum/doc/reference.md vendored Normal file
View File

@@ -0,0 +1,433 @@
# Reference
* [`enum_cast` obtains enum value from string or integer.](#enum_cast)
* [`enum_value` returns enum value at specified index.](#enum_value)
* [`enum_values` obtains enum value sequence.](#enum_values)
* [`enum_count` returns number of enum values.](#enum_count)
* [`enum_integer` obtains integer value from enum value.](#enum_integer)
* [`enum_name` returns name from enum value.](#enum_name)
* [`enum_names` obtains string enum name sequence.](#enum_names)
* [`enum_entries` obtains pair (value enum, string enum name) sequence.](#enum_entries)
* [`enum_index` obtains index in enum value sequence from enum value.](#enum_index)
* [`enum_contains` checks whether enum contains enumerator with such value.](#enum_contains)
* [`enum_type_name` returns type name of enum.](#enum_type_name)
* [`is_unscoped_enum` checks whether type is an Unscoped enumeration.](#is_unscoped_enum)
* [`is_scoped_enum` checks whether type is an Scoped enumeration.](#is_scoped_enum)
* [`underlying_type` improved UB-free "SFINAE-friendly" underlying_type.](#underlying_type)
* [`ostream_operators` ostream operators for enums.](#ostream_operators)
* [`bitwise_operators` bitwise operators for enums.](#bitwise_operators)
## Synopsis
* Before use, read the [limitations](limitations.md) of functionality.
* To check is magic_enum supported compiler use macro `MAGIC_ENUM_SUPPORTED` or constexpr constant `magic_enum::is_magic_enum_supported`.</br>
If magic_enum used on unsupported compiler, occurs the compilation error. To suppress error define macro `MAGIC_ENUM_NO_CHECK_SUPPORT`.
* For the small enum use the API from the namespace `magic_enum`, and for enum-flags use the API from the namespace `magic_enum::flags`.
* To add custom enum or type names see the [example](../example/example_custom_name.cpp).
* To change the type of strings or ortional, use special macros:
```cpp
#include <my_lib/string.hpp>
#include <my_lib/string_view.hpp>
#define MAGIC_ENUM_USING_ALIAS_STRING using string = my_lib::String;
#define MAGIC_ENUM_USING_ALIAS_STRING_VIEW using string_view = my_lib::StringView;
#define MAGIC_ENUM_USING_ALIAS_OPTIONAL template <typename T> using optional = my_lib::Optional<T>;
#include <magic_enum.hpp>
```
Optionally define `MAGIC_ENUM_CONFIG_FILE` i.e., in your build system, with path to header file with defined
macros or constants, for example:
```cpp
#define MAGIC_ENUM_CONFIG_FILE "my_magic_enum_cfg.hpp"
```
my_magic_enum_cfg.hpp:
```cpp
#include <my_lib/string.hpp>
#include <my_lib/string_view.hpp>
#define MAGIC_ENUM_USING_ALIAS_STRING using string = my_lib::String;
#define MAGIC_ENUM_USING_ALIAS_STRING_VIEW using string_view = my_lib::StringView;
#define MAGIC_ENUM_USING_ALIAS_OPTIONAL template <typename T> using optional = my_lib::Optional<T>;
#define MAGIC_ENUM_RANGE_MIN 0
#define MAGIC_ENUM_RANGE_MAX 255
```
## `enum_cast`
```cpp
template <typename E>
constexpr optional<E> enum_cast(underlying_type_t<E> value) noexcept;
template <typename E>
constexpr optional<E> enum_cast(string_view value) noexcept;
template <typename E, typename BinaryPredicate>
constexpr optional<E> enum_cast(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v<BinaryPredicate>);
```
* Obtains enum value from string or integer.
* Returns `optional<E>`, using `has_value()` to check contains enum value and `value()` to get the enum value.
* If argument does not enum value, returns empty `optional`.
* Examples
* String to enum value.
```cpp
string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name);
if (color.has_value()) {
// color.value() -> Color::GREEN
}
```
* Integer to enum value.
```cpp
int color_integer = 2;
auto color = magic_enum::enum_cast<Color>(color_integer);
if (color.has_value()) {
// color.value() -> Color::RED
}
```
## `enum_value`
```cpp
template <typename E>
constexpr E enum_value(size_t index) noexcept;
```
* Returns enum value at specified index.
* No bounds checking is performed: the behavior is undefined if `index >= number of enum values`.
* Examples
```cpp
int i = 1;
Color color = magic_enum::enum_value<Color>(i);
// color -> Color::BLUE
````
## `enum_values`
```cpp
template <typename E>
constexpr array<E, N> enum_values() noexcept;
```
* Returns `array<E, N>` with all enum values where `N = number of enum values`, sorted by enum value.
* Examples
```cpp
constexpr auto colors = magic_enum::enum_values<Color>();
// colors -> {Color::RED, Color::BLUE, Color::GREEN}
// colors[0] -> Color::RED
```
## `enum_count`
```cpp
template <typename E>
constexpr size_t enum_count() noexcept;
```
* Returns number of enum values.
* Examples
```cpp
constexpr auto color_count = magic_enum::enum_count<Color>();
// color_count -> 3
```
## `enum_integer`
```cpp
template <typename E>
constexpr underlying_type_t<E> enum_integer(E value) noexcept;
```
* Returns integer value from enum value.
* Examples
```cpp
Color color = Color::RED;
auto color_integer = magic_enum::enum_integer(color);
// color -> 2
```
## `enum_name`
```cpp
template <typename E>
constexpr string_view enum_name(E value) noexcept;
template <auto V>
constexpr string_view enum_name() noexcept;
```
* Returns name from enum value as `string_view` with null-terminated string.
* If enum value does not have name or [out of range](limitations.md), `enum_name(value)` returns empty string.
* If enum value does not have name, `enum_name<value>()` occurs the compilation error `"Enum value does not have a name."`.
* `enum_name<value>()` is much lighter on the compile times and is not restricted to the enum_range [limitation](limitations.md).
* Examples
* Enum value to string.
```cpp
Color color = Color::RED;
auto color_name = magic_enum::enum_name(color);
// color_name -> "RED"
```
* Static storage enum variable to string.
```cpp
constexpr Color color = Color::BLUE;
constexpr auto color_name = magic_enum::enum_name<color>();
// color_name -> "BLUE"
```
## `enum_names`
```cpp
template <typename E>
constexpr array<string_view, N> enum_names() noexcept;
```
* Returns `array<string_view, N>` with all names where `N = number of enum values`, sorted by enum value.
* Examples
```cpp
constexpr auto color_names = magic_enum::enum_names<Color>();
// color_names -> {"RED", "BLUE", "GREEN"}
// color_names[0] -> "RED"
```
## `enum_entries`
```cpp
template <typename E>
constexpr array<pair<E, string_view>, N> enum_entries() noexcept;
```
* Returns `array<pair<E, string_view>, N>` with all pairs (value, name) where `N = number of enum values`, sorted by enum value.
* Examples
```cpp
constexpr auto color_entries = magic_enum::enum_entries<Color>();
// color_entries -> {{Color::RED, "RED"}, {Color::BLUE, "BLUE"}, {Color::GREEN, "GREEN"}}
// color_entries[0].first -> Color::RED
// color_entries[0].second -> "RED"
```
## `enum_index`
```cpp
template <typename E>
constexpr optional<size_t> enum_index() noexcept;
```
* Obtains index in enum values from enum value.
* Returns `optional<size_t>` with index.
* Examples
```cpp
constexpr auto color_index = magic_enum::enum_index(Color::BLUE);
// color_index -> color_index.value() -> 1
// color_index -> color_index.has_value() -> true
```
## `enum_contains`
```cpp
template <typename E>
constexpr bool enum_contains(E value) noexcept;
template <typename E>
constexpr bool enum_contains(underlying_type_t<E> value) noexcept;
template <typename E>
constexpr bool enum_contains(string_view value) noexcept;
template <typename E, typename BinaryPredicate>
constexpr optional<E> enum_contains(string_view value, BinaryPredicate p) noexcept(is_nothrow_invocable_v<BinaryPredicate>);
```
* Checks whether enum contains enumerator with such value.
* Returns true is enum contains value, otherwise false.
* Examples
```cpp
magic_enum::enum_contains(Color::GREEN); // -> true
magic_enum::enum_contains<Color>(2); // -> true
magic_enum::enum_contains<Color>(123); // -> false
magic_enum::enum_contains<Color>("GREEN"); // -> true
magic_enum::enum_contains<Color>("fda"); // -> false
```
## `enum_type_name`
```cpp
template <typename E>
constexpr string_view enum_type_name() noexcept;
```
* Returns type name of enum as `string_view` null-terminated string.
* Examples
```cpp
Color color = Color::RED;
auto type_name = magic_enum::enum_type_name<decltype(color)>();
// color_name -> "Color"
```
## `is_unscoped_enum`
```cpp
template <typename T>
struct is_unscoped_enum;
template <typename T>
inline constexpr bool is_unscoped_enum_v = is_unscoped_enum<T>::value;
```
* Checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration).
* Provides the member constant value which is equal to true, if T is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) type.</br>
Otherwise, value is equal to false.
* Examples
```cpp
magic_enum::is_unscoped_enum<color>::value -> true
magic_enum::is_unscoped_enum<Direction>::value -> false
// Helper variable template.
magic_enum::is_unscoped_enum_v<color> -> true
```
## `is_scoped_enum`
```cpp
template <typename T>
struct is_scoped_enum;
template <typename T>
inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
```
* Checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations).
* Provides the member constant value which is equal to true, if T is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type.</br>
Otherwise, value is equal to false.
* Examples
```cpp
magic_enum::is_scoped_enum<color>::value -> false
magic_enum::is_scoped_enum<Direction>::value -> true
// Helper variable template.
magic_enum::is_scoped_enum_v<Direction> -> true
```
## `underlying_type`
```cpp
template <typename T>
struct underlying_type;
template <typename T>
using underlying_type_t = typename underlying_type<T>::type;
```
* Improved UB-free "SFINAE-friendly" [underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type).
* If T is a complete enumeration type, provides a member typedef type that names the underlying type of T.</br>
Otherwise, if T is not an enumeration type, there is no member type.</br>
Otherwise (T is an incomplete enumeration type), the program is ill-formed.
* Examples
```cpp
magic_enum::underlying_type<color>::type -> int
// Helper types.
magic_enum::underlying_type_t<Direction> -> int
```
## `ostream_operators`
```cpp
template <typename Char, typename Traits, typename E>
basic_ostream<Char, Traits>& operator<<(basic_ostream<Char, Traits>& os, E value);
template <typename Char, typename Traits, typename E>
basic_ostream<Char, Traits>& operator<<(basic_ostream<Char, Traits>& os, optional<E> value);
```
* Out-of-the-box ostream operators for all enums.
* Examples
```cpp
using namespace magic_enum::ostream_operators; // out-of-the-box ostream operators for enums.
Color color = Color::BLUE;
std::cout << color << std::endl; // "BLUE"
```
## `bitwise_operators`
```cpp
template <typename E>
constexpr E operator~(E rhs) noexcept;
template <typename E>
constexpr E operator|(E lhs, E rhs) noexcept;
template <typename E>
constexpr E operator&(E lhs, E rhs) noexcept;
template <typename E>
constexpr E operator^(E lhs, E rhs) noexcept;
template <typename E>
constexpr E& operator|=(E& lhs, E rhs) noexcept;
template <typename E>
constexpr E& operator&=(E& lhs, E rhs) noexcept;
template <typename E>
constexpr E& operator^=(E& lhs, E rhs) noexcept;
```
* Out-of-the-box bitwise operators for all enums.
* Examples
```cpp
enum class Flags { A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3 };
using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for enums.
// Support operators: ~, |, &, ^, |=, &=, ^=.
Flags flags = Flags::A | Flags::B & ~Flags::C;
```

View File

@@ -0,0 +1,22 @@
include(CheckCXXCompilerFlag)
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
set(OPTIONS -Wall -Wextra -pedantic-errors -Werror)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(OPTIONS /W4 /WX)
if(HAS_PERMISSIVE_FLAG)
set(OPTIONS ${OPTIONS} /permissive-)
endif()
endif()
function(make_example target)
add_executable(${target} ${target}.cpp)
set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF)
target_compile_features(${target} PRIVATE cxx_std_17)
target_compile_options(${target} PRIVATE ${OPTIONS})
target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME})
endfunction()
make_example(example)
make_example(enum_flag_example)
make_example(example_custom_name)

View File

@@ -0,0 +1,95 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 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.
#include <iostream>
#include <string>
#include <magic_enum.hpp>
enum class AnimalFlags : std::uint64_t { HasClaws = 1 << 10, CanFly = 1 << 20, EatsFish = 1 << 30, Endangered = std::uint64_t{1} << 40 };
int main() {
// Enum-flags variable to string name.
AnimalFlags f1 = AnimalFlags::Endangered;
auto f1_name = magic_enum::flags::enum_name(f1);
std::cout << f1_name << std::endl; // Endangered
// String enum-flags name sequence.
constexpr auto& names = magic_enum::flags::enum_names<AnimalFlags>();
std::cout << "AnimalFlags names:";
for (const auto& n : names) {
std::cout << " " << n;
}
std::cout << std::endl;
// AnimalFlags names: HasClaws CanFly EatsFish Endangered
// String name to enum-flags value.
auto f2 = magic_enum::flags::enum_cast<AnimalFlags>("EatsFish|CanFly");
if (f2.has_value()) {
std::cout << "EatsFish = " << magic_enum::flags::enum_integer(f2.value()) << std::endl; // CanFly|EatsFish = 1074790400
}
// Integer value to enum-flags value.
auto f3 = magic_enum::flags::enum_cast<AnimalFlags>(1073742848);
if (f3.has_value()) {
std::cout << magic_enum::flags::enum_name(f3.value()) << " = " << magic_enum::flags::enum_integer(f3.value()) << std::endl; // HasClaws|EatsFish = 1073742848
}
// Enum-flags value to integer value.
auto f4_integer = magic_enum::flags::enum_integer(AnimalFlags::HasClaws);
std::cout << "HasClaws = " << f4_integer << std::endl; // HasClaws = 1024
using namespace magic_enum::flags::ostream_operators; // out-of-the-box ostream operator for enum-flags.
// Ostream operator for enum-flags.
std::cout << f1 << " " << f2 << " " << f3 << std::endl; // Endangered CanFly|EatsFish HasClaws|EatsFish
// Number of enum-flags values.
std::cout << "AnimalFlags enum size: " << magic_enum::flags::enum_count<AnimalFlags>() << std::endl; // AnimalFlags enum size: 4
// Indexed access to enum-flags value.
std::cout << "AnimalFlags[0] = " << magic_enum::flags::enum_value<AnimalFlags>(0) << std::endl; // AnimalFlags[0] = HasClaws
// Enum-flags value sequence.
constexpr auto& values = magic_enum::flags::enum_values<AnimalFlags>();
std::cout << "AnimalFlags values:";
for (const auto& f : values) {
std::cout << " " << f; // Ostream operator for enum-flags.
}
std::cout << std::endl;
// AnimalFlags sequence: HasClaws CanFly EatsFish Endangered
using namespace magic_enum::flags::bitwise_operators; // out-of-the-box bitwise operators for all enums.
// Support operators: ~, |, &, ^, |=, &=, ^=.
AnimalFlags flag = AnimalFlags::HasClaws | AnimalFlags::CanFly;
std::cout << flag << std::endl; // HasClaws|CanFly
// Enum-flags pair (value, string name) sequence.
constexpr auto& entries = magic_enum::flags::enum_entries<AnimalFlags>();
std::cout << "AnimalFlags entries:";
for (const auto& e : entries) {
std::cout << " " << e.second << " = " << magic_enum::flags::enum_integer(e.first);
}
std::cout << std::endl;
// AnimalFlags entries: AnimalFlags entries: HasClaws = 1024 CanFly = 1048576 EatsFish = 1073741824 Endangered = 1099511627776
return 0;
}

View File

@@ -0,0 +1,113 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 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.
#include <iostream>
#include <magic_enum.hpp>
enum class Color : int { RED = -10, BLUE = 0, GREEN = 10 };
template <typename E>
auto to_integer(magic_enum::Enum<E> value) {
// magic_enum::Enum<E> - C++17 Concept for enum type.
return static_cast<magic_enum::underlying_type_t<E>>(value);
}
int main() {
// Enum variable to string name.
Color c1 = Color::RED;
auto c1_name = magic_enum::enum_name(c1);
std::cout << c1_name << std::endl; // RED
// String enum name sequence.
constexpr auto& names = magic_enum::enum_names<Color>();
std::cout << "Color names:";
for (const auto& n : names) {
std::cout << " " << n;
}
std::cout << std::endl;
// Color names: RED BLUE GREEN
// String name to enum value.
auto c2 = magic_enum::enum_cast<Color>("BLUE");
if (c2.has_value()) {
std::cout << "BLUE = " << to_integer(c2.value()) << std::endl; // BLUE = 0
}
// Integer value to enum value.
auto c3 = magic_enum::enum_cast<Color>(10);
if (c3.has_value()) {
std::cout << "GREEN = " << magic_enum::enum_integer(c3.value()) << std::endl; // GREEN = 10
}
// Enum value to integer value.
auto c4_integer = magic_enum::enum_integer(Color::RED);
std::cout << "RED = " << c4_integer << std::endl; // RED = -10
using namespace magic_enum::ostream_operators; // out-of-the-box ostream operator for all enums.
// Ostream operator for enum.
std::cout << "Color: " << c1 << " " << c2 << " " << c3 << std::endl; // Color: RED BLUE GREEN
// Number of enum values.
std::cout << "Color enum size: " << magic_enum::enum_count<Color>() << std::endl; // Color size: 3
// Indexed access to enum value.
std::cout << "Color[0] = " << magic_enum::enum_value<Color>(0) << std::endl; // Color[0] = RED
// Enum value sequence.
constexpr auto& values = magic_enum::enum_values<Color>();
std::cout << "Colors values:";
for (const auto& c : values) {
std::cout << " " << c; // Ostream operator for enum.
}
std::cout << std::endl;
// Color sequence: RED BLUE GREEN
enum class Flags { A = 1, B = 2, C = 4, D = 8 };
using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for all enums.
// Support operators: ~, |, &, ^, |=, &=, ^=.
Flags flag = Flags::A | Flags::C;
std::cout << flag << std::endl; // 5
enum color { red, green, blue };
// Checks whether type is an Unscoped enumeration.
static_assert(magic_enum::is_unscoped_enum_v<color>);
static_assert(!magic_enum::is_unscoped_enum_v<Color>);
static_assert(!magic_enum::is_unscoped_enum_v<Flags>);
// Checks whether type is an Scoped enumeration.
static_assert(!magic_enum::is_scoped_enum_v<color>);
static_assert(magic_enum::is_scoped_enum_v<Color>);
static_assert(magic_enum::is_scoped_enum_v<Flags>);
// Enum pair (value enum, string enum name) sequence.
constexpr auto& entries = magic_enum::enum_entries<Color>();
std::cout << "Colors entries:";
for (const auto& e : entries) {
std::cout << " " << e.second << " = " << static_cast<int>(e.first);
}
std::cout << std::endl;
// Color entries: RED = -10 BLUE = 0 GREEN = 10
return 0;
}

View File

@@ -0,0 +1,71 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2020 - 2021 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.
#include <iostream>
#include <magic_enum.hpp>
enum class Color : int { RED = -10, BLUE = 0, GREEN = 10 };
// Сustom definitions of names for enum.
// Specialization of `enum_name` must be injected in `namespace magic_enum::customize`.
template <>
constexpr std::string_view magic_enum::customize::enum_name<Color>(Color value) noexcept {
switch (value) {
case Color::RED:
return "the red color";
case Color::BLUE:
return "The BLUE";
case Color::GREEN:
return {}; // Empty string for default value.
}
return {}; // Empty string for unknow value.
}
enum class Numbers : int { One, Two, Three };
// Сustom definitions of names for enum.
// Specialization of `enum_name` must be injected in `namespace magic_enum::customize`.
template <>
constexpr std::string_view magic_enum::customize::enum_name<Numbers>(Numbers value) noexcept {
switch (value) {
case Numbers::One:
return "the one";
default:
return {}; // Empty string for default or unknow value.
}
}
int main() {
std::cout << magic_enum::enum_name(Color::RED) << std::endl; // the red color
std::cout << magic_enum::enum_name(Color::BLUE) << std::endl; // The BLUE
std::cout << magic_enum::enum_name(Color::GREEN) << std::endl; // GREEN
std::cout << std::boolalpha;
std::cout << (magic_enum::enum_cast<Color>("the red color").value() == Color::RED) << std::endl; // true
std::cout << magic_enum::enum_name(Numbers::One) << std::endl; // the one
std::cout << magic_enum::enum_name(Numbers::Two) << std::endl; // Two
std::cout << magic_enum::enum_name(Numbers::Three) << std::endl; // Three
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
include(CheckCXXCompilerFlag)
set(SOURCES test.cpp)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(OPTIONS /W4 /WX)
check_cxx_compiler_flag(/permissive HAS_PERMISSIVE_FLAG)
if(HAS_PERMISSIVE_FLAG)
set(OPTIONS ${OPTIONS} /permissive-)
endif()
check_cxx_compiler_flag(/std:c++20 HAS_CPP20_FLAG)
check_cxx_compiler_flag(/std:c++latest HAS_CPPLATEST_FLAG)
elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
set(CMAKE_VERBOSE_MAKEFILE ON)
set(OPTIONS -Wall -Wextra -pedantic-errors -Werror)
check_cxx_compiler_flag(-std=c++20 HAS_CPP20_FLAG)
endif()
function(make_test src target std)
add_executable(${target} ${src})
target_compile_options(${target} PRIVATE ${OPTIONS})
target_include_directories(${target} PRIVATE 3rdparty/Catch2/include)
target_link_libraries(${target} PRIVATE ${CMAKE_PROJECT_NAME})
set_target_properties(${target} PROPERTIES CXX_EXTENSIONS OFF)
if(std)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(${target} PRIVATE /std:${std})
else()
target_compile_options(${target} PRIVATE -std=${std})
endif()
endif()
add_test(NAME ${target} COMMAND ${target})
endfunction()
make_test(test.cpp test-cpp17 c++17)
make_test(test_flags.cpp test_flags-cpp17 c++17)
if(HAS_CPP20_FLAG)
make_test(test.cpp test-cpp20 c++20)
make_test(test_flags.cpp test_flags-cpp20 c++20)
endif()
if(HAS_CPPLATEST_FLAG)
make_test(test.cpp test-cpplatest c++latest)
make_test(test_flags.cpp test_flags-cpplatest c++latest)
endif()

739
thirdparty/magic_enum/test/test.cpp vendored Normal file
View File

@@ -0,0 +1,739 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 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>
#undef MAGIC_ENUM_RANGE_MIN
#define MAGIC_ENUM_RANGE_MIN -120
#undef MAGIC_ENUM_RANGE_MAX
#define MAGIC_ENUM_RANGE_MAX 120
#include <magic_enum.hpp>
#include <array>
#include <cctype>
#include <string_view>
#include <sstream>
enum class Color { RED = -12, GREEN = 7, BLUE = 15 };
enum class Numbers : int { one = 1, two, three, many = 127 };
enum Directions { Up = 85, Down = -42, Right = 120, Left = -120 };
enum number : unsigned long {
one = 100,
two = 200,
three = 300,
four = 400,
#if defined(MAGIC_ENUM_SUPPORTED_ALIASES)
_1 = one,
_2 = two,
_3 = three,
_4 = four
#endif
};
namespace magic_enum::customize {
template <>
struct enum_range<number> {
static constexpr int min = 100;
static constexpr int max = 300;
};
} // namespace magic_enum
template <>
constexpr std::string_view magic_enum::customize::enum_name<Color>(Color value) noexcept {
switch (value) {
case Color::RED:
return "red";
default:
return {};
}
}
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>("red");
REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>("GREEN").value() == Color::GREEN);
REQUIRE(enum_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
REQUIRE_FALSE(enum_cast<Color>("None").has_value());
constexpr auto no = enum_cast<Numbers>("one");
REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
REQUIRE(enum_cast<Numbers>("three").value() == Numbers::three);
REQUIRE_FALSE(enum_cast<Numbers>("many").has_value());
REQUIRE_FALSE(enum_cast<Numbers>("None").has_value());
constexpr auto dr = enum_cast<Directions>("Right");
REQUIRE(enum_cast<Directions&>("Up").value() == Directions::Up);
REQUIRE(enum_cast<const Directions>("Down").value() == Directions::Down);
REQUIRE(dr.value() == Directions::Right);
REQUIRE(enum_cast<Directions>("Left").value() == Directions::Left);
REQUIRE_FALSE(enum_cast<Directions>("None").has_value());
constexpr auto nt = enum_cast<number>("three");
REQUIRE(enum_cast<number>("one").value() == number::one);
REQUIRE(enum_cast<number>("two").value() == number::two);
REQUIRE(nt.value() == number::three);
REQUIRE_FALSE(enum_cast<number>("four").has_value());
REQUIRE_FALSE(enum_cast<number>("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());
constexpr auto no = enum_cast<Numbers>(1);
REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>(2).value() == Numbers::two);
REQUIRE(enum_cast<Numbers>(3).value() == Numbers::three);
REQUIRE_FALSE(enum_cast<Numbers>(127).has_value());
REQUIRE_FALSE(enum_cast<Numbers>(0).has_value());
constexpr auto dr = enum_cast<Directions>(120);
REQUIRE(enum_cast<Directions&>(85).value() == Directions::Up);
REQUIRE(enum_cast<const Directions>(-42).value() == Directions::Down);
REQUIRE(dr.value() == Directions::Right);
REQUIRE(enum_cast<Directions>(-120).value() == Directions::Left);
REQUIRE_FALSE(enum_cast<Directions>(0).has_value());
constexpr auto nt = enum_cast<number>(300);
REQUIRE(enum_cast<number>(100).value() == number::one);
REQUIRE(enum_cast<number>(200).value() == number::two);
REQUIRE(nt.value() == number::three);
REQUIRE_FALSE(enum_cast<number>(400).has_value());
REQUIRE_FALSE(enum_cast<number>(0).has_value());
}
}
TEST_CASE("enum_integer") {
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
constexpr auto cr = enum_integer(Color::RED);
Color cg = Color::GREEN;
REQUIRE(cr == -12);
REQUIRE(enum_integer<Color&>(cg) == 7);
REQUIRE(enum_integer(cm[2]) == 15);
REQUIRE(enum_integer(static_cast<Color>(0)) == 0);
constexpr auto no = enum_integer(Numbers::one);
REQUIRE(no == 1);
REQUIRE(enum_integer(Numbers::two) == 2);
REQUIRE(enum_integer(Numbers::three) == 3);
REQUIRE(enum_integer(Numbers::many) == 127);
REQUIRE(enum_integer(static_cast<Numbers>(0)) == 0);
constexpr auto dr = enum_integer(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_integer<Directions&>(dl) == -120);
REQUIRE(enum_integer<const Directions>(Directions::Down) == -42);
REQUIRE(enum_integer(Directions::Up) == 85);
REQUIRE(dr == 120);
REQUIRE(enum_integer(static_cast<Directions>(0)) == 0);
constexpr auto nt = enum_integer(number::three);
REQUIRE(enum_integer(number::one) == 100);
REQUIRE(enum_integer(number::two) == 200);
REQUIRE(nt == 300);
REQUIRE(enum_integer(number::four) == 400);
REQUIRE(enum_integer(static_cast<number>(0)) == 0);
}
TEST_CASE("enum_index") {
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
constexpr auto cr = enum_index(Color::RED);
Color cg = Color::GREEN;
REQUIRE(cr.value() == 0);
REQUIRE(enum_index<Color&>(cg).value() == 1);
REQUIRE(enum_index(cm[2]).value() == 2);
REQUIRE_FALSE(enum_index(static_cast<Color>(0)).has_value());
constexpr auto no = enum_index(Numbers::one);
REQUIRE(no.value() == 0);
REQUIRE(enum_index(Numbers::two).value() == 1);
REQUIRE(enum_index(Numbers::three).value() == 2);
REQUIRE_FALSE(enum_index(Numbers::many).has_value());
REQUIRE_FALSE(enum_index(static_cast<Numbers>(0)).has_value());
constexpr auto dr = enum_index(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_index<Directions&>(dl).value() == 0);
REQUIRE(enum_index<const Directions>(Directions::Down).value() == 1);
REQUIRE(enum_index(Directions::Up).value() == 2);
REQUIRE(dr.value() == 3);
REQUIRE_FALSE(enum_index(static_cast<Directions>(0)).has_value());
constexpr auto nt = enum_index(number::three);
REQUIRE(enum_index(number::one).value() == 0);
REQUIRE(enum_index(number::two).value() == 1);
REQUIRE(nt.value() == 2);
REQUIRE_FALSE(enum_index(number::four).has_value());
REQUIRE_FALSE(enum_index(static_cast<number>(0)).has_value());
}
TEST_CASE("enum_contains") {
SECTION("value") {
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
constexpr auto cr = enum_contains(Color::RED);
Color cg = Color::GREEN;
REQUIRE(cr);
REQUIRE(enum_contains<Color&>(cg));
REQUIRE(enum_contains(cm[2]));
REQUIRE_FALSE(enum_contains(static_cast<Color>(0)));
constexpr auto no = enum_contains(Numbers::one);
REQUIRE(no);
REQUIRE(enum_contains(Numbers::two));
REQUIRE(enum_contains(Numbers::three));
REQUIRE_FALSE(enum_contains(Numbers::many));
REQUIRE_FALSE(enum_contains(static_cast<Numbers>(0)));
constexpr auto dr = enum_contains(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_contains<Directions&>(dl));
REQUIRE(enum_contains<const Directions>(Directions::Down));
REQUIRE(enum_contains(Directions::Up));
REQUIRE(dr);
REQUIRE_FALSE(enum_contains(static_cast<Directions>(0)));
constexpr auto nt = enum_contains(number::three);
REQUIRE(enum_contains(number::one));
REQUIRE(enum_contains<number&>(number::two));
REQUIRE(nt);
REQUIRE_FALSE(enum_contains(number::four));
REQUIRE_FALSE(enum_contains(static_cast<number>(0)));
}
SECTION("integer") {
REQUIRE(enum_contains<Color>(-12));
REQUIRE(enum_contains<Color&>(7));
REQUIRE(enum_contains<Color>(15));
REQUIRE_FALSE(enum_contains<Color>(42));
REQUIRE_FALSE(enum_contains<Color>(-120));
REQUIRE_FALSE(enum_contains<Color>(0));
constexpr auto no = enum_integer(Numbers::one);
REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>(enum_integer(Numbers::two)));
REQUIRE(enum_contains<Numbers>(enum_integer(Numbers::three)));
REQUIRE_FALSE(enum_contains<Numbers>(enum_integer(Numbers::many)));
constexpr auto dr = enum_integer(Directions::Right);
REQUIRE(enum_contains<Directions&>(dr));
REQUIRE(enum_contains<const Directions>(Directions::Down));
REQUIRE(enum_contains<Directions>(Directions::Up));
REQUIRE_FALSE(enum_contains<Directions>(static_cast<Directions>(0)));
constexpr auto nt = enum_contains<number>(number::three);
REQUIRE(enum_contains<number>(number::one));
REQUIRE(enum_contains<number>(100));
REQUIRE(enum_contains<number>(200));
REQUIRE(enum_contains<number>(300));
REQUIRE(enum_contains<number>(number::two));
REQUIRE(nt);
REQUIRE_FALSE(enum_contains<number>(number::four));
REQUIRE_FALSE(enum_contains<number>(111));
REQUIRE_FALSE(enum_contains<number>(0));
}
SECTION("string") {
constexpr auto cr = "red";
REQUIRE(enum_contains<Color>(cr));
REQUIRE(enum_contains<Color&>("GREEN"));
REQUIRE(enum_contains<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE_FALSE(enum_contains<Color>("None"));
constexpr auto no = std::string_view{"one"};
REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>("two"));
REQUIRE(enum_contains<Numbers>("three"));
REQUIRE_FALSE(enum_contains<Numbers>("many"));
REQUIRE_FALSE(enum_contains<Numbers>("None"));
auto dr = std::string{"Right"};
REQUIRE(enum_contains<Directions&>("Up"));
REQUIRE(enum_contains<Directions>("Down"));
REQUIRE(enum_contains<const Directions>(dr));
REQUIRE(enum_contains<Directions>("Left"));
REQUIRE_FALSE(enum_contains<Directions>("None"));
constexpr auto nt = enum_contains<number>("three");
REQUIRE(enum_contains<number>("one"));
REQUIRE(enum_contains<number>("two"));
REQUIRE(nt);
REQUIRE_FALSE(enum_contains<number>("four"));
REQUIRE_FALSE(enum_contains<number>("None"));
}
}
TEST_CASE("enum_value") {
constexpr auto cr = enum_value<Color>(0);
REQUIRE(cr == Color::RED);
REQUIRE(enum_value<Color&>(1) == Color::GREEN);
REQUIRE(enum_value<Color>(2) == Color::BLUE);
constexpr auto no = enum_value<Numbers>(0);
REQUIRE(no == Numbers::one);
REQUIRE(enum_value<Numbers>(1) == Numbers::two);
REQUIRE(enum_value<Numbers>(2) == Numbers::three);
constexpr auto dr = enum_value<Directions>(3);
REQUIRE(enum_value<Directions&>(0) == Directions::Left);
REQUIRE(enum_value<const Directions>(1) == Directions::Down);
REQUIRE(enum_value<Directions>(2) == Directions::Up);
REQUIRE(dr == Directions::Right);
constexpr auto nt = enum_value<number>(2);
REQUIRE(enum_value<number>(0) == number::one);
REQUIRE(enum_value<number>(1) == number::two);
REQUIRE(nt == number::three);
}
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}});
constexpr auto& s2 = enum_values<Numbers>();
REQUIRE(s2 == std::array<Numbers, 3>{{Numbers::one, Numbers::two, Numbers::three}});
constexpr auto& s3 = enum_values<const Directions>();
REQUIRE(s3 == std::array<Directions, 4>{{Directions::Left, Directions::Down, Directions::Up, Directions::Right}});
constexpr auto& s4 = enum_values<number>();
REQUIRE(s4 == std::array<number, 3>{{number::one, number::two, number::three}});
}
TEST_CASE("enum_count") {
constexpr auto s1 = enum_count<Color&>();
REQUIRE(s1 == 3);
constexpr auto s2 = enum_count<Numbers>();
REQUIRE(s2 == 3);
constexpr auto s3 = enum_count<const Directions>();
REQUIRE(s3 == 4);
constexpr auto s4 = enum_count<number>();
REQUIRE(s4 == 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 == "red");
REQUIRE(enum_name<Color&>(cb) == "BLUE");
REQUIRE(enum_name(cm[1]) == "GREEN");
REQUIRE(enum_name(static_cast<Color>(0)).empty());
constexpr Numbers no = Numbers::one;
constexpr auto no_name = enum_name(no);
REQUIRE(no_name == "one");
REQUIRE(enum_name(Numbers::two) == "two");
REQUIRE(enum_name(Numbers::three) == "three");
REQUIRE(enum_name(Numbers::many).empty());
REQUIRE(enum_name(static_cast<Numbers>(0)).empty());
constexpr Directions dr = Directions::Right;
constexpr auto dr_name = enum_name(dr);
Directions du = Directions::Up;
REQUIRE(enum_name<Directions&>(du) == "Up");
REQUIRE(enum_name<const Directions>(Directions::Down) == "Down");
REQUIRE(dr_name == "Right");
REQUIRE(enum_name(Directions::Left) == "Left");
REQUIRE(enum_name(static_cast<Directions>(0)).empty());
constexpr number nt = number::three;
constexpr auto nt_name = enum_name(nt);
REQUIRE(enum_name(number::one) == "one");
REQUIRE(enum_name(number::two) == "two");
REQUIRE(nt_name == "three");
REQUIRE(enum_name(number::four).empty());
REQUIRE(enum_name(static_cast<number>(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 == "red");
REQUIRE(enum_name<Color::BLUE>() == "BLUE");
REQUIRE(enum_name<cm[1]>() == "GREEN");
constexpr Numbers no = Numbers::one;
constexpr auto no_name = enum_name<no>();
REQUIRE(no_name == "one");
REQUIRE(enum_name<Numbers::two>() == "two");
REQUIRE(enum_name<Numbers::three>() == "three");
REQUIRE(enum_name<Numbers::many>() == "many");
constexpr Directions dr = Directions::Right;
constexpr auto dr_name = enum_name<dr>();
REQUIRE(enum_name<Directions::Up>() == "Up");
REQUIRE(enum_name<Directions::Down>() == "Down");
REQUIRE(dr_name == "Right");
REQUIRE(enum_name<Directions::Left>() == "Left");
constexpr number nt = number::three;
constexpr auto nt_name = enum_name<nt>();
REQUIRE(enum_name<number::one>() == "one");
REQUIRE(enum_name<number::two>() == "two");
REQUIRE(nt_name == "three");
REQUIRE(enum_name<number::four>() == "four");
}
}
TEST_CASE("enum_names") {
REQUIRE(std::is_same_v<decltype(magic_enum::enum_names<Color>()), const std::array<std::string_view, 3>&>);
constexpr auto& s1 = enum_names<Color&>();
REQUIRE(s1 == std::array<std::string_view, 3>{{"red", "GREEN", "BLUE"}});
constexpr auto& s2 = enum_names<Numbers>();
REQUIRE(s2 == std::array<std::string_view, 3>{{"one", "two", "three"}});
constexpr auto& s3 = enum_names<const Directions>();
REQUIRE(s3 == std::array<std::string_view, 4>{{"Left", "Down", "Up", "Right"}});
constexpr auto& s4 = enum_names<number>();
REQUIRE(s4 == std::array<std::string_view, 3>{{"one", "two", "three"}});
}
TEST_CASE("enum_entries") {
REQUIRE(std::is_same_v<decltype(magic_enum::enum_entries<Color>()), const std::array<std::pair<Color, std::string_view>, 3>&>);
constexpr auto& s1 = enum_entries<Color&>();
REQUIRE(s1 == std::array<std::pair<Color, std::string_view>, 3>{{{Color::RED, "red"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}});
constexpr auto& s2 = enum_entries<Numbers>();
REQUIRE(s2 == std::array<std::pair<Numbers, std::string_view>, 3>{{{Numbers::one, "one"}, {Numbers::two, "two"}, {Numbers::three, "three"}}});
constexpr auto& s3 = enum_entries<Directions&>();
REQUIRE(s3 == std::array<std::pair<Directions, std::string_view>, 4>{{{Directions::Left, "Left"}, {Directions::Down, "Down"}, {Directions::Up, "Up"}, {Directions::Right, "Right"}}});
constexpr auto& s4 = enum_entries<number>();
REQUIRE(s4 == std::array<std::pair<number, std::string_view>, 3>{{{number::one, "one"}, {number::two, "two"}, {number::three, "three"}}});
}
TEST_CASE("ostream_operators") {
auto test_ostream = [](auto e, std::string_view name) {
using namespace magic_enum::ostream_operators;
std::stringstream ss;
ss << e;
REQUIRE(ss.str() == name);
};
test_ostream(std::make_optional(Color::RED), "red");
test_ostream(Color::GREEN, "GREEN");
test_ostream(Color::BLUE, "BLUE");
test_ostream(static_cast<Color>(0), "0");
test_ostream(std::make_optional(static_cast<Color>(0)), "0");
test_ostream(std::make_optional(Numbers::one), "one");
test_ostream(Numbers::two, "two");
test_ostream(Numbers::three, "three");
test_ostream(Numbers::many, "127");
test_ostream(static_cast<Numbers>(0), "0");
test_ostream(std::make_optional(static_cast<Numbers>(0)), "0");
test_ostream(std::make_optional(Directions::Up), "Up");
test_ostream(Directions::Down, "Down");
test_ostream(Directions::Right, "Right");
test_ostream(Directions::Left, "Left");
test_ostream(static_cast<Directions>(0), "0");
test_ostream(std::make_optional(static_cast<Directions>(0)), "0");
test_ostream(std::make_optional(number::one), "one");
test_ostream(number::two, "two");
test_ostream(number::three, "three");
test_ostream(number::four, "400");
test_ostream(static_cast<number>(0), "0");
test_ostream(std::make_optional(static_cast<number>(0)), "0");
}
TEST_CASE("bitwise_operators") {
using namespace magic_enum::bitwise_operators;
SECTION("operator^") {
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));
REQUIRE(enum_integer(~number::one) == ~enum_integer(number::one));
}
SECTION("operator|") {
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)));
REQUIRE(enum_integer(number::one | number::two) == (enum_integer(number::one) | enum_integer(number::two)));
}
SECTION("operator&") {
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)));
REQUIRE(enum_integer(number::one & number::two) == (enum_integer(number::one) & enum_integer(number::two)));
}
SECTION("operator^") {
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)));
REQUIRE(enum_integer(number::one ^ number::two) == (enum_integer(number::one) ^ enum_integer(number::two)));
}
SECTION("operator|=") {
Color x1 = Color::RED;
x1 |= Color::BLUE;
REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) | enum_integer(Color::BLUE)));
Numbers x2 = Numbers::one;
x2 |= Numbers::two;
REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) | enum_integer(Numbers::two)));
Directions x3 = Directions::Up;
x3 |= Directions::Down;
REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) | enum_integer(Directions::Down)));
number x4 = number::one;
x4 |= number::two;
REQUIRE(enum_integer(x4) == (enum_integer(number::one) | enum_integer(number::two)));
}
SECTION("operator&=") {
Color x1 = Color::RED;
x1 &= Color::BLUE;
REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) & enum_integer(Color::BLUE)));
Numbers x2 = Numbers::one;
x2 &= Numbers::two;
REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) & enum_integer(Numbers::two)));
Directions x3 = Directions::Up;
x3 &= Directions::Down;
REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) & enum_integer(Directions::Down)));
number x4 = number::one;
x4 &= number::two;
REQUIRE(enum_integer(x4) == (enum_integer(number::one) & enum_integer(number::two)));
}
SECTION("operator^=") {
Color x1 = Color::RED;
x1 ^= Color::BLUE;
REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) ^ enum_integer(Color::BLUE)));
Numbers x2 = Numbers::one;
x2 ^= Numbers::two;
REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) ^ enum_integer(Numbers::two)));
Directions x3 = Directions::Up;
x3 ^= Directions::Down;
REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) ^ enum_integer(Directions::Down)));
number x4 = number::one;
x4 ^= number::two;
REQUIRE(enum_integer(x4) == (enum_integer(number::one) ^ enum_integer(number::two)));
}
}
TEST_CASE("type_traits") {
REQUIRE_FALSE(is_unscoped_enum_v<Color>);
REQUIRE_FALSE(is_unscoped_enum_v<Numbers>);
REQUIRE(is_unscoped_enum_v<Directions>);
REQUIRE(is_unscoped_enum_v<number>);
REQUIRE(is_scoped_enum_v<Color>);
REQUIRE(is_scoped_enum_v<Numbers>);
REQUIRE_FALSE(is_scoped_enum_v<Directions>);
REQUIRE_FALSE(is_scoped_enum_v<number>);
}
TEST_CASE("enum_type_name") {
REQUIRE(enum_type_name<Color&>() == "Color");
REQUIRE(enum_type_name<const Numbers>() == "Numbers");
REQUIRE(enum_type_name<const Directions&>() == "Directions");
REQUIRE(enum_type_name<number>() == "number");
}
#if defined(MAGIC_ENUM_SUPPORTED_ALIASES)
TEST_CASE("aliases") {
REQUIRE(enum_count<number>() == 3);
REQUIRE(enum_name(number::one) == enum_name(number::_1));
REQUIRE(enum_name(number::two) == enum_name(number::_2));
REQUIRE(enum_name(number::three) == enum_name(number::_3));
REQUIRE(enum_name(number::four) == enum_name(number::_4));
REQUIRE(enum_integer(number::one) == enum_integer(number::_1));
REQUIRE(enum_integer(number::two) == enum_integer(number::_2));
REQUIRE(enum_integer(number::three) == enum_integer(number::_3));
REQUIRE(enum_integer(number::four) == enum_integer(number::_4));
REQUIRE_FALSE(enum_cast<number>("_1").has_value());
REQUIRE_FALSE(enum_cast<number>("_2").has_value());
REQUIRE_FALSE(enum_cast<number>("_3").has_value());
REQUIRE_FALSE(enum_cast<number>("_4").has_value());
}
#endif
TEST_CASE("extrema") {
enum class BadColor : std::uint64_t {
RED,
GREEN,
YELLOW,
// The value NONE is ignored (out of range).
// However, it affects the value of min_v. When reflected_min_v was incorrect,
// the presence of NONE caused miv_v to be equal to -1, which was then cast to unsigned,
// leading to a value of 18446744073709551615 (numeric_limit_max of uint64_t).
NONE = std::numeric_limits<std::uint64_t>::max()
};
REQUIRE(magic_enum::enum_name<BadColor>(BadColor::NONE).empty());
REQUIRE_FALSE(magic_enum::enum_cast<BadColor>(std::numeric_limits<std::uint64_t>::max()).has_value());
REQUIRE_FALSE(magic_enum::enum_contains<BadColor>(std::numeric_limits<std::uint64_t>::max()));
REQUIRE_FALSE(magic_enum::enum_contains<BadColor>(BadColor::NONE));
SECTION("min") {
REQUIRE(magic_enum::customize::enum_range<BadColor>::min == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::reflected_min_v<BadColor> == 0);
REQUIRE(magic_enum::detail::min_v<BadColor> == 0);
REQUIRE(magic_enum::customize::enum_range<Color>::min == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::reflected_min_v<Color> == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::min_v<Color> == -12);
REQUIRE(magic_enum::customize::enum_range<Numbers>::min == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::reflected_min_v<Numbers> == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::min_v<Numbers> == 1);
REQUIRE(magic_enum::customize::enum_range<Directions>::min == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::reflected_min_v<Directions> == MAGIC_ENUM_RANGE_MIN);
REQUIRE(magic_enum::detail::min_v<Directions> == -120);
REQUIRE(magic_enum::customize::enum_range<number>::min == 100);
REQUIRE(magic_enum::detail::reflected_min_v<number> == 100);
REQUIRE(magic_enum::detail::min_v<number> == 100);
}
SECTION("max") {
REQUIRE(magic_enum::customize::enum_range<BadColor>::max == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::reflected_max_v<BadColor> == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::max_v<BadColor> == 2);
REQUIRE(magic_enum::customize::enum_range<Color>::max == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::reflected_max_v<Color> == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::max_v<Color> == 15);
REQUIRE(magic_enum::customize::enum_range<Numbers>::max == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::reflected_max_v<Numbers> == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::max_v<Numbers> == 3);
REQUIRE(magic_enum::customize::enum_range<Directions>::max == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::reflected_max_v<Directions> == MAGIC_ENUM_RANGE_MAX);
REQUIRE(magic_enum::detail::max_v<Directions> == 120);
REQUIRE(magic_enum::customize::enum_range<number>::max == 300);
REQUIRE(magic_enum::detail::reflected_max_v<number> == 300);
REQUIRE(magic_enum::detail::max_v<number> == 300);
}
}
TEST_CASE("cmp_less") {
using magic_enum::detail::cmp_less;
constexpr std::uint64_t uint64_t_min = std::numeric_limits<std::uint64_t>::min();
constexpr std::uint32_t uint32_t_min = std::numeric_limits<std::uint32_t>::min();
constexpr std::uint32_t uint32_t_max = std::numeric_limits<std::uint32_t>::max();
constexpr std::uint64_t uint64_t_max = std::numeric_limits<std::uint64_t>::max();
constexpr std::int64_t int64_t_min = std::numeric_limits<std::int64_t>::min();
constexpr std::int32_t int32_t_min = std::numeric_limits<std::int32_t>::min();
constexpr std::int32_t int32_t_max = std::numeric_limits<std::int32_t>::max();
constexpr std::int64_t int64_t_max = std::numeric_limits<std::int64_t>::max();
// Also testing with offset to avoid corner cases.
// Two variables to avoid hidden casts:
constexpr std::int64_t offset_int64_t = 17;
constexpr std::int32_t offset_int32_t = 17;
SECTION("same signedness") {
REQUIRE(cmp_less(-5, -3));
REQUIRE(cmp_less(27U, 49U));
}
SECTION("same signedness, different width") {
REQUIRE(cmp_less(uint32_t_max, uint64_t_max));
REQUIRE_FALSE(cmp_less(uint64_t_max, uint32_t_max));
REQUIRE(cmp_less(int64_t_min, int32_t_min));
REQUIRE_FALSE(cmp_less(int32_t_min, int64_t_min));
REQUIRE(cmp_less(int64_t_min + offset_int64_t, int32_t_min + offset_int32_t));
REQUIRE_FALSE(cmp_less(int32_t_min + offset_int32_t, int64_t_min + offset_int64_t));
}
SECTION("left signed, right unsigned") {
REQUIRE(cmp_less(-5, 3U));
REQUIRE(cmp_less(3, 5U));
}
SECTION("left signed, right unsigned, different width") {
REQUIRE(cmp_less(int32_t_max, uint64_t_max));
REQUIRE_FALSE(cmp_less(int64_t_max, uint32_t_max));
REQUIRE(cmp_less(int32_t_min, uint64_t_min));
REQUIRE(cmp_less(int64_t_min, uint32_t_min));
REQUIRE(cmp_less(int32_t_max - offset_int32_t, uint64_t_max));
REQUIRE_FALSE(cmp_less(int64_t_max - offset_int64_t, uint32_t_max));
REQUIRE(cmp_less(int32_t_min + offset_int32_t, uint64_t_min));
REQUIRE(cmp_less(int64_t_min + offset_int64_t, uint32_t_min));
}
SECTION("left unsigned, right signed") {
REQUIRE_FALSE(cmp_less(3U, -5));
REQUIRE(cmp_less(3U, 5));
}
SECTION("left unsigned, right signed, different width") {
REQUIRE(cmp_less(uint32_t_max, int64_t_max));
REQUIRE_FALSE(cmp_less(uint64_t_max, int32_t_max));
REQUIRE_FALSE(cmp_less(uint32_t_min, int64_t_min));
REQUIRE_FALSE(cmp_less(uint64_t_min, int32_t_min));
REQUIRE(cmp_less(uint32_t_max, int64_t_max - offset_int32_t));
REQUIRE_FALSE(cmp_less(uint64_t_max, int32_t_max - offset_int64_t));
REQUIRE_FALSE(cmp_less(uint32_t_min, int64_t_min + offset_int32_t));
REQUIRE_FALSE(cmp_less(uint64_t_min, int32_t_min + offset_int64_t));
}
}

View File

@@ -0,0 +1,549 @@
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2021 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.
#if defined(__clang__)
# pragma clang diagnostic push
#elif defined(__GNUC__)
# pragma GCC diagnostic push
#elif defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable : 4244) // warning C4244: 'argument': conversion from 'const T' to 'unsigned int', possible loss of data.
#endif
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <magic_enum.hpp>
#include <array>
#include <cctype>
#include <string_view>
#include <sstream>
enum class Color { RED = 1, GREEN = 2, BLUE = 4 };
enum class Numbers : int {
one = 1 << 1,
two = 1 << 2,
three = 1 << 3,
many = 1 << 30,
};
enum Directions : std::uint64_t {
Left = std::uint64_t{1} << 10,
Down = std::uint64_t{1} << 20,
Up = std::uint64_t{1} << 31,
Right = std::uint64_t{1} << 63,
};
enum number : unsigned long {
one = 1 << 1,
two = 1 << 2,
three = 1 << 3,
four = 1 << 4,
#if defined(MAGIC_ENUM_SUPPORTED_ALIASES)
_1 = one,
_2 = two,
_3 = three,
_4 = four
#endif
};
namespace magic_enum::customize {
template <>
struct enum_range<number> {
static constexpr int min = 100;
static constexpr int max = 300;
};
} // namespace magic_enum
using namespace magic_enum::flags;
using namespace magic_enum::bitwise_operators;
TEST_CASE("enum_cast") {
SECTION("string") {
constexpr auto cr = enum_cast<Color>("RED");
REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>("GREEN").value() == Color::GREEN);
REQUIRE(enum_cast<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == Color::BLUE);
REQUIRE(enum_cast<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }).value() == (Color::BLUE | Color::RED));
REQUIRE(enum_cast<Color&>("GREEN|RED").value() == (Color::GREEN | Color::RED));
REQUIRE(enum_cast<Color&>("GREEN|RED|RED").value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_cast<Color&>("GREEN|RED|None").has_value());
REQUIRE_FALSE(enum_cast<Color>("None").has_value());
constexpr auto no = enum_cast<Numbers>("one");
REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>("two").value() == Numbers::two);
REQUIRE(enum_cast<Numbers>("three").value() == Numbers::three);
REQUIRE(enum_cast<Numbers>("many") == Numbers::many);
REQUIRE_FALSE(enum_cast<Numbers>("None").has_value());
constexpr auto dr = enum_cast<Directions>("Right");
REQUIRE(enum_cast<Directions&>("Up").value() == Directions::Up);
REQUIRE(enum_cast<const Directions>("Down").value() == Directions::Down);
REQUIRE(dr.value() == Directions::Right);
REQUIRE(enum_cast<Directions>("Left").value() == Directions::Left);
REQUIRE_FALSE(enum_cast<Directions>("None").has_value());
constexpr auto nto = enum_cast<number>("three|one");
REQUIRE(enum_cast<number>("one").value() == number::one);
REQUIRE(enum_cast<number>("two").value() == number::two);
REQUIRE(enum_cast<number>("three").value() == number::three);
REQUIRE(enum_cast<number>("four") == number::four);
REQUIRE(nto.value() == (number::three | number::one));
REQUIRE_FALSE(enum_cast<number>("None").has_value());
}
SECTION("integer") {
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
constexpr auto cr = enum_cast<Color>(1);
REQUIRE(cr.value() == Color::RED);
REQUIRE(enum_cast<Color&>(2).value() == Color::GREEN);
REQUIRE(enum_cast<Color>(static_cast<int>(cm[2])).value() == Color::BLUE);
REQUIRE(enum_cast<Color>(1 | 2).value() == (Color::GREEN | Color::RED));
REQUIRE(enum_cast<Color>(1 | 2 | 1).value() == (Color::GREEN | Color::RED));
REQUIRE_FALSE(enum_cast<Color>(1 | 2 | 8).has_value());
REQUIRE_FALSE(enum_cast<Color>(0).has_value());
constexpr auto no = enum_cast<Numbers>(2);
REQUIRE(no.value() == Numbers::one);
REQUIRE(enum_cast<Numbers>(4).value() == Numbers::two);
REQUIRE(enum_cast<Numbers>(8).value() == Numbers::three);
REQUIRE(enum_cast<Numbers>(1 << 30).value() == Numbers::many);
REQUIRE_FALSE(enum_cast<Numbers>(127).has_value());
REQUIRE_FALSE(enum_cast<Numbers>(0).has_value());
constexpr auto dr = enum_cast<Directions>(std::uint64_t{1} << 63);
REQUIRE(enum_cast<Directions&>(std::uint64_t{1} << 31).value() == Directions::Up);
REQUIRE(enum_cast<const Directions>(std::uint64_t{1} << 20).value() == Directions::Down);
REQUIRE(dr.value() == Directions::Right);
REQUIRE(enum_cast<Directions>(std::uint64_t{1} << 10).value() == Directions::Left);
REQUIRE_FALSE(enum_cast<Directions>(0).has_value());
constexpr auto nto = enum_cast<number>(2 | 8);
REQUIRE(enum_cast<number>(1 << 1).value() == number::one);
REQUIRE(enum_cast<number>(1 << 2).value() == number::two);
REQUIRE(enum_cast<number>(1 << 3).value() == number::three);
REQUIRE(enum_cast<number>(1 << 4).value() == number::four);
REQUIRE(nto.value() == (number::three | number::one));
REQUIRE_FALSE(enum_cast<number>(0).has_value());
}
}
TEST_CASE("enum_index") {
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
constexpr auto cr = enum_index(Color::RED);
Color cg = Color::GREEN;
REQUIRE(cr.value() == 0);
REQUIRE(enum_index<Color&>(cg).value() == 1);
REQUIRE(enum_index(cm[2]).value() == 2);
REQUIRE_FALSE(enum_index<Color>(Color::RED | Color::GREEN).has_value());
REQUIRE_FALSE(enum_index<Color>(Color::RED | Color::GREEN | Color::RED).has_value());
REQUIRE_FALSE(enum_index<Color>(Color::RED | Color{8}).has_value());
REQUIRE_FALSE(enum_index(static_cast<Color>(0)).has_value());
constexpr auto no = enum_index(Numbers::one);
REQUIRE(no.value() == 0);
REQUIRE(enum_index(Numbers::two).value() == 1);
REQUIRE(enum_index(Numbers::three).value() == 2);
REQUIRE(enum_index(Numbers::many).value() == 3);
REQUIRE_FALSE(enum_index(static_cast<Numbers>(0)).has_value());
constexpr auto dr = enum_index(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_index<Directions&>(dl).value() == 0);
REQUIRE(enum_index<const Directions>(Directions::Down).value() == 1);
REQUIRE(enum_index(Directions::Up).value() == 2);
REQUIRE(dr.value() == 3);
REQUIRE_FALSE(enum_index(static_cast<Directions>(0)).has_value());
constexpr auto nto = enum_index(number::three | number::one);
REQUIRE(enum_index(number::one).value() == 0);
REQUIRE(enum_index(number::two).value() == 1);
REQUIRE(enum_index(number::three).value() == 2);
REQUIRE(enum_index(number::four).value() == 3);
REQUIRE_FALSE(nto.has_value());
REQUIRE_FALSE(enum_index(static_cast<number>(0)).has_value());
}
TEST_CASE("enum_contains") {
SECTION("value") {
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
constexpr auto cr = enum_contains(Color::RED);
Color cg = Color::GREEN;
REQUIRE(cr);
REQUIRE(enum_contains<Color&>(cg));
REQUIRE(enum_contains(cm[2]));
REQUIRE(enum_contains<Color>(Color::RED | Color::GREEN));
REQUIRE(enum_contains<Color>(Color::RED | Color::GREEN | Color::GREEN));
REQUIRE_FALSE(enum_contains<Color>(Color::RED | Color{8}));
REQUIRE_FALSE(enum_contains(static_cast<Color>(0)));
constexpr auto no = enum_contains(Numbers::one);
REQUIRE(no);
REQUIRE(enum_contains(Numbers::two));
REQUIRE(enum_contains(Numbers::three));
REQUIRE(enum_contains(Numbers::many));
REQUIRE_FALSE(enum_contains(static_cast<Numbers>(0)));
constexpr auto dr = enum_contains(Directions::Right);
Directions dl = Directions::Left;
REQUIRE(enum_contains<Directions&>(dl));
REQUIRE(enum_contains<const Directions>(Directions::Down));
REQUIRE(enum_contains(Directions::Up));
REQUIRE(dr);
REQUIRE_FALSE(enum_contains(static_cast<Directions>(0)));
constexpr auto nto = enum_contains(number::three | number::one);
REQUIRE(enum_contains(number::one));
REQUIRE(enum_contains<number&>(number::two));
REQUIRE(enum_contains(number::one));
REQUIRE(enum_contains(number::four));
REQUIRE(nto);
REQUIRE_FALSE(enum_contains(static_cast<number>(0)));
}
SECTION("integer") {
REQUIRE(enum_contains<Color>(1));
REQUIRE(enum_contains<Color&>(2));
REQUIRE(enum_contains<Color>(4));
REQUIRE(enum_contains<Color>(1 | 2));
REQUIRE(enum_contains<Color>(1 | 2 | 1));
REQUIRE_FALSE(enum_contains<Color>(1 | 2 | 8));
REQUIRE_FALSE(enum_contains<Color>(0));
constexpr auto no = enum_contains<Numbers>(1 << 1);
REQUIRE(no);
REQUIRE(enum_contains<Numbers>(1 << 2));
REQUIRE(enum_contains<Numbers>(1 << 3));
REQUIRE(enum_contains<Numbers>(1 << 30));
constexpr auto dr = enum_contains<Directions&>(std::uint64_t{1} << 63);
REQUIRE(dr);
REQUIRE(enum_contains<const Directions>(std::uint64_t{1} << 10));
REQUIRE(enum_contains<Directions>(std::uint64_t{1} << 20));
REQUIRE(enum_contains<Directions>(std::uint64_t{1} << 31));
REQUIRE_FALSE(enum_contains<Directions>(static_cast<Directions>(0)));
constexpr auto nto = enum_contains<number>(8 | 2);
REQUIRE(enum_contains<number>(1 << 1));
REQUIRE(enum_contains<number>(1 << 2));
REQUIRE(enum_contains<number>(1 << 3));
REQUIRE(enum_contains<number>(1 << 4));
REQUIRE(enum_contains<number>(8 | 2 | 16));
REQUIRE(enum_contains<number>(8 | 16 | 16));
REQUIRE(nto);
REQUIRE_FALSE(enum_contains<number>(8 | 64));
REQUIRE_FALSE(enum_contains<number>(0));
}
SECTION("string") {
constexpr auto cr = "RED";
REQUIRE(enum_contains<Color>(cr));
REQUIRE(enum_contains<Color&>("GREEN"));
REQUIRE(enum_contains<Color>("blue", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&>("blue|RED", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); }));
REQUIRE(enum_contains<Color&>("GREEN|RED"));
REQUIRE(enum_contains<Color&>("GREEN|RED|RED"));
REQUIRE_FALSE(enum_contains<Color>("GREEN|RED|None"));
REQUIRE_FALSE(enum_contains<Color>("None"));
constexpr auto no = std::string_view{"one"};
REQUIRE(enum_contains<Numbers>(no));
REQUIRE(enum_contains<Numbers>("two"));
REQUIRE(enum_contains<Numbers>("three"));
REQUIRE(enum_contains<Numbers>("many"));
REQUIRE_FALSE(enum_contains<Numbers>("None"));
auto dr = std::string{"Right"};
REQUIRE(enum_contains<Directions&>("Up"));
REQUIRE(enum_contains<Directions>("Down"));
REQUIRE(enum_contains<const Directions>(dr));
REQUIRE(enum_contains<Directions>("Left"));
REQUIRE_FALSE(enum_contains<Directions>("None"));
constexpr auto nto = enum_contains<number>("three|one");
REQUIRE(enum_contains<number>("one"));
REQUIRE(enum_contains<number>("two"));
REQUIRE(enum_contains<number>("three"));
REQUIRE(enum_contains<number>("four"));
REQUIRE(nto);
REQUIRE_FALSE(enum_contains<number>("None"));
}
}
TEST_CASE("enum_value") {
constexpr auto cr = enum_value<Color>(0);
REQUIRE(cr == Color::RED);
REQUIRE(enum_value<Color&>(1) == Color::GREEN);
REQUIRE(enum_value<Color>(2) == Color::BLUE);
constexpr auto no = enum_value<Numbers>(0);
REQUIRE(no == Numbers::one);
REQUIRE(enum_value<Numbers>(1) == Numbers::two);
REQUIRE(enum_value<Numbers>(2) == Numbers::three);
REQUIRE(enum_value<Numbers>(3) == Numbers::many);
constexpr auto dr = enum_value<Directions>(3);
REQUIRE(enum_value<Directions&>(0) == Directions::Left);
REQUIRE(enum_value<const Directions>(1) == Directions::Down);
REQUIRE(enum_value<Directions>(2) == Directions::Up);
REQUIRE(dr == Directions::Right);
constexpr auto nt = enum_value<number>(2);
REQUIRE(enum_value<number>(0) == number::one);
REQUIRE(enum_value<number>(1) == number::two);
REQUIRE(nt == number::three);
REQUIRE(enum_value<number>(3) == number::four);
}
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}});
constexpr auto& s2 = enum_values<Numbers>();
REQUIRE(s2 == std::array<Numbers, 4>{{Numbers::one, Numbers::two, Numbers::three, Numbers::many}});
constexpr auto& s3 = enum_values<const Directions>();
REQUIRE(s3 == std::array<Directions, 4>{{Directions::Left, Directions::Down, Directions::Up, Directions::Right}});
constexpr auto& s4 = enum_values<number>();
REQUIRE(s4 == std::array<number, 4>{{number::one, number::two, number::three, number::four}});
}
TEST_CASE("enum_count") {
constexpr auto s1 = enum_count<Color&>();
REQUIRE(s1 == 3);
constexpr auto s2 = enum_count<Numbers>();
REQUIRE(s2 == 4);
constexpr auto s3 = enum_count<const Directions>();
REQUIRE(s3 == 4);
constexpr auto s4 = enum_count<number>();
REQUIRE(s4 == 4);
}
TEST_CASE("enum_name") {
SECTION("automatic storage") {
constexpr Color cr = Color::RED;
auto cr_name = enum_name(cr);
Color cm[3] = {Color::RED, Color::GREEN, Color::BLUE};
Color cb = Color::BLUE;
REQUIRE(cr_name == "RED");
REQUIRE(enum_name<Color&>(cb) == "BLUE");
REQUIRE(enum_name(cm[1]) == "GREEN");
REQUIRE(enum_name(Color::RED | Color{0}) == "RED");
REQUIRE(enum_name(Color::RED | Color::GREEN) == "RED|GREEN");
REQUIRE(enum_name(Color::RED | Color{8}).empty());
REQUIRE(enum_name(static_cast<Color>(0)).empty());
constexpr Numbers no = Numbers::one;
auto no_name = enum_name(no);
REQUIRE(no_name == "one");
REQUIRE(enum_name(Numbers::two) == "two");
REQUIRE(enum_name(Numbers::three) == "three");
REQUIRE(enum_name(Numbers::many) == "many");
REQUIRE(enum_name(Numbers::many | Numbers::two) == "two|many");
REQUIRE(enum_name(static_cast<Numbers>(0)).empty());
constexpr Directions dr = Directions::Right;
auto dr_name = enum_name(dr);
Directions du = Directions::Up;
REQUIRE(enum_name<Directions&>(du) == "Up");
REQUIRE(enum_name<const Directions>(Directions::Down) == "Down");
REQUIRE(dr_name == "Right");
REQUIRE(enum_name(Directions::Left) == "Left");
REQUIRE(enum_name(Directions::Right | Directions::Up | Directions::Left | Directions::Down) == "Left|Down|Up|Right");
REQUIRE(enum_name(static_cast<Directions>(0)).empty());
constexpr number nto = number::three | number::one;
auto nto_name = enum_name(nto);
REQUIRE(enum_name(number::one) == "one");
REQUIRE(enum_name(number::two) == "two");
REQUIRE(enum_name(number::three) == "three");
REQUIRE(enum_name(number::four) == "four");
REQUIRE(nto_name == "one|three");
REQUIRE(enum_name(static_cast<number>(0)).empty());
}
}
TEST_CASE("enum_names") {
REQUIRE(std::is_same_v<decltype(magic_enum::enum_names<Color>()), const std::array<std::string_view, 3>&>);
constexpr auto& s1 = enum_names<Color&>();
REQUIRE(s1 == std::array<std::string_view, 3>{{"RED", "GREEN", "BLUE"}});
constexpr auto& s2 = enum_names<Numbers>();
REQUIRE(s2 == std::array<std::string_view, 4>{{"one", "two", "three", "many"}});
constexpr auto& s3 = enum_names<const Directions>();
REQUIRE(s3 == std::array<std::string_view, 4>{{"Left", "Down", "Up", "Right"}});
constexpr auto& s4 = enum_names<number>();
REQUIRE(s4 == std::array<std::string_view, 4>{{"one", "two", "three", "four"}});
}
TEST_CASE("enum_entries") {
REQUIRE(std::is_same_v<decltype(magic_enum::enum_entries<Color>()), const std::array<std::pair<Color, std::string_view>, 3>&>);
constexpr auto& s1 = enum_entries<Color&>();
REQUIRE(s1 == std::array<std::pair<Color, std::string_view>, 3>{{{Color::RED, "RED"}, {Color::GREEN, "GREEN"}, {Color::BLUE, "BLUE"}}});
constexpr auto& s2 = enum_entries<Numbers>();
REQUIRE(s2 == std::array<std::pair<Numbers, std::string_view>, 4>{{{Numbers::one, "one"}, {Numbers::two, "two"}, {Numbers::three, "three"}, {Numbers::many, "many"}}});
constexpr auto& s3 = enum_entries<Directions&>();
REQUIRE(s3 == std::array<std::pair<Directions, std::string_view>, 4>{{{Directions::Left, "Left"}, {Directions::Down, "Down"}, {Directions::Up, "Up"}, {Directions::Right, "Right"}}});
constexpr auto& s4 = enum_entries<number>();
REQUIRE(s4 == std::array<std::pair<number, std::string_view>, 4>{{{number::one, "one"}, {number::two, "two"}, {number::three, "three"}, {number::four, "four"}}});
}
TEST_CASE("ostream_operators") {
auto test_ostream = [](auto e, std::string_view name) {
using namespace magic_enum::flags::ostream_operators;
std::stringstream ss;
ss << e;
REQUIRE(ss.str() == name);
};
test_ostream(std::make_optional(Color::RED), "RED");
test_ostream(Color::GREEN, "GREEN");
test_ostream(Color::BLUE, "BLUE");
test_ostream(Color::BLUE | Color::RED, "RED|BLUE");
test_ostream(Color::BLUE | Color::RED | Color::RED, "RED|BLUE");
test_ostream(static_cast<Color>(0), "0");
test_ostream(std::make_optional(static_cast<Color>(0)), "0");
test_ostream(std::make_optional(Numbers::one), "one");
test_ostream(Numbers::two, "two");
test_ostream(Numbers::three, "three");
test_ostream(Numbers::many, "many");
test_ostream(static_cast<Numbers>(0), "0");
test_ostream(std::make_optional(static_cast<Numbers>(0)), "0");
test_ostream(std::make_optional(Directions::Up), "Up");
test_ostream(Directions::Down, "Down");
test_ostream(Directions::Right, "Right");
test_ostream(Directions::Left, "Left");
test_ostream(Directions::Right | Directions::Left, "Left|Right");
test_ostream(static_cast<Directions>(0), "0");
test_ostream(std::make_optional(static_cast<Directions>(0)), "0");
test_ostream(std::make_optional(number::one), "one");
test_ostream(number::two, "two");
test_ostream(number::three, "three");
test_ostream(number::four, "four");
test_ostream(number::four | number::one, "one|four");
test_ostream(static_cast<number>(0), "0");
test_ostream(std::make_optional(static_cast<number>(0)), "0");
}
TEST_CASE("bitwise_operators") {
SECTION("operator^") {
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));
REQUIRE(enum_integer(~number::one) == ~enum_integer(number::one));
}
SECTION("operator|") {
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)));
REQUIRE(enum_integer(number::one | number::two) == (enum_integer(number::one) | enum_integer(number::two)));
}
SECTION("operator&") {
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)));
REQUIRE(enum_integer(number::one & number::two) == (enum_integer(number::one) & enum_integer(number::two)));
}
SECTION("operator^") {
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)));
REQUIRE(enum_integer(number::one ^ number::two) == (enum_integer(number::one) ^ enum_integer(number::two)));
}
SECTION("operator|=") {
Color x1 = Color::RED;
x1 |= Color::BLUE;
REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) | enum_integer(Color::BLUE)));
Numbers x2 = Numbers::one;
x2 |= Numbers::two;
REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) | enum_integer(Numbers::two)));
Directions x3 = Directions::Up;
x3 |= Directions::Down;
REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) | enum_integer(Directions::Down)));
number x4 = number::one;
x4 |= number::two;
REQUIRE(enum_integer(x4) == (enum_integer(number::one) | enum_integer(number::two)));
}
SECTION("operator&=") {
Color x1 = Color::RED;
x1 &= Color::BLUE;
REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) & enum_integer(Color::BLUE)));
Numbers x2 = Numbers::one;
x2 &= Numbers::two;
REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) & enum_integer(Numbers::two)));
Directions x3 = Directions::Up;
x3 &= Directions::Down;
REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) & enum_integer(Directions::Down)));
number x4 = number::one;
x4 &= number::two;
REQUIRE(enum_integer(x4) == (enum_integer(number::one) & enum_integer(number::two)));
}
SECTION("operator^=") {
Color x1 = Color::RED;
x1 ^= Color::BLUE;
REQUIRE(enum_integer(x1) == (enum_integer(Color::RED) ^ enum_integer(Color::BLUE)));
Numbers x2 = Numbers::one;
x2 ^= Numbers::two;
REQUIRE(enum_integer(x2) == (enum_integer(Numbers::one) ^ enum_integer(Numbers::two)));
Directions x3 = Directions::Up;
x3 ^= Directions::Down;
REQUIRE(enum_integer(x3) == (enum_integer(Directions::Up) ^ enum_integer(Directions::Down)));
number x4 = number::one;
x4 ^= number::two;
REQUIRE(enum_integer(x4) == (enum_integer(number::one) ^ enum_integer(number::two)));
}
}