├── .clang-format ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── CMakePresets.json ├── LICENSE ├── README.md ├── benchmarks ├── CMakeLists.txt └── generate_compile_time_tests.py ├── cmake └── enchantumConfig.cmake.in ├── docs ├── features.md └── limitations.md ├── enchantum └── include │ └── enchantum │ ├── algorithms.hpp │ ├── all.hpp │ ├── array.hpp │ ├── bitflags.hpp │ ├── bitset.hpp │ ├── bitwise_operators.hpp │ ├── common.hpp │ ├── details │ ├── enchantum_clang.hpp │ ├── enchantum_gcc.hpp │ ├── enchantum_msvc.hpp │ ├── format_util.hpp │ ├── generate_arrays.hpp │ ├── optional.hpp │ ├── string.hpp │ └── string_view.hpp │ ├── enchantum.hpp │ ├── entries.hpp │ ├── fmt_format.hpp │ ├── iostream.hpp │ ├── istream.hpp │ ├── next_value.hpp │ ├── ostream.hpp │ ├── scoped.hpp │ ├── std_format.hpp │ └── type_name.hpp ├── single_include └── enchantum_single_header.hpp └── tests ├── CMakeLists.txt ├── algorithms.cpp ├── array.cpp ├── bitflags.cpp ├── bitset.cpp ├── case_insensitive.hpp ├── concepts.cpp ├── different_entries_types.cpp ├── double_include.cpp ├── enchantum.cpp ├── fmt_format.cpp ├── functors.cpp ├── istream.cpp ├── next_value.cpp ├── null_terminated.cpp ├── ostream.cpp ├── prefix_length.cpp ├── scoped.cpp ├── std_format.cpp ├── test_utility.hpp ├── third_party └── fmt │ ├── args.h │ ├── base.h │ ├── chrono.h │ ├── color.h │ ├── compile.h │ ├── core.h │ ├── format-inl.h │ ├── format.h │ ├── os.h │ ├── ostream.h │ ├── printf.h │ ├── ranges.h │ ├── std.h │ └── xchar.h ├── type_name.cpp └── unscoped_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | # Project 2 | Language: Cpp 3 | Standard: c++20 4 | 5 | ColumnLimit: 120 6 | 7 | # Indentation 8 | AccessModifierOffset: -2 9 | BitFieldColonSpacing: Both 10 | ContinuationIndentWidth: 2 11 | IndentCaseLabels: true 12 | IndentCaseBlocks: false 13 | IndentExternBlock: Indent 14 | IndentPPDirectives: BeforeHash 15 | IndentRequires: true 16 | IndentWidth: 2 17 | IndentWrappedFunctionNames: true 18 | TabWidth: 2 19 | UseTab: Never 20 | # Alignment 21 | 22 | AlignAfterOpenBracket: Align 23 | AlignConsecutiveAssignments: Consecutive 24 | AlignConsecutiveBitFields: Consecutive 25 | AlignConsecutiveDeclarations: Consecutive 26 | AlignConsecutiveMacros: Consecutive 27 | AlignEscapedNewlines: Left 28 | AlignOperands: DontAlign 29 | AlignTrailingComments: true 30 | 31 | # Allow 32 | AllowAllArgumentsOnNextLine: false 33 | AllowAllConstructorInitializersOnNextLine: true 34 | AllowAllParametersOfDeclarationOnNextLine: false 35 | AllowShortBlocksOnASingleLine: Never 36 | AllowShortCaseLabelsOnASingleLine: false 37 | AllowShortEnumsOnASingleLine: false 38 | AllowShortFunctionsOnASingleLine: true 39 | AllowShortIfStatementsOnASingleLine: Never 40 | AllowShortLoopsOnASingleLine: false 41 | AllowShortLambdasOnASingleLine: All 42 | 43 | # Break 44 | AlwaysBreakAfterReturnType: None 45 | AlwaysBreakBeforeMultilineStrings: true 46 | AlwaysBreakTemplateDeclarations: Yes 47 | BreakBeforeConceptDeclarations: true 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializers: BeforeComma 50 | BreakBeforeBinaryOperators: None 51 | BreakInheritanceList: AfterColon 52 | BreakStringLiterals: true 53 | 54 | # Initializers & arguments 55 | BinPackArguments: false 56 | BinPackParameters: false 57 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 58 | ConstructorInitializerIndentWidth: 0 59 | Cpp11BracedListStyle: true 60 | # Braces 61 | BreakBeforeBraces: Stroustrup 62 | # BraceWrapping: 63 | # AfterCaseLabel: false 64 | # AfterClass: false 65 | # AfterControlStatement: Always 66 | # AfterEnum: false 67 | # AfterFunction: true 68 | # AfterNamespace: false 69 | # AfterStruct: false 70 | # AfterUnion: false 71 | # AfterExternBlock: false 72 | # BeforeCatch: false 73 | # BeforeElse: false 74 | # BeforeLambdaBody: true 75 | # BeforeWhile: false 76 | # IndentBraces: false 77 | # SplitEmptyFunction: true 78 | # SplitEmptyRecord: true 79 | # SplitEmptyNamespace: true 80 | 81 | # Namespaces 82 | ShortNamespaceLines: 0 83 | CompactNamespaces: false 84 | FixNamespaceComments: true 85 | NamespaceIndentation: Inner 86 | 87 | # Derive 88 | DeriveLineEnding: false 89 | DerivePointerAlignment: false 90 | 91 | # Empty lines 92 | EmptyLineBeforeAccessModifier: Leave 93 | KeepEmptyLinesAtTheStartOfBlocks: true 94 | MaxEmptyLinesToKeep: 2 95 | 96 | 97 | # Sorting 98 | SortUsingDeclarations: true 99 | 100 | # Penalties 101 | PenaltyBreakAssignment: 1000 102 | PenaltyBreakBeforeFirstCallParameter: 1000 103 | PenaltyBreakComment: 200 104 | PenaltyBreakFirstLessLess: 100 105 | PenaltyBreakString: 1 106 | PenaltyBreakTemplateDeclaration: 0 107 | PenaltyExcessCharacter: 5 108 | PenaltyIndentedWhitespace: 1 109 | PenaltyReturnTypeOnItsOwnLine: 5000000 110 | 111 | # Pointer alignment 112 | PointerAlignment: Left 113 | 114 | # Comments 115 | ReflowComments: false 116 | 117 | # Spaces 118 | SpaceAfterCStyleCast: false 119 | SpaceAfterLogicalNot: false 120 | SpaceAfterTemplateKeyword: false 121 | SpaceAroundPointerQualifiers: Default 122 | SpaceBeforeAssignmentOperators: true 123 | SpaceBeforeCaseColon: false 124 | SpaceBeforeCpp11BracedList: false 125 | SpaceBeforeCtorInitializerColon: true 126 | SpaceBeforeInheritanceColon: true 127 | SpaceBeforeParens: ControlStatements 128 | SpaceBeforeRangeBasedForLoopColon: true 129 | SpaceBeforeSquareBrackets: false 130 | SpaceInEmptyBlock: false 131 | SpaceInEmptyParentheses: false 132 | SpacesBeforeTrailingComments: 1 133 | SpacesInAngles: false 134 | SpacesInCStyleCastParentheses: false 135 | SpacesInConditionalStatement: false 136 | SpacesInContainerLiterals: false 137 | SpacesInParentheses: false 138 | SpacesInSquareBrackets: false 139 | 140 | # Line endings 141 | UseCRLF: false 142 | 143 | # Qualifiers (const, volatile, static, etc) 144 | QualifierAlignment: Custom 145 | QualifierOrder: ['static', 'inline', 'constexpr', 'const', 'volatile', 'type'] 146 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | workflow_dispatch: 9 | 10 | defaults: 11 | run: 12 | shell: bash 13 | 14 | jobs: 15 | build: 16 | name: ${{ matrix.platform.name }} - ${{ matrix.build_type.name }} 17 | runs-on: ${{ matrix.platform.os }} 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | platform: 23 | - { name: Windows MSVC 2022, os: windows-2022, compiler: msvc,flags: "" } 24 | 25 | - { name: Windows Clang 18, os: windows-latest, compiler: clang, version: 18.1.0, flags: "-DCMAKE_CXX_COMPILER=clang++" } 26 | - { name: Windows Clang 19, os: windows-latest, compiler: clang, version: 19.1.0, flags: "-DCMAKE_CXX_COMPILER=clang++" } 27 | - { name: Windows Clang 20, os: windows-latest, compiler: clang, version: 20.1.0, flags: "-DCMAKE_CXX_COMPILER=clang++" } 28 | 29 | - { name: MacOS Clang Latest, os: macos-latest, compiler: clang, flags: "-DCMAKE_CXX_COMPILER=clang++" } 30 | 31 | - { name: Ubuntu GCC 11, os: ubuntu-latest, compiler: gcc, version: 11, flags: "-DCMAKE_CXX_COMPILER=g++" } 32 | - { name: Ubuntu GCC 12, os: ubuntu-latest, compiler: gcc, version: 12, flags: "-DCMAKE_CXX_COMPILER=g++" } 33 | - { name: Ubuntu GCC 13, os: ubuntu-latest, compiler: gcc, version: 13, flags: "-DCMAKE_CXX_COMPILER=g++" } 34 | - { name: Ubuntu GCC 14, os: ubuntu-latest, compiler: gcc, version: 14, flags: "-DCMAKE_CXX_COMPILER=g++" } 35 | 36 | - { name: Ubuntu Clang 16, os: ubuntu-latest, compiler: clang, version: 16, flags: "-DCMAKE_CXX_COMPILER=clang++" } 37 | - { name: Ubuntu Clang 17, os: ubuntu-latest, compiler: clang, version: 17, flags: "-DCMAKE_CXX_COMPILER=clang++" } 38 | - { name: Ubuntu Clang 18, os: ubuntu-latest, compiler: clang, version: 18, flags: "-DCMAKE_CXX_COMPILER=clang++" } 39 | - { name: Ubuntu Clang 19, os: ubuntu-latest, compiler: clang, version: 19, flags: "-DCMAKE_CXX_COMPILER=clang++" } 40 | - { name: Ubuntu Clang 20, os: ubuntu-latest, compiler: clang, version: 20, flags: "-DCMAKE_CXX_COMPILER=clang++" } 41 | build_type: 42 | - { name: Debug, flags: "-DCMAKE_BUILD_TYPE=Debug" } 43 | - { name: Release, flags: "-DCMAKE_BUILD_TYPE=Release" } 44 | 45 | steps: 46 | - name: Checkout Code 47 | uses: actions/checkout@v4 48 | 49 | - name: Install CMake and Ninja 50 | uses: lukka/get-cmake@latest 51 | 52 | - name: Setup MSVC Dev Command Prompt 53 | if: runner.os == 'Windows' && matrix.platform.compiler == 'msvc' 54 | uses: ilammy/msvc-dev-cmd@v1 55 | 56 | - name: Setup Clang (Windows) 57 | if: runner.os == 'Windows' && matrix.platform.compiler == 'clang' 58 | run: | 59 | choco install -y llvm --version=${{ matrix.platform.version }} --force 60 | echo "C:\\Program Files\\LLVM\\bin" >> $GITHUB_PATH 61 | 62 | - name: Setup Clang (Linux) 63 | if: runner.os == 'Linux' && matrix.platform.compiler == 'clang' 64 | uses: egor-tensin/setup-clang@v1 65 | with: 66 | version: ${{ matrix.platform.version }} 67 | 68 | - name: Setup GCC 69 | if: matrix.platform.compiler == 'gcc' 70 | uses: egor-tensin/setup-gcc@v1 71 | with: 72 | version: ${{ matrix.platform.version }} 73 | 74 | - name: Configure CMake 75 | run: > 76 | cmake -B build 77 | -G Ninja 78 | ${{ matrix.platform.flags }} 79 | ${{ matrix.build_type.flags }} 80 | -DENCHANTUM_BUILD_TESTS=ON 81 | 82 | - name: Build 83 | run: cmake --build build 84 | 85 | - name: Run Tests 86 | run: ctest --test-dir build --output-on-failure 87 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.slo 2 | *.lo 3 | *.o 4 | *.obj 5 | *.gch 6 | *.pch 7 | *.so 8 | *.dylib 9 | *.dll 10 | *.lai 11 | *.la 12 | *.a 13 | *.lib 14 | *.exe 15 | *.out 16 | *.app 17 | *.user 18 | *.vcxproj 19 | *.vcxproj.user 20 | *.vcxproj.filters 21 | *.sln 22 | 23 | cmake_install.cmake 24 | .cache/ 25 | *.TMP 26 | 27 | Testing 28 | Debug/ 29 | build/ 30 | buildClang/ 31 | buildGCC/ 32 | CMakeFiles/ 33 | .vs/ 34 | benchmarks/time.py 35 | benchmarks/*.cpp -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22.0) 2 | project(enchantum LANGUAGES CXX) 3 | 4 | if(POLICY CMP0128) 5 | cmake_policy(SET CMP0128 NEW) 6 | endif() 7 | 8 | set(INCDIR "${CMAKE_CURRENT_SOURCE_DIR}/enchantum/include") 9 | file(GLOB_RECURSE INCS "${INCDIR}/*.hpp") 10 | 11 | add_library(enchantum INTERFACE ${INCS}) 12 | add_library(enchantum::enchantum ALIAS enchantum) 13 | target_compile_features(enchantum INTERFACE cxx_std_20) 14 | 15 | target_include_directories(enchantum INTERFACE 16 | $ 17 | $ 18 | ) 19 | 20 | option(ENCHANTUM_BUILD_TESTS "Enable tests for this `enchantum` library" OFF) 21 | option(ENCHANTUM_BUILD_BENCHMARKS "Enable compile time benchmarks `enchantum` library" OFF) 22 | 23 | if(ENCHANTUM_BUILD_TESTS) 24 | enable_testing() 25 | add_subdirectory(tests) 26 | endif() 27 | 28 | if(ENCHANTUM_BUILD_BENCHMARKS) 29 | add_subdirectory(benchmarks) 30 | endif() 31 | 32 | 33 | install(TARGETS enchantum 34 | EXPORT enchantumTargets 35 | ) 36 | 37 | install(DIRECTORY ${INCDIR}/enchantum/ 38 | DESTINATION include/enchantum 39 | FILES_MATCHING PATTERN "*.hpp" 40 | ) 41 | 42 | install(EXPORT enchantumTargets 43 | NAMESPACE enchantum:: 44 | DESTINATION cmake 45 | ) 46 | 47 | include(CMakePackageConfigHelpers) 48 | 49 | write_basic_package_version_file( 50 | "${CMAKE_CURRENT_BINARY_DIR}/enchantumConfigVersion.cmake" 51 | VERSION 0.1.0 52 | COMPATIBILITY AnyNewerVersion 53 | ) 54 | 55 | configure_package_config_file( 56 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/enchantumConfig.cmake.in" 57 | "${CMAKE_CURRENT_BINARY_DIR}/enchantumConfig.cmake" 58 | INSTALL_DESTINATION cmake 59 | ) 60 | 61 | install(FILES 62 | "${CMAKE_CURRENT_BINARY_DIR}/enchantumConfig.cmake" 63 | "${CMAKE_CURRENT_BINARY_DIR}/enchantumConfigVersion.cmake" 64 | DESTINATION cmake 65 | ) -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "dev", 6 | "displayName": "Developer Configuartion", 7 | "cacheVariables": { 8 | "CMAKE_CXX_STANDARD": "20", 9 | "CMAKE_CXX_STANDARD_REQUIRED": "ON", 10 | "CMAKE_CXX_EXTENSIONS": "OFF", 11 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", 12 | "ENCHANTUM_BUILD_TESTS": "ON", 13 | "ENCHANTUM_BUILD_BENCHMARKS":"ON" 14 | } 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 ZXShady 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Enchantum 2 | 3 | **Enchantum** (short for "enchant enum") is a modern **C++20** header-only library for **compile-time enum reflection**. It provides fast, lightweight access to enum values, names, and bitflags all without macros or boilerplate. 4 | 5 | > Every year, countless turtles perish due to the pollution caused by slow, bloated build times. 6 | Save the turtles — and your compile times — by switching to enchantum! 7 | 8 | Source: I made it up. 9 | 10 | Key Features 11 | 12 | - Macro-free and non-intrusive boilerplate-free reflection 13 | 14 | - Fast compile times (benchmarked below) 15 | 16 | - Supports: 17 | - Scoped enums 18 | - Unscoped C enums 19 | - Sparse enums 20 | - Bitflags 21 | - Iteration over values,names. 22 | - string <=> enums conversions 23 | - enum <=> index conversions 24 | - enum aware containers like `enchantum::bitset` and `enchantum::array` 25 | - Extra features like: 26 | 27 | - Null terminator disabling 28 | 29 | - Prefix stripping for C-style enums 30 | 31 | 32 | [Simple Examples](#simple-examples) 33 | 34 | [Why another enum reflection library?](#why-another-enum-reflection-library) 35 | 36 | [Features](docs/features.md) 37 | 38 | [Limitations](docs/limitations.md) 39 | 40 | [CMake Integration](#cmake-integration) 41 | 42 | Tested locally on Windows 10 with: 43 | - Visual Studio 2022 (v17.13.6) 44 | - GCC 14.2.0 45 | - Clang 20.1.2 46 | 47 | --- 48 | 49 | ## Notes 50 | Please read [Limitations](docs/limitations.md) before use. 51 | 52 | ## Simple Examples 53 | 54 | Enums used in the examples 55 | 56 | * to string 57 | ```cpp 58 | #include // to_string 59 | #include // ostream support 60 | enum class Music { Rock, Jazz , Metal }; 61 | 62 | int main() 63 | { 64 | auto music = Music::Rock; 65 | std::string_view music_name = enchantum::to_string(music); 66 | // music_name == "Rock" 67 | 68 | using namespace enchantum::ostream_operators; 69 | std::cout << music; 70 | // Prints Rock 71 | } 72 | ``` 73 | 74 | * from strings 75 | ```cpp 76 | #include // cast 77 | enum class Music { Rock, Jazz , Metal }; 78 | int main() 79 | { 80 | // case sensitive 81 | std::optional music = enchantum::cast("Jazz"); 82 | if(music.has_value()) // check if cast succeeded 83 | { 84 | // *music == Music::Jazz 85 | } 86 | // pass a predicate taking two string views 87 | music = enchantum::cast("JAZZ",[](std::string_view a,std::string_view b){ 88 | return std::ranges::equal(a,b,[](unsigned char x,unsigned char y){ 89 | return std::tolower(x) == std::tolower(y); 90 | }) 91 | }); 92 | if(music.has_value()) { 93 | // *music == Music::JAZZ 94 | } 95 | } 96 | ``` 97 | 98 | * index into enums 99 | ```cpp 100 | #include // index_to_enum and enum_to_index 101 | enum class Music { Rock, Jazz , Metal }; 102 | 103 | int main() 104 | { 105 | // case sensitive 106 | std::optional music = enchantum::index_to_enum(1); // Jazz is the second enum member 107 | if(music.has_value()) // check if index is not out of bounds 108 | { 109 | // *music == Music::Jazz 110 | std::optional index = enchantum::enum_to_index(*music); 111 | // *index == 1 112 | } 113 | } 114 | ``` 115 | 116 | * iteration 117 | ```cpp 118 | #include // entries,values and names 119 | enum class Music { Rock, Jazz , Metal }; 120 | 121 | int main() 122 | { 123 | // Iterate over values 124 | for(Music music : enchantum::values) 125 | std::cout << static_cast(music) << " "; 126 | // Prints "0 1 2" 127 | 128 | // Iterate over names 129 | for(std::string_view name : enchantum::names) 130 | std::cout << name << " "; 131 | // Prints "Rock Jazz Metal" 132 | 133 | // Iterate over both! 134 | for(const auto& [music,name] : enchantum::entries) 135 | std::cout << name << " = " << static_cast(music) << "\n"; 136 | // Prints 137 | // Rock = 0 138 | // Jazz = 1 139 | // Metal = 2 140 | } 141 | ``` 142 | 143 | Look at [Features](docs/features.md) for more information. 144 | 145 | 146 | ## Why Another Enum Reflection Library? 147 | 148 | There are several enum reflection libraries out there — so why choose **enchantum** instead of [magic_enum](https://github.com/Neargye/magic_enum), [simple_enum](https://github.com/arturbac/simple_enum), or [conjure_enum](https://github.com/fix8mt/conjure_enum)? 149 | 150 | 151 | ### magic_enum 152 | 153 | **Pros** 154 | - Macro-free (non intrusive) 155 | - No modifications needed for existing enums 156 | - Allows specifying ranges for specific enums when needed 157 | - Supports C++17 158 | - Nicer compiler errors 159 | - Supports wide strings. 160 | - Efficient Binary size 161 | **Cons** 162 | - Compile times grow significantly with larger `MAGIC_ENUM_MAX_RANGE`. 163 | - Requires alternate APIs like `magic_enum::enum_name()` to mitigate said compile-time costs. which forces those functions not to be functors. 164 | ### conjure_enum 165 | 166 | **Pros** 167 | - Macro-free (non intrusive) 168 | - Uses C++20 169 | 170 | **Cons** 171 | 172 | *Note: Could not get this to compile locally. Based on the README, compile times are similar to or worse than magic_enum.* 173 | 174 | ### simple_enum 175 | 176 | **Pros** 177 | - Fast compile times but only if range is small 178 | - Functor based api. 179 | 180 | **Cons** 181 | - Requires specifying enum first/last values manually (instrusive does not mix well with third party enums) 182 | - Compile time slows down with large enum ranges 183 | 184 | ### enchantum 185 | 186 | **Pros** 187 | - Macro-free (non intrusive) 188 | - Does not sacrifice API ease of use and features for compile time sake (e.g no `ENCHANTUM_ALL_ENUMS_ARE_CONTIGUOUS_OPTIMIZATION` flag which would disable support for sparse enums) 189 | - Allows specifying ranges for specific enums when needed 190 | - Compiles fast. 191 | - Clean and Simple Functor based API `enchantum::to_string(E)` no `enchantum::to_string()` since compile times are fast. 192 | - Features like disabling null termination if not needed and specifying common enum prefix for C style enums, and reflect '0' values for bit flag enums. 193 | - Supports all sort of enums (scoped,unscoped,C style unfixed underlying type,anonymous namespaced enums, enums with commas in their typename,etc...); 194 | - Efficient Binary size 195 | **Cons** 196 | - C++20 required 197 | - Compiler errors are incomprehensible if something goes wrong, needs a level 10 wizard to read them. 198 | - No support for wide strings (yet) 199 | --- 200 | 201 | ## Compile-Time Benchmarks 202 | 203 | Each benchmark was run 10 times (except MSVC which was ran 3 times) and averaged unless noted otherwise. 204 | `range` is `ENCHANTUM_MAX_RANGE` and `MAGIC_ENUM_RANGE_MAX` 205 | ### Small Enums 206 | *200 enums, 16 values each, range: -128 to 128* 207 | 208 | | Compiler | `magic_enum` (s) | `enchantum` (s) | Time Saved | 209 | | ----------- | ---------------- | --------------- | ---------- | 210 | | MSVC | 80 | 22 | 58 | 211 | | GCC | 47 | 7 | 40 | 212 | | Clang | 47 | 8 | 39 | 213 | 214 | ### Large Enums 215 | *32 enums, 200 values each, range: -256 to 256* 216 | 217 | | Compiler | `magic_enum` (s) | `enchantum` (s) | Time Saved | 218 | | ----------- | ---------------- | --------------- | ---------- | 219 | | MSVC | 37 | 15 | 22 | 220 | | Clang | 18 | 5 | 13 | 221 | | GCC | 21 | 6 | 15 | 222 | 223 | 224 | ### Very Large Enum Range 225 | *200 enums, 16 values each, range: -1024 to 1024* 226 | 227 | *Only ran 2 times due to extreme compilation times.* 228 | 229 | | Compiler | `magic_enum` | `enchantum` | 230 | |----------|------------------|---------------| 231 | | MSVC | >20 min (killed) | ~107 sec | 232 | | GCC | >15 min (killed) | ~37 sec | 233 | | Clang | >15 min (killed) | ~42 sec | 234 | --- 235 | 236 | ## Summary 237 | 238 | **enchantum** significantly reduces compile times in enum reflection projects. In my own project (which uses [libassert](https://github.com/jeremy-rifkin/libassert) and enum reflection for configuration), switching from `magic_enum` reduced full rebuild times from about 2 minutes to 1 minute and 30 seconds. I was surprised that `magic_enum` alone took 30 seconds. 239 | 240 | The trade-off is that `enchantum` requires C++20, while `magic_enum` supports C++17. 241 | But this requirement can be lifted if there is enough demand for a C++17 version of `enchantum`. 242 | 243 | 244 | # CMake Integration 245 | 246 | The **cmake** file provides the target `enchantum::enchantum` since this library is header-only it is very simple to use you can copy and paste the files and add the include directory or use **cmake** and the library as a submodule. 247 | 248 | ```cpp 249 | add_subdirectory("third_party/enchantum") 250 | target_link_libraries(your_executable enchantum::enchantum) 251 | ``` -------------------------------------------------------------------------------- /benchmarks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | include(FetchContent) 4 | FetchContent_Declare(magic_enum 5 | GIT_REPOSITORY https://github.com/Neargye/magic_enum.git 6 | GIT_SHALLOW ON 7 | GIT_TAG master) 8 | FetchContent_MakeAvailable(magic_enum) 9 | 10 | add_executable(benchmarks) 11 | target_include_directories(benchmarks PRIVATE "${magic_enum_SOURCE_DIR}/include") 12 | target_link_libraries(benchmarks enchantum::enchantum Catch2::Catch2 Catch2::Catch2WithMain) 13 | 14 | if(MSVC) 15 | target_compile_options(benchmarks PRIVATE /Za /permissive-) 16 | target_compile_options(benchmarks PRIVATE /W4) 17 | else() 18 | target_compile_options(benchmarks PRIVATE -Wall -Wextra -Wconversion -Wpedantic) 19 | endif() 20 | 21 | 22 | 23 | # Gather source files 24 | file(GLOB_RECURSE SRCS "*.cpp" "*.hpp") 25 | 26 | target_sources(benchmarks PRIVATE 27 | ${SRCS} 28 | ) 29 | -------------------------------------------------------------------------------- /benchmarks/generate_compile_time_tests.py: -------------------------------------------------------------------------------- 1 | start = 0 2 | step_count = 1 3 | end = int(input("Enter enum count: ")) 4 | is_enum_class_input = input("Enum classes? (True/False): ").strip().lower() 5 | is_enum_class = is_enum_class_input == "true" 6 | value_count = int(input("Enter number of enum values per enum (e.g., 15): ")) 7 | base_filename = input("Enter base file name (e.g., big_enums): ").strip() 8 | min_range = int(input("Enter the min range for MAGIC_ENUM_RANGE_MIN and ENCHANTUM_MIN_RANGE: ")) 9 | max_range = int(input("Enter the max range for MAGIC_ENUM_RANGE_MAX and ENCHANTUM_MAX_RANGE: ")) 10 | 11 | # Configuration for each enum reflection library 12 | libs = { 13 | "magic_enum": { 14 | "include_path": "magic_enum/magic_enum.hpp", 15 | "to_string": "magic_enum::enum_name", 16 | "min_macro": "MAGIC_ENUM_RANGE_MIN", 17 | "max_macro": "MAGIC_ENUM_RANGE_MAX" 18 | }, 19 | #"conjure_enum": { 20 | # "include_path": "fix8/conjure_enum.hpp", 21 | # "to_string": "enum_to_string_conjure_wrapper", 22 | # "min_macro": "FIX8_CONJURE_ENUM_MIN_VALUE", 23 | # "max_macro": "FIX8_CONJURE_ENUM_MAX_VALUE" 24 | #}, 25 | "enchantum": { 26 | "include_path": "enchantum/enchantum.hpp", 27 | "to_string": "enchantum::to_string", 28 | "min_macro": "ENCHANTUM_MIN_RANGE", 29 | "max_macro": "ENCHANTUM_MAX_RANGE" 30 | }, 31 | #"simple_enum": { 32 | # "include_path": "simple_enum/simple_enum.hpp", 33 | # "to_string": "simple_enum::enum_name", 34 | # "min_macro": "", 35 | # "max_macro": "" 36 | #} 37 | } 38 | 39 | def create_tests(lib_name: str, lib_config: dict, filename: str): 40 | with open(filename, "w") as f: 41 | if lib_config["min_macro"] and lib_config["max_macro"]: 42 | f.write(f"""\ 43 | #define {lib_config["min_macro"]} ({min_range}) 44 | #define {lib_config["max_macro"]} ({max_range}) 45 | """) 46 | 47 | f.write(f'#include <{lib_config["include_path"]}>\n\n') 48 | if lib_name == "conjure_enum": 49 | f.write(""" 50 | template 51 | constexpr auto enum_to_string_conjure_wrapper(T e) { 52 | return conjure_enum::enum_to_string(e); 53 | } 54 | """) 55 | # Write enum definitions 56 | for i in range(start, end): 57 | enum_name = f"A_{i}" 58 | enum_type = "enum class" if is_enum_class else "enum" 59 | f.write(f"{enum_type} {enum_name} : int {{\n") 60 | values = [f"{enum_name}_{j} = {j}," for j in range(0,value_count,step_count)] 61 | f.write("".join(values)) 62 | f.write(f""" 63 | first = {enum_name}_0, 64 | last = {enum_name}_{list(range(0, value_count, step_count))[-1]} 65 | }}; 66 | """) 67 | 68 | for i in range(start, end): 69 | enum_name = f"A_{i}" 70 | for j in range(0,value_count,step_count): 71 | value_name = f"{enum_name}_{j}" 72 | f.write(f'static_assert({lib_config["to_string"]}({enum_name}::{value_name}) == "{value_name}");\n') 73 | 74 | print(f"File '{filename}' generated for '{lib_name}' with {end - start} enums.") 75 | 76 | # generate benchmarks for each library 77 | for lib_name, lib_config in libs.items(): 78 | create_tests(lib_name, lib_config, f"{lib_name}_{base_filename}.cpp") 79 | 80 | input("Press enter to exit...") 81 | -------------------------------------------------------------------------------- /cmake/enchantumConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/enchantumTargets.cmake") 4 | -------------------------------------------------------------------------------- /docs/limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations 2 | 3 | This library requires C++20. but I can make a C++17 version if really wanted the clang implementation is actually almost C++17 except for some things. 4 | 5 | This library just like other libraries uses compiler-specific hacks based on `__FUNCSIG__` / `__PRETTY_FUNCTION__`. 6 | 7 | ## Enum Range 8 | 9 | Enum values must be in the range [`ENCHANTUM_MIN_RANGE`,`ENCHANTUM_MAX_RANGE`] 10 | By default, `ENCHANTUM_MIN_RANGE` = -256, `ENCHANTUM_MAX_RANGE` = 256. 11 | 12 | If you need a different range for all enum types by default, redefine the macro `ENCHANTUM_MAX_RANGE` and if you don't explicitly define `ENCHANTUM_MIN_RANGE` it will be `-ENCHANTUM_MAX_RANGE`. Increasing this value can lead to longer compilation times but unlike other libraries it is not massivly increasing [see benchmarks](../README.md#compile-time-benchmarks). 13 | 14 | 15 | *Note: Defining the macro values project wide is recommended* 16 | 17 | ```cpp 18 | #define ENCHANTUM_MIN_RANGE 0 // if not defined it will be -512 19 | #define ENCHANTUM_MAX_RANGE 512 20 | #include 21 | ``` 22 | 23 | If you need a different range for a specific enum type, add the specialization `enum_traits` for the enum type. 24 | 25 | ```cpp 26 | #include 27 | 28 | enum class wizards { longbeard = 300, midbeard = 400, redbearded = 600 }; 29 | 30 | template <> 31 | struct enchantum::enum_traits { // defined in enchantum/common.hpp you only need that header 32 | static constexpr auto min = static_cast(wizards::longbeard); 33 | static constexpr auto max = static_cast(wizards::redbearded); 34 | }; 35 | ``` 36 | 37 | ## Other Compiler Issues 38 | 39 | If you see a message that goes like this 40 | 41 | ``` 42 | note: constexpr evaluation hit maximum step limit; possible infinite loop? // gcc or clang 43 | note: maximum constexpr step count exceeded. // msvc 44 | ``` 45 | 46 | Change the limit for the number of constexpr steps allowed: (hyperlink to docs) 47 | 48 | [MSVC](https://docs.microsoft.com/en-us/cpp/build/reference/constexpr-control-constexpr-evaluation): `/constexpr:depthN, /constexpr:stepsN` 49 | 50 | [Clang](https://clang.llvm.org/docs/UsersManual.html#controlling-implementation-limits): `-fconstexpr-depth=N, -fconstexpr-steps=N` 51 | 52 | [GCC](https://gcc.gnu.org/onlinedocs/gcc-14.2.0/gcc/C_002b_002b-Dialect-Options.html#index-fconstexpr-depth): `-fconstexpr-depth=N, -fconstexpr-loop-limit=N, -fconstexpr-ops-limit=N` 53 | 54 | ## Unscoped enums in templates may not work correctly on Сlang. 55 | 56 | Clang is super weird with these enums 57 | 58 | ```cpp 59 | template 60 | struct ReallyClang_QuestionMark { 61 | enum Type { 62 | A,B,C 63 | }; 64 | }; 65 | 66 | enchantum::entries::Type>; // some long compiler error 67 | ``` 68 | 69 | Apparantly Clang optimizes unused enum names in templates? I don't know really but a workaround is this (you must atleast mention the name of an enumarator once) 70 | 71 | ```cpp 72 | template 73 | struct GoodClang { 74 | enum Type_ { 75 | A,B,C 76 | }; 77 | 78 | using Type = decltype(Type_::A); 79 | }; 80 | 81 | enchantum::entries::Type>; // happy clang 82 | ``` 83 | 84 | ## Unscoped Enums 85 | 86 | unnamed unscoped enums are not supported except on msvc 87 | 88 | ```cpp 89 | enum { 90 | SomeConstant = 10; 91 | }; 92 | 93 | using Type = decltype(SomeConstant); 94 | enchantum::entries; // won't work and won't be officially supported 95 | ``` 96 | 97 | this works 98 | 99 | ```cpp 100 | typedef enum { 101 | SomeConstant = 10; 102 | } Type; 103 | ``` 104 | 105 | just give it a name. -------------------------------------------------------------------------------- /enchantum/include/enchantum/algorithms.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "enchantum.hpp" 4 | #include 5 | #include 6 | 7 | namespace enchantum { 8 | 9 | #if 0 10 | namespace details { 11 | 12 | template 13 | constexpr auto cartesian_product() 14 | { 15 | constexpr auto size = []() { 16 | std::size_t x = range; 17 | std::size_t n = sets; 18 | while (--n != 0) 19 | x *= range; 20 | return x; 21 | }(); 22 | 23 | std::array, size> products{}; 24 | std::array counter{}; 25 | 26 | for (auto& product : products) { 27 | product = counter; 28 | 29 | ++counter.back(); 30 | for (std::size_t i = counter.size() - 1; i != 0; i--) { 31 | if (counter[i] != range) 32 | break; 33 | 34 | counter[i] = 0; 35 | ++counter[i - 1]; 36 | } 37 | } 38 | return products; 39 | } 40 | 41 | } // namespace details 42 | #endif 43 | 44 | #if 0 45 | template Func> 46 | constexpr auto visit(Func func, E e) 47 | noexcept(std::is_nothrow_invocable_v) 48 | { 49 | using Ret = decltype(func(e)); 50 | 51 | 52 | return [&](std::index_sequence) { 53 | if ((values[Idx] == enums)) 54 | (func(std::integral_constant[Idx]> {}), ...); 55 | }(std::make_index_sequence>()); 56 | } 57 | template Func> 58 | constexpr auto visit(Func func, Enums... enums) noexcept(std::is_nothrow_invocable_v) 59 | { 60 | using Ret = decltype(func(enums...)); 61 | return [&](std::index_sequence) { 62 | if ((values[Idx] == enums) && ...) 63 | (func(std::integral_constant[Idx]> {}), ...); 64 | }(std::make_index_sequence>()...); 65 | } 66 | #endif 67 | namespace details { 68 | 69 | template 70 | constexpr auto for_each(Func& f, std::index_sequence) 71 | { 72 | (void)(f(std::integral_constant[I]> {}), ...); 73 | } 74 | 75 | } // namespace details 76 | 77 | template 78 | constexpr void for_each(Func f) // intentional not const 79 | { 80 | details::for_each(f, std::make_index_sequence>{}); 81 | } 82 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/all.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "details/optional.hpp" 3 | #include "details/string.hpp" 4 | #include "details/string_view.hpp" 5 | #include "details/generate_arrays.hpp" 6 | #include "algorithms.hpp" 7 | #include "array.hpp" 8 | #include "bitset.hpp" 9 | #include "bitflags.hpp" 10 | #include "bitwise_operators.hpp" 11 | #include "enchantum.hpp" 12 | #include "entries.hpp" 13 | #include "iostream.hpp" 14 | #include "next_value.hpp" 15 | #include "bitset.hpp" 16 | 17 | #if __has_include() 18 | #include "fmt_format.hpp" 19 | #elif __has_include() 20 | #include "std_format.hpp" 21 | #endif -------------------------------------------------------------------------------- /enchantum/include/enchantum/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "enchantum.hpp" 3 | #include 4 | #include 5 | 6 | namespace enchantum { 7 | 8 | template 9 | class array : public std::array> { 10 | private: 11 | using base = std::array>; 12 | public: 13 | using index_type = E; 14 | using typename base::const_iterator; 15 | using typename base::const_pointer; 16 | using typename base::const_reference; 17 | using typename base::const_reverse_iterator; 18 | using typename base::difference_type; 19 | using typename base::iterator; 20 | using typename base::pointer; 21 | using typename base::reference; 22 | using typename base::reverse_iterator; 23 | using typename base::size_type; 24 | using typename base::value_type; 25 | 26 | using base::at; 27 | using base::operator[]; 28 | 29 | [[nodiscard]] constexpr reference at(const E index) 30 | { 31 | if (const auto i = enchantum::enum_to_index(index)) 32 | return operator[](*i); 33 | ENCHANTUM_THROW(std::out_of_range("enchantum::array::at index out of range"), index); 34 | } 35 | 36 | [[nodiscard]] constexpr const_reference at(const E index) const 37 | { 38 | if (const auto i = enchantum::enum_to_index(index)) 39 | return operator[](*i); 40 | ENCHANTUM_THROW(std::out_of_range("enchantum::array::at: index out of range"), index); 41 | } 42 | 43 | [[nodiscard]] constexpr reference operator[](const E index) noexcept 44 | { 45 | return operator[](*enchantum::enum_to_index(index)); 46 | } 47 | 48 | [[nodiscard]] constexpr const_reference operator[](const E index) const noexcept 49 | { 50 | return operator[](*enchantum::enum_to_index(index)); 51 | } 52 | }; 53 | 54 | } // namespace enchantum 55 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/bitflags.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "common.hpp" 3 | #include "details/string.hpp" 4 | #include "details/string_view.hpp" 5 | #include "enchantum.hpp" 6 | 7 | namespace enchantum { 8 | 9 | 10 | template 11 | inline constexpr E value_ors = [] { 12 | using T = std::underlying_type_t; 13 | T ret{}; 14 | for (const auto val : values) 15 | ret |= static_cast(val); 16 | return static_cast(ret); 17 | }(); 18 | 19 | 20 | template 21 | [[nodiscard]] constexpr bool contains_bitflag(const std::underlying_type_t value) noexcept 22 | { 23 | using T = std::underlying_type_t; 24 | if constexpr (is_contiguous_bitflag) { 25 | return value >= static_cast(min) && value <= static_cast(value_ors); 26 | } 27 | else { 28 | if (value == 0) 29 | return has_zero_flag; 30 | T valid_bits = 0; 31 | 32 | for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { 33 | const auto v = static_cast(values[i]); 34 | if ((value & v) == v) 35 | valid_bits |= v; 36 | } 37 | return valid_bits == value; 38 | } 39 | } 40 | 41 | template 42 | [[nodiscard]] constexpr bool contains_bitflag(const E value) noexcept 43 | { 44 | return enchantum::contains_bitflag(static_cast>(value)); 45 | } 46 | 47 | template BinaryPred> 48 | [[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept 49 | { 50 | std::size_t pos = 0; 51 | for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { 52 | if (!enchantum::contains(s.substr(pos, i - pos), binary_pred)) 53 | return false; 54 | pos = i + 1; 55 | } 56 | return enchantum::contains(s.substr(pos), binary_pred); 57 | } 58 | 59 | 60 | template 61 | [[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep = '|') noexcept 62 | { 63 | std::size_t pos = 0; 64 | for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { 65 | if (!enchantum::contains(s.substr(pos, i - pos))) 66 | return false; 67 | pos = i + 1; 68 | } 69 | return enchantum::contains(s.substr(pos)); 70 | } 71 | 72 | 73 | template 74 | [[nodiscard]] constexpr String to_string_bitflag(const E value, const char sep = '|') 75 | { 76 | using T = std::underlying_type_t; 77 | if constexpr (has_zero_flag) 78 | if (static_cast(value) == 0) 79 | return String(names[0]); 80 | 81 | String name; 82 | T check_value = 0; 83 | for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { 84 | const auto& [v, s] = entries[i]; 85 | if (v == (value & v)) { 86 | if (!name.empty()) 87 | name.append(1, sep); // append separator if not the first value 88 | name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always 89 | check_value |= static_cast(v); 90 | } 91 | } 92 | if (check_value == static_cast(value)) 93 | return name; 94 | return String(); 95 | } 96 | 97 | template BinaryPred> 98 | [[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept 99 | { 100 | using T = std::underlying_type_t; 101 | T check_value{}; 102 | std::size_t pos = 0; 103 | for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { 104 | if (const auto v = enchantum::cast(s.substr(pos, i - pos), binary_pred)) 105 | check_value |= static_cast(*v); 106 | else 107 | return optional(); 108 | pos = i + 1; 109 | } 110 | 111 | if (const auto v = enchantum::cast(s.substr(pos), binary_pred)) 112 | return optional(static_cast(check_value | static_cast(*v))); 113 | return optional(); 114 | } 115 | 116 | template 117 | [[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep = '|') noexcept 118 | { 119 | return enchantum::cast_bitflag(s, sep, [](const auto& a, const auto& b) { return a == b; }); 120 | } 121 | 122 | template 123 | [[nodiscard]] constexpr optional cast_bitflag(const std::underlying_type_t value) noexcept 124 | { 125 | return enchantum::contains_bitflag(value) ? optional(static_cast(value)) : optional(); 126 | } 127 | 128 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/bitset.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "enchantum.hpp" 3 | #ifndef ENCHANTUM_ALIAS_BITSET 4 | #include 5 | #endif 6 | #include "bitflags.hpp" 7 | #include 8 | 9 | namespace enchantum { 10 | 11 | namespace details { 12 | #ifndef ENCHANTUM_ALIAS_BITSET 13 | using ::std::bitset; 14 | #else 15 | ENCHANTUM_ALIAS_BITSET; 16 | #endif 17 | } // namespace details 18 | 19 | template 20 | class bitset : public details::bitset> { 21 | private: 22 | using base = details::bitset>; 23 | public: 24 | using typename base::reference; 25 | 26 | using base::operator[]; 27 | using base::flip; 28 | using base::reset; 29 | using base::set; 30 | using base::test; 31 | 32 | using base::base; 33 | using base::operator=; 34 | 35 | [[nodiscard]] constexpr string to_string(const char sep = '|') const 36 | { 37 | string name; 38 | for (std::size_t i = 0; i < enchantum::count; ++i) { 39 | if (test(i)) { 40 | const auto s = enchantum::names[i]; 41 | if (!name.empty()) 42 | name += sep; 43 | name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always 44 | } 45 | } 46 | return name; 47 | } 48 | 49 | [[nodiscard]] constexpr auto to_string(const char zero,const char one) const 50 | { 51 | return base::to_string(zero,one); 52 | } 53 | 54 | constexpr bitset(const std::initializer_list values) noexcept 55 | { 56 | for (auto value : values) { 57 | set(value, true); 58 | } 59 | } 60 | 61 | [[nodiscard]] constexpr reference operator[](const E index) noexcept 62 | { 63 | return operator[](*enchantum::enum_to_index(index)); 64 | } 65 | 66 | [[nodiscard]] constexpr bool operator[](const E index) const noexcept 67 | { 68 | return operator[](*enchantum::enum_to_index(index)); 69 | } 70 | 71 | constexpr bool test(const E pos) 72 | { 73 | 74 | if (const auto i = enchantum::enum_to_index(pos)) 75 | return test(*i); 76 | ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::test(E pos,bool value) out of range exception"), pos); 77 | } 78 | 79 | constexpr bitset& set(const E pos, bool value = true) 80 | { 81 | 82 | if (const auto i = enchantum::enum_to_index(pos)) 83 | return static_cast(set(*i, value)); 84 | ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::set(E pos,bool value) out of range exception"), pos); 85 | } 86 | 87 | 88 | constexpr bitset& reset(const E pos) 89 | { 90 | if (const auto i = enchantum::enum_to_index(pos)) 91 | return static_cast(reset(*i)); 92 | ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::reset(E pos) out of range exception"), pos); 93 | } 94 | 95 | constexpr bitset& flip(const E pos) 96 | { 97 | if (const auto i = enchantum::enum_to_index(pos)) 98 | return static_cast(flip(*i)); 99 | ENCHANTUM_THROW(std::out_of_range("enchantum::bitset::flip(E pos) out of range exception"), pos); 100 | } 101 | }; 102 | 103 | } // namespace enchantum 104 | 105 | 106 | template 107 | struct std::hash> : std::hash>> { 108 | using std::hash>>::operator(); 109 | }; 110 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/bitwise_operators.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include 5 | /* 6 | Note this header is an extremely easy way to cause ODR issues. 7 | 8 | class Flags { F1 = 1 << 0,F2 = 1<< 1}; 9 | // **note I did not define any operators** 10 | 11 | enchantum::contains(Flags::F1); // considered a classical `Enum` concept 12 | 13 | using namespace enchantum::bitwise_operators; 14 | 15 | enchantum::contains(Flags::F1); // considered `BitFlagEnum` concept woops! ODR! 16 | 17 | */ 18 | 19 | namespace enchantum::bitwise_operators { 20 | template 21 | [[nodiscard]] constexpr E operator~(E e) noexcept 22 | { 23 | return static_cast(~static_cast>(e)); 24 | } 25 | 26 | template 27 | [[nodiscard]] constexpr E operator|(E a, E b) noexcept 28 | { 29 | using T = std::underlying_type_t; 30 | return static_cast(static_cast(a) | static_cast(b)); 31 | } 32 | 33 | template 34 | [[nodiscard]] constexpr E operator&(E a, E b) noexcept 35 | { 36 | using T = std::underlying_type_t; 37 | return static_cast(static_cast(a) & static_cast(b)); 38 | } 39 | 40 | template 41 | [[nodiscard]] constexpr E operator^(E a, E b) noexcept 42 | { 43 | using T = std::underlying_type_t; 44 | return static_cast(static_cast(a) ^ static_cast(b)); 45 | } 46 | 47 | template 48 | constexpr E& operator|=(E& a, E b) noexcept 49 | { 50 | using T = std::underlying_type_t; 51 | return a = static_cast(static_cast(a) | static_cast(b)); 52 | } 53 | 54 | template 55 | constexpr E& operator&=(E& a, E b) noexcept 56 | { 57 | using T = std::underlying_type_t; 58 | return a = static_cast(static_cast(a) & static_cast(b)); 59 | } 60 | 61 | template 62 | constexpr E& operator^=(E& a, E b) noexcept 63 | { 64 | using T = std::underlying_type_t; 65 | return a = static_cast(static_cast(a) ^ static_cast(b)); 66 | } 67 | 68 | } // namespace enchantum::bitwise_operators 69 | 70 | #define ENCHANTUM_DEFINE_BITWISE_FOR(Enum) \ 71 | [[nodiscard]] constexpr Enum operator&(Enum a, Enum b) noexcept \ 72 | { \ 73 | using T = std::underlying_type_t; \ 74 | return static_cast(static_cast(a) & static_cast(b)); \ 75 | } \ 76 | [[nodiscard]] constexpr Enum operator|(Enum a, Enum b) noexcept \ 77 | { \ 78 | using T = std::underlying_type_t; \ 79 | return static_cast(static_cast(a) | static_cast(b)); \ 80 | } \ 81 | [[nodiscard]] constexpr Enum operator^(Enum a, Enum b) noexcept \ 82 | { \ 83 | using T = std::underlying_type_t; \ 84 | return static_cast(static_cast(a) ^ static_cast(b)); \ 85 | } \ 86 | constexpr Enum& operator&=(Enum& a, Enum b) noexcept { return a = a & b; } \ 87 | constexpr Enum& operator|=(Enum& a, Enum b) noexcept { return a = a | b; } \ 88 | constexpr Enum& operator^=(Enum& a, Enum b) noexcept { return a = a ^ b; } \ 89 | [[nodiscard]] constexpr Enum operator~(Enum a) noexcept \ 90 | { \ 91 | return static_cast(~static_cast>(a)); \ 92 | } 93 | 94 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #ifndef ENCHANTUM_ASSERT 8 | #include 9 | // clang-format off 10 | #define ENCHANTUM_ASSERT(cond, msg, ...) assert(cond && msg) 11 | // clang-format on 12 | #endif 13 | 14 | #ifndef ENCHANTUM_THROW 15 | // additional info such as local variables are here 16 | #define ENCHANTUM_THROW(exception, ...) throw exception 17 | #endif 18 | 19 | #ifndef ENCHANTUM_MAX_RANGE 20 | #define ENCHANTUM_MAX_RANGE 256 21 | #endif 22 | #ifndef ENCHANTUM_MIN_RANGE 23 | #define ENCHANTUM_MIN_RANGE (-ENCHANTUM_MAX_RANGE) 24 | #endif 25 | 26 | namespace enchantum { 27 | 28 | template 29 | concept Enum = std::is_enum_v; 30 | 31 | template 32 | concept SignedEnum = Enum && std::signed_integral>; 33 | 34 | template 35 | concept UnsignedEnum = Enum && !SignedEnum; 36 | 37 | template 38 | concept ScopedEnum = Enum && (!std::is_convertible_v>); 39 | 40 | template 41 | concept UnscopedEnum = Enum && !ScopedEnum; 42 | 43 | template 44 | concept EnumOfUnderlying = Enum && std::same_as, Underlying>; 45 | 46 | template 47 | inline constexpr bool is_bitflag = requires(E e) { 48 | requires std::same_as || std::same_as; 49 | { ~e } -> std::same_as; 50 | { e | e } -> std::same_as; 51 | { e &= e } -> std::same_as; 52 | { e |= e } -> std::same_as; 53 | }; 54 | 55 | template 56 | concept BitFlagEnum = Enum && is_bitflag; 57 | 58 | template 59 | concept EnumFixedUnderlying = Enum && requires { T{0}; }; 60 | 61 | template 62 | struct enum_traits; 63 | 64 | template 65 | struct enum_traits { 66 | private: 67 | using U = std::underlying_type_t; 68 | using L = std::numeric_limits; 69 | public: 70 | static constexpr std::size_t prefix_length = 0; 71 | 72 | static constexpr auto min = (L::min)() > ENCHANTUM_MIN_RANGE ? (L::min)() : ENCHANTUM_MIN_RANGE; 73 | static constexpr auto max = (L::max)() < ENCHANTUM_MAX_RANGE ? (L::max)() : ENCHANTUM_MAX_RANGE; 74 | }; 75 | 76 | template 77 | struct enum_traits { 78 | private: 79 | using T = std::underlying_type_t; 80 | using L = std::numeric_limits; 81 | public: 82 | static constexpr std::size_t prefix_length = 0; 83 | 84 | static constexpr auto min = []() { 85 | if constexpr (std::is_same_v) 86 | return false; 87 | else 88 | return (ENCHANTUM_MIN_RANGE) < 0 ? 0 : (ENCHANTUM_MIN_RANGE); 89 | }(); 90 | static constexpr auto max = []() { 91 | if constexpr (std::is_same_v) 92 | return true; 93 | else 94 | return (L::max)() < (ENCHANTUM_MAX_RANGE) ? (L::max)() : (ENCHANTUM_MAX_RANGE); 95 | }(); 96 | }; 97 | 98 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/enchantum_clang.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #if __clang_major__ < 20 3 | #pragma clang diagnostic push 4 | #pragma clang diagnostic ignored "-Wenum-constexpr-conversion" 5 | #endif 6 | 7 | #include "../common.hpp" 8 | #include "generate_arrays.hpp" 9 | #include "string_view.hpp" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace enchantum { 17 | namespace details { 18 | 19 | #if __clang_major__ >= 20 20 | 21 | template 22 | inline constexpr bool is_valid_cast = false; 23 | 24 | template 25 | inline constexpr bool is_valid_cast(V)>>> = true; 26 | 27 | template max_range = 1> 28 | constexpr auto valid_cast_range() 29 | { 30 | if constexpr (max_range >= 0) { 31 | if constexpr (max_range <= ENCHANTUM_MAX_RANGE) { 32 | // this tests whether `static_cast`ing max_range is valid 33 | // because C style enums stupidly is like a bit field 34 | // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` 35 | // which means giving E.val a larger than 2 bit value is UB so is it for enums 36 | // and gcc and msvc ignore this (for good) 37 | // while clang makes it a subsituation failure which we can check for 38 | // using std::inegral_constant makes sure this is a constant expression situation 39 | // for SFINAE to occur 40 | if constexpr (is_valid_cast) 41 | return valid_cast_range(); 42 | else 43 | return max_range - 1; 44 | } 45 | else { 46 | return max_range - 1; 47 | } 48 | } 49 | else { 50 | if constexpr (max_range >= ENCHANTUM_MIN_RANGE) { 51 | // this tests whether `static_cast`ing max_range is valid 52 | // because C style enums stupidly is like a bit field 53 | // `enum E { a,b,c,d = 3};` is like a bitfield `struct E { int val : 2;}` 54 | // which means giving E.val a larger than 2 bit value is UB so is it for enums 55 | // and gcc and msvc ignore this (for good) 56 | // while clang makes it a subsituation failure which we can check for 57 | // using std::inegral_constant makes sure this is a constant expression situation 58 | // for SFINAE to occur 59 | if constexpr (is_valid_cast) 60 | return valid_cast_range(); 61 | else 62 | return max_range / 2; 63 | } 64 | else { 65 | return max_range / 2; 66 | } 67 | } 68 | } 69 | #else 70 | template max_range = 1> 71 | constexpr auto valid_cast_range() 72 | { 73 | if constexpr (max_range >= 0) 74 | return ENCHANTUM_MAX_RANGE; 75 | else 76 | return ENCHANTUM_MIN_RANGE; 77 | } 78 | #endif 79 | 80 | } // namespace details 81 | 82 | template 83 | requires SignedEnum && (!EnumFixedUnderlying) 84 | struct enum_traits { 85 | static constexpr auto max = details::valid_cast_range(); 86 | static constexpr decltype(max) min = details::valid_cast_range(); 87 | }; 88 | 89 | template 90 | requires UnsignedEnum && (!EnumFixedUnderlying) 91 | struct enum_traits { 92 | static constexpr auto max = details::valid_cast_range(); 93 | static constexpr decltype(max) min = 0; 94 | }; 95 | 96 | 97 | namespace details { 98 | #define SZC(x) (sizeof(x) - 1) 99 | template 100 | constexpr auto enum_in_array_name() noexcept 101 | { 102 | // constexpr auto f() [with auto _ = ( 103 | //constexpr auto f() [Enum = (Scoped)0] 104 | auto s = string_view(__PRETTY_FUNCTION__ + SZC("auto enchantum::details::enum_in_array_name() [Enum = "), 105 | SZC(__PRETTY_FUNCTION__) - SZC("auto enchantum::details::enum_in_array_name() [Enum = ]")); 106 | 107 | if constexpr (ScopedEnum) { 108 | if (s[s.size() - 2] == ')') { 109 | s.remove_prefix(SZC("(")); 110 | s.remove_suffix(SZC(")0")); 111 | return s; 112 | } 113 | else { 114 | return s.substr(0, s.rfind("::")); 115 | } 116 | } 117 | else { 118 | if (s[s.size() - 2] == ')') { 119 | s.remove_prefix(SZC("(")); 120 | s.remove_suffix(SZC(")0")); 121 | } 122 | if (const auto pos = s.rfind("::"); pos != s.npos) 123 | return s.substr(0, pos); 124 | return string_view(); 125 | } 126 | } 127 | 128 | template 129 | constexpr auto var_name() noexcept 130 | { 131 | // "auto enchantum::details::var_name() [Vs = <(A)0, a, b, c, e, d, (A)6>]" 132 | constexpr auto funcsig_off = SZC("auto enchantum::details::var_name() [Vs = <"); 133 | return string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC(">]")); 134 | } 135 | 136 | 137 | template 138 | inline constexpr auto static_storage_for = Copy; 139 | 140 | template 141 | constexpr auto reflect() noexcept 142 | { 143 | constexpr auto Min = enum_traits::min; 144 | constexpr auto Max = enum_traits::max; 145 | 146 | constexpr auto elements = []() { 147 | constexpr auto Array = details::generate_arrays(); 148 | auto str = [Array](std::index_sequence) { 149 | return details::var_name(); 150 | }(std::make_index_sequence()); 151 | 152 | struct RetVal { 153 | std::array pairs{}; 154 | std::size_t total_string_length = 0; 155 | std::size_t valid_count = 0; 156 | } ret; 157 | 158 | std::size_t index = 0; 159 | constexpr auto enum_in_array_name = details::enum_in_array_name(); 160 | constexpr auto enum_in_array_len = enum_in_array_name.size(); 161 | 162 | // ((anonymous namespace)::A)0 163 | // (anonymous namespace)::a 164 | 165 | // this is needed to determine whether the above are cast expression if 2 braces are 166 | // next to eachother then it is a cast but only for anonymoused namespaced enums 167 | constexpr std::size_t index_check = !enum_in_array_name.empty() && enum_in_array_name.front() == '(' ? 1 : 0; 168 | while (index < Array.size()) { 169 | if (str[index_check] == '(') { 170 | str.remove_prefix(SZC("(") + enum_in_array_len + SZC(")0")); // there is atleast 1 base 10 digit 171 | //if(!str.empty()) 172 | // std::cout << "after str \"" << str << '"' << '\n'; 173 | if (const auto commapos = str.find(','); commapos != str.npos) 174 | str.remove_prefix(commapos + 2); 175 | //std::cout << "strsize \"" << str.size() << '"' << '\n'; 176 | } 177 | else { 178 | if constexpr (enum_in_array_len != 0) { 179 | str.remove_prefix(enum_in_array_len + SZC("::")); 180 | } 181 | if constexpr (details::prefix_length_or_zero != 0) { 182 | str.remove_prefix(details::prefix_length_or_zero); 183 | } 184 | const auto commapos = str.find(','); 185 | 186 | const auto name = str.substr(0, commapos); 187 | 188 | ret.pairs[ret.valid_count] = Pair{Array[index], name}; 189 | ret.total_string_length += name.size() + ShouldNullTerminate; 190 | 191 | if (commapos != str.npos) 192 | str.remove_prefix(commapos + 2); // skip comma and space 193 | ++ret.valid_count; 194 | } 195 | ++index; 196 | } 197 | return ret; 198 | }(); 199 | 200 | constexpr auto strings = [elements]() { 201 | std::array strings; 202 | for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { 203 | const auto& [_, s] = elements.pairs[_i]; 204 | for (std::size_t i = 0; i < s.size(); ++i) 205 | strings[index++] = s[i]; 206 | 207 | if constexpr (ShouldNullTerminate) 208 | strings[index++] = '\0'; 209 | } 210 | return strings; 211 | }(); 212 | 213 | std::array ret; 214 | constexpr const auto* str = static_storage_for.data(); 215 | for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { 216 | const auto& [e, s] = elements.pairs[i]; 217 | auto& [re, rs] = ret[i]; 218 | re = e; 219 | 220 | rs = {str + string_index, str + string_index + s.size()}; 221 | string_index += s.size() + ShouldNullTerminate; 222 | } 223 | return ret; 224 | } 225 | 226 | } // namespace details 227 | 228 | 229 | //template 230 | //constexpr std::size_t enum_count = details::enum_count; 231 | 232 | 233 | } // namespace enchantum 234 | 235 | #if __clang_major__ < 20 236 | #pragma clang diagnostic pop 237 | #endif 238 | #undef SZC 239 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/enchantum_gcc.hpp: -------------------------------------------------------------------------------- 1 | #include "../common.hpp" 2 | #include "../type_name.hpp" 3 | #include "generate_arrays.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "string_view.hpp" 11 | 12 | namespace enchantum { 13 | namespace details { 14 | #define SZC(x) (sizeof(x) - 1) 15 | template 16 | constexpr auto enum_in_array_name() noexcept 17 | { 18 | // constexpr auto f() [with auto _ = ( 19 | //constexpr auto f() [with auto _ = (Scoped)0] 20 | auto s = string_view(__PRETTY_FUNCTION__ + 21 | SZC("constexpr auto enchantum::details::enum_in_array_name() [with auto Enum = "), 22 | SZC(__PRETTY_FUNCTION__) - 23 | SZC("constexpr auto enchantum::details::enum_in_array_name() [with auto Enum = ]")); 24 | 25 | if constexpr (ScopedEnum) { 26 | if (s.front() == '(') { 27 | s.remove_prefix(SZC("(")); 28 | s.remove_suffix(SZC(")0")); 29 | return s; 30 | } 31 | else { 32 | return s.substr(0, s.rfind("::")); 33 | } 34 | } 35 | else { 36 | if (s.front() == '(') { 37 | s.remove_prefix(SZC("(")); 38 | s.remove_suffix(SZC(")0")); 39 | } 40 | if (const auto pos = s.rfind("::"); pos != s.npos) 41 | return s.substr(0, pos); 42 | return string_view(); 43 | } 44 | } 45 | 46 | template 47 | constexpr auto length_of_enum_in_template_array_if_casting() noexcept 48 | { 49 | if constexpr (ScopedEnum) { 50 | return details::enum_in_array_name().size(); 51 | } 52 | else { 53 | constexpr auto s = enum_in_array_name().size(); 54 | constexpr auto& tyname = type_name; 55 | if (constexpr auto pos = tyname.rfind("::"); pos != tyname.npos) { 56 | return s + tyname.substr(pos).size(); 57 | } 58 | else { 59 | return s + tyname.size(); 60 | } 61 | } 62 | } 63 | 64 | template 65 | constexpr auto var_name() noexcept 66 | { 67 | //constexpr auto f() [with auto _ = std::array{std::__array_traits::_Type{a, b, c, e, d, (E)6}}] 68 | constexpr std::size_t funcsig_off = SZC("constexpr auto enchantum::details::var_name() [with auto ...Vs = {"); 69 | return std::string_view(__PRETTY_FUNCTION__ + funcsig_off, SZC(__PRETTY_FUNCTION__) - funcsig_off - SZC("}]")); 70 | } 71 | 72 | 73 | template 74 | inline constexpr auto static_storage_for = Copy; 75 | 76 | template 77 | constexpr auto reflect() noexcept 78 | { 79 | constexpr auto Min = enum_traits::min; 80 | constexpr auto Max = enum_traits::max; 81 | 82 | constexpr auto elements = []() { 83 | constexpr auto length_of_enum_in_template_array_casting = details::length_of_enum_in_template_array_if_casting(); 84 | constexpr auto Array = details::generate_arrays(); 85 | auto str = [Array](std::index_sequence) { 86 | return details::var_name(); 87 | }(std::make_index_sequence()); 88 | struct RetVal { 89 | std::array pairs{}; 90 | std::size_t total_string_length = 0; 91 | std::size_t valid_count = 0; 92 | } ret; 93 | std::size_t index = 0; 94 | constexpr auto enum_in_array_len = enum_in_array_name().size(); 95 | while (index < Array.size()) { 96 | if (str.front() == '(') { 97 | str.remove_prefix(SZC("(") + length_of_enum_in_template_array_casting + SZC(")0")); // there is atleast 1 base 10 digit 98 | //if(!str.empty()) 99 | // std::cout << "after str \"" << str << '"' << '\n'; 100 | 101 | if (const auto commapos = str.find(','); commapos != str.npos) 102 | str.remove_prefix(commapos + 2); 103 | 104 | //std::cout << "strsize \"" << str.size() << '"' << '\n'; 105 | } 106 | else { 107 | if constexpr (enum_in_array_len != 0) 108 | str.remove_prefix(enum_in_array_len + SZC("::")); 109 | if constexpr (details::prefix_length_or_zero != 0) { 110 | str.remove_prefix(details::prefix_length_or_zero); 111 | } 112 | const auto commapos = str.find(','); 113 | 114 | const auto name = str.substr(0, commapos); 115 | 116 | ret.pairs[ret.valid_count] = Pair{Array[index], name}; 117 | ret.total_string_length += name.size() + ShouldNullTerminate; 118 | 119 | if (commapos != str.npos) 120 | str.remove_prefix(commapos + 2); 121 | ++ret.valid_count; 122 | } 123 | ++index; 124 | } 125 | return ret; 126 | }(); 127 | 128 | constexpr auto strings = [elements]() { 129 | std::array ret; 130 | for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { 131 | const auto& [_, s] = elements.pairs[_i]; 132 | for (std::size_t i = 0; i < s.size(); ++i) 133 | ret[index++] = s[i]; 134 | 135 | if constexpr (ShouldNullTerminate) 136 | ret[index++] = '\0'; 137 | } 138 | return ret; 139 | }(); 140 | 141 | std::array ret; 142 | constexpr const auto* str = static_storage_for.data(); 143 | for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { 144 | const auto& [e, s] = elements.pairs[i]; 145 | auto& [re, rs] = ret[i]; 146 | re = e; 147 | 148 | rs = {str + string_index, str + string_index + s.size()}; 149 | string_index += s.size() + ShouldNullTerminate; 150 | } 151 | return ret; 152 | } 153 | 154 | } // namespace details 155 | 156 | } // namespace enchantum 157 | 158 | #undef SZC -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/enchantum_msvc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../common.hpp" 3 | #include "../type_name.hpp" 4 | #include "generate_arrays.hpp" 5 | #include "string_view.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace enchantum { 13 | 14 | #define SZC(x) (sizeof(x) - 1) 15 | namespace details { 16 | template 17 | constexpr auto enum_in_array_name() noexcept 18 | { 19 | string_view s = __FUNCSIG__ + SZC("auto __cdecl enchantum::details::enum_in_array_name<"); 20 | s.remove_suffix(SZC(">(void) noexcept")); 21 | 22 | if constexpr (ScopedEnum) { 23 | if (s.front() == '(') { 24 | s.remove_prefix(SZC("(enum ")); 25 | s.remove_suffix(SZC(")0x0")); 26 | return s; 27 | } 28 | return s.substr(0, s.rfind("::")); 29 | } 30 | else { 31 | if (s.front() == '(') { 32 | s.remove_prefix(SZC("(enum ")); 33 | s.remove_suffix(SZC(")0x0")); 34 | } 35 | if (const auto pos = s.rfind("::"); pos != s.npos) 36 | return s.substr(0, pos); 37 | return string_view(); 38 | } 39 | } 40 | 41 | template 42 | constexpr auto var_name() noexcept 43 | { 44 | //auto __cdecl f{enum `anonymous-namespace'::UnscopedAnon 45 | 46 | using T = typename decltype(Array)::value_type; 47 | std::size_t funcsig_off = SZC("auto __cdecl enchantum::details::var_name.size(); 49 | funcsig_off += type_name_len + SZC(","); 50 | constexpr auto Size = Array.size(); 51 | // clang-format off 52 | funcsig_off += Size < 10 ? 1 53 | : Size < 100 ? 2 54 | : Size < 1000 ? 3 55 | : Size < 10000 ? 4 56 | : Size < 100000 ? 5 57 | : Size < 1000000 ? 6 58 | : Size < 10000000 ? 7 59 | : Size < 100000000 ? 8 60 | : Size < 1000000000 ? 9 61 | : 10; 62 | // clang-format on 63 | funcsig_off += SZC(">{enum ") + type_name_len; 64 | return string_view(__FUNCSIG__ + funcsig_off, SZC(__FUNCSIG__) - funcsig_off - SZC("}>(void) noexcept")); 65 | } 66 | 67 | template 68 | inline constexpr auto static_storage_for = Copy; 69 | 70 | template 71 | constexpr auto get_elements() 72 | { 73 | constexpr auto type_name_len = type_name.size(); 74 | 75 | auto str = var_name(); 76 | struct RetVal { 77 | std::array pairs{}; 78 | std::size_t total_string_length = 0; 79 | std::size_t valid_count = 0; 80 | } ret; 81 | std::size_t index = 0; 82 | constexpr auto enum_in_array_len = details::enum_in_array_name().size(); 83 | while (index < Array.size()) { 84 | if (str.front() == '(') { 85 | str.remove_prefix(SZC("(enum ") + type_name_len + SZC(")0x0")); // there is atleast 1 base 16 hex digit 86 | 87 | if (const auto commapos = str.find(','); commapos != str.npos) 88 | str.remove_prefix(commapos + 1); 89 | } 90 | else { 91 | if constexpr (enum_in_array_len != 0) 92 | str.remove_prefix(enum_in_array_len + SZC("::")); 93 | 94 | if constexpr (details::prefix_length_or_zero != 0) 95 | str.remove_prefix(details::prefix_length_or_zero); 96 | 97 | const auto commapos = str.find(','); 98 | 99 | const auto name = str.substr(0, commapos); 100 | 101 | ret.pairs[ret.valid_count] = Pair{Array[index], name}; 102 | ret.total_string_length += name.size() + ShouldNullTerminate; 103 | 104 | if (commapos != str.npos) 105 | str.remove_prefix(commapos + 1); 106 | ++ret.valid_count; 107 | } 108 | ++index; 109 | } 110 | return ret; 111 | } 112 | 113 | template 114 | constexpr auto reflect() noexcept 115 | { 116 | constexpr auto Min = enum_traits::min; 117 | constexpr auto Max = enum_traits::max; 118 | 119 | constexpr auto elements = details::get_elements(), ShouldNullTerminate>(); 120 | 121 | constexpr auto strings = [elements]() { 122 | std::array strings; 123 | for (std::size_t _i = 0, index = 0; _i < elements.valid_count; ++_i) { 124 | const auto& [_, s] = elements.pairs[_i]; 125 | for (std::size_t i = 0; i < s.size(); ++i) 126 | strings[index++] = s[i]; 127 | 128 | if constexpr (ShouldNullTerminate) 129 | strings[index++] = '\0'; 130 | } 131 | return strings; 132 | }(); 133 | 134 | std::array ret; 135 | constexpr const auto* str = static_storage_for.data(); 136 | for (std::size_t i = 0, string_index = 0; i < elements.valid_count; ++i) { 137 | const auto& [e, s] = elements.pairs[i]; 138 | auto& [re, rs] = ret[i]; 139 | re = e; 140 | 141 | rs = {str + string_index, str + string_index + s.size()}; 142 | string_index += s.size() + ShouldNullTerminate; 143 | } 144 | 145 | return ret; 146 | } 147 | } // namespace details 148 | 149 | 150 | } // namespace enchantum 151 | 152 | #undef SZC -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/format_util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../enchantum.hpp" 4 | #include 5 | 6 | namespace enchantum { 7 | namespace details { 8 | template 9 | std::string format(E e) noexcept 10 | { 11 | if constexpr (is_bitflag) { 12 | if (const auto name = enchantum::to_string_bitflag(e); !name.empty()) 13 | return std::string(name.data(), name.size()); 14 | } 15 | else { 16 | if (const auto name = enchantum::to_string(e); !name.empty()) 17 | return std::string(name.data(), name.size()); 18 | } 19 | return std::to_string(+enchantum::to_underlying(e)); // promote using + to select int overload if to underlying returns char 20 | } 21 | } // namespace details 22 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/generate_arrays.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../common.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace enchantum::details { 9 | 10 | template 11 | inline constexpr std::size_t prefix_length_or_zero = 0; 12 | 13 | template 14 | inline constexpr auto prefix_length_or_zero::prefix_length)> = std::size_t{ 15 | enum_traits::prefix_length}; 16 | 17 | template 18 | constexpr auto generate_arrays() 19 | { 20 | #if defined __clang__ && __clang_major__ >= 20 21 | if constexpr (BitFlagEnum) { 22 | if constexpr (EnumFixedUnderlying) { 23 | constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; 24 | std::array ret{}; // 0 value reflected 25 | for (std::size_t i = 0; i < bits; ++i) 26 | ret[i + 1] = static_cast(static_cast>>(1) << i); 27 | return ret; 28 | } 29 | else { 30 | constexpr auto bits = 31 | []() { 32 | auto copy = (Max > Min ? Max - Min : Min - Max); // handle negative; 33 | std::size_t count = 0; 34 | do 35 | ++count; 36 | while (copy >>= 1); 37 | return count; 38 | }(); 39 | std::array b{}; // 0 value reflected 40 | for (std::size_t i = 0; i < bits; ++i) 41 | b[i + 1] = static_cast(static_cast>>(1) << i); 42 | return b; 43 | } 44 | } 45 | #else 46 | if constexpr (BitFlagEnum) { 47 | constexpr std::size_t bits = sizeof(Enum) * CHAR_BIT; 48 | std::array ret{}; // 0 value reflected 49 | for (std::size_t i = 0; i < bits; ++i) 50 | ret[i + 1] = static_cast(static_cast>>(1) << i); 51 | return ret; 52 | } 53 | #endif 54 | else { 55 | static_assert(Min < Max, "enum_traits::min must be less than enum_traits::max"); 56 | std::array array; 57 | auto* const array_data = array.data(); 58 | for (std::size_t i = 0, size = array.size(); i < size; ++i) 59 | array_data[i] = static_cast(static_cast(i) + Min); 60 | return array; 61 | } 62 | } 63 | } // namespace enchantum::details -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/optional.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef ENCHANTUM_CONFIG_FILE 4 | #include ENCHANTUM_CONFIG_FILE 5 | #endif 6 | 7 | #ifndef ENCHANTUM_ALIAS_OPTIONAL 8 | #include 9 | #endif 10 | 11 | 12 | namespace enchantum { 13 | #ifdef ENCHANTUM_ALIAS_OPTIONAL 14 | ENCHANTUM_ALIAS_OPTIONAL; 15 | #else 16 | using ::std::optional; 17 | #endif 18 | 19 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/string.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #ifdef ENCHANTUM_CONFIG_FILE 5 | #include ENCHANTUM_CONFIG_FILE 6 | #endif 7 | 8 | #ifndef ENCHANTUM_ALIAS_STRING 9 | #include 10 | #endif 11 | 12 | 13 | namespace enchantum { 14 | #ifdef ENCHANTUM_ALIAS_STRING 15 | ENCHANTUM_ALIAS_STRING; 16 | #else 17 | using ::std::string; 18 | #endif 19 | 20 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/details/string_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #ifdef ENCHANTUM_CONFIG_FILE 5 | #include ENCHANTUM_CONFIG_FILE 6 | #endif 7 | 8 | #ifndef ENCHANTUM_ALIAS_STRING_VIEW 9 | #include 10 | #endif 11 | 12 | 13 | namespace enchantum { 14 | #ifdef ENCHANTUM_ALIAS_STRING_VIEW 15 | ENCHANTUM_ALIAS_STRING_VIEW; 16 | #else 17 | using ::std::string_view; 18 | #endif 19 | 20 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/enchantum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include "details/optional.hpp" 5 | #include "details/string_view.hpp" 6 | #include "entries.hpp" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace enchantum { 12 | 13 | namespace details { 14 | constexpr std::pair minmax_string_size(const string_view* begin, const string_view* const end) 15 | { 16 | using T = std::size_t; 17 | auto minmax = std::pair(std::numeric_limits::max(), 0); 18 | 19 | for (; begin != end; ++begin) { 20 | const auto size = begin->size(); 21 | minmax.first = minmax.first < size ? minmax.first : size; 22 | minmax.second = minmax.second > size ? minmax.second : size; 23 | } 24 | return minmax; 25 | } 26 | 27 | } // namespace details 28 | 29 | template 30 | inline constexpr bool has_zero_flag = false; 31 | 32 | template 33 | inline constexpr bool has_zero_flag = []() { 34 | for (const auto v : values) 35 | if (static_cast>(v) == 0) 36 | return true; 37 | return false; 38 | }(); 39 | 40 | 41 | template 42 | inline constexpr bool is_contiguous = false; 43 | 44 | template 45 | inline constexpr bool is_contiguous = static_cast(to_underlying(max) - to_underlying(min)) + 1 == 46 | count; 47 | 48 | 49 | template 50 | concept ContiguousEnum = Enum && is_contiguous; 51 | 52 | template 53 | inline constexpr bool is_contiguous_bitflag = false; 54 | 55 | template 56 | inline constexpr bool is_contiguous_bitflag = []() { 57 | constexpr auto& enums = entries; 58 | using T = std::underlying_type_t; 59 | for (auto i = std::size_t{has_zero_flag}; i < enums.size() - 1; ++i) 60 | if (T(enums[i].first) << 1 != T(enums[i + 1].first)) 61 | return false; 62 | return true; 63 | }(); 64 | 65 | template 66 | concept ContiguousBitFlagEnum = BitFlagEnum && is_contiguous_bitflag; 67 | 68 | 69 | template 70 | [[nodiscard]] constexpr bool contains(const std::underlying_type_t value) noexcept 71 | { 72 | using T = std::underlying_type_t; 73 | if (value < T(min) || value > T(max)) 74 | return false; 75 | 76 | if constexpr (is_bitflag) { 77 | if constexpr (has_zero_flag) 78 | if (value == 0) 79 | return true; 80 | return 1 == std::popcount(static_cast>(value)); 81 | } 82 | else { 83 | for (const auto v : values) 84 | if (static_cast(v) == value) 85 | return true; 86 | return is_contiguous; 87 | } 88 | } 89 | 90 | template 91 | [[nodiscard]] constexpr bool contains(const E value) noexcept 92 | { 93 | return enchantum::contains(static_cast>(value)); 94 | } 95 | 96 | template 97 | [[nodiscard]] constexpr bool contains(const string_view name) noexcept 98 | { 99 | constexpr auto minmax = details::minmax_string_size(names.data(), names.data() + names.size()); 100 | if (const auto size = name.size(); size < minmax.first || size > minmax.second) 101 | return false; 102 | 103 | for (const auto& s : names) 104 | if (s == name) 105 | return true; 106 | return false; 107 | } 108 | 109 | 110 | template BinaryPredicate> 111 | [[nodiscard]] constexpr bool contains(const string_view name, const BinaryPredicate binary_predicate) noexcept 112 | { 113 | for (const auto& s : names) 114 | if (binary_predicate(name, s)) 115 | return true; 116 | return false; 117 | } 118 | 119 | 120 | namespace details { 121 | template 122 | struct index_to_enum_functor { 123 | [[nodiscard]] constexpr optional operator()(const std::size_t index) const noexcept 124 | { 125 | optional ret; 126 | if (index < values.size()) 127 | ret.emplace(values[index]); 128 | return ret; 129 | } 130 | }; 131 | 132 | struct enum_to_index_functor { 133 | template 134 | [[nodiscard]] constexpr optional operator()(const E e) const noexcept 135 | { 136 | using T = std::underlying_type_t; 137 | 138 | if constexpr (is_contiguous) { 139 | if (enchantum::contains(e)) { 140 | return optional(std::size_t(T(e) - T(min))); 141 | } 142 | } 143 | else if constexpr (is_contiguous_bitflag) { 144 | if (enchantum::contains(e)) { 145 | constexpr bool has_zero = has_zero_flag; 146 | if constexpr (has_zero) 147 | if (static_cast(e) == 0) 148 | return optional(0); // assumes 0 is the index of value `0` 149 | 150 | using U = std::make_unsigned_t; 151 | return has_zero + std::countr_zero(static_cast(e)) - std::countr_zero(static_cast(values[has_zero])); 152 | } 153 | } 154 | else { 155 | for (std::size_t i = 0; i < values.size(); ++i) { 156 | if (values[i] == e) 157 | return i; 158 | } 159 | } 160 | return optional(); 161 | } 162 | }; 163 | 164 | 165 | template 166 | struct cast_functor { 167 | [[nodiscard]] constexpr optional operator()(const std::underlying_type_t value) const noexcept 168 | { 169 | optional a; // rvo not that it really matters 170 | if (!enchantum::contains(value)) 171 | return a; 172 | a.emplace(static_cast(value)); 173 | return a; 174 | } 175 | 176 | [[nodiscard]] constexpr optional operator()(const string_view name) const noexcept 177 | { 178 | optional a; // rvo not that it really matters 179 | 180 | constexpr auto minmax = details::minmax_string_size(names.data(), names.data() + names.size()); 181 | if (const auto size = name.size(); size < minmax.first || size > minmax.second) 182 | return a; 183 | 184 | for (const auto& [e, s] : entries) { 185 | if (s == name) { 186 | a.emplace(e); 187 | return a; 188 | } 189 | } 190 | return a; // nullopt 191 | } 192 | 193 | template BinaryPred> 194 | [[nodiscard]] constexpr optional operator()(const string_view name, const BinaryPred binary_predicate) const noexcept 195 | { 196 | optional a; // rvo not that it really matters 197 | for (const auto& [e, s] : entries) { 198 | if (binary_predicate(name, s)) { 199 | a.emplace(e); 200 | return a; 201 | } 202 | } 203 | return a; 204 | } 205 | }; 206 | 207 | } // namespace details 208 | 209 | template 210 | inline constexpr details::index_to_enum_functor index_to_enum{}; 211 | 212 | inline constexpr details::enum_to_index_functor enum_to_index{}; 213 | 214 | template 215 | inline constexpr details::cast_functor cast{}; 216 | 217 | 218 | namespace details { 219 | struct to_string_functor { 220 | template 221 | [[nodiscard]] constexpr string_view operator()(const E value) const noexcept 222 | { 223 | if (const auto i = enchantum::enum_to_index(value)) 224 | return names[*i]; 225 | return string_view(); 226 | } 227 | }; 228 | 229 | } // namespace details 230 | inline constexpr details::to_string_functor to_string{}; 231 | 232 | 233 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/entries.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "details/string_view.hpp" 4 | 5 | #if defined(__clang__) 6 | #include "details/enchantum_clang.hpp" 7 | #elif defined(__GNUC__) || defined(__GNUG__) 8 | #include "details/enchantum_gcc.hpp" 9 | #elif defined(_MSC_VER) 10 | #include "details/enchantum_msvc.hpp" 11 | #endif 12 | 13 | #include "common.hpp" 14 | #include 15 | #include 16 | 17 | namespace enchantum { 18 | 19 | #ifdef __cpp_lib_to_underlying 20 | using ::std::to_underlying; 21 | #else 22 | template 23 | [[nodiscard]] constexpr auto to_underlying(const E e) noexcept 24 | { 25 | return static_cast>(e); 26 | } 27 | #endif 28 | 29 | template, bool ShouldNullTerminate = true> 30 | inline constexpr auto entries = details::reflect, Pair, ShouldNullTerminate>(); 31 | 32 | template 33 | inline constexpr auto values = []() { 34 | constexpr auto& enums = entries; 35 | std::array ret; 36 | for (std::size_t i = 0; i < ret.size(); ++i) 37 | ret[i] = enums[i].first; 38 | return ret; 39 | }(); 40 | 41 | template 42 | inline constexpr auto names = []() { 43 | constexpr auto& enums = entries, NullTerminated>; 44 | std::array ret; 45 | for (std::size_t i = 0; i < ret.size(); ++i) 46 | ret[i] = enums[i].second; 47 | return ret; 48 | }(); 49 | 50 | template 51 | inline constexpr auto min = entries.front().first; 52 | 53 | template 54 | inline constexpr auto max = entries.back().first; 55 | 56 | template 57 | inline constexpr std::size_t count = entries.size(); 58 | 59 | } // namespace enchantum -------------------------------------------------------------------------------- /enchantum/include/enchantum/fmt_format.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bitflags.hpp" 4 | #include "details/format_util.hpp" 5 | #include "enchantum.hpp" 6 | #include 7 | 8 | template 9 | struct fmt::formatter : fmt::formatter { 10 | template 11 | constexpr auto format(const E e, FmtContext& ctx) const 12 | { 13 | return fmt::formatter::format(enchantum::details::format(e), ctx); 14 | } 15 | }; -------------------------------------------------------------------------------- /enchantum/include/enchantum/iostream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "istream.hpp" 3 | #include "ostream.hpp" 4 | 5 | namespace enchantum::iostream_operators { 6 | using ::enchantum::istream_operators::operator>>; 7 | using ::enchantum::ostream_operators::operator<<; 8 | } // namespace enchantum::iostream_operators -------------------------------------------------------------------------------- /enchantum/include/enchantum/istream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "enchantum.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | namespace enchantum::istream_operators { 8 | template 9 | requires std::assignable_from 10 | std::basic_istream& operator>>(std::basic_istream& is, E& value) 11 | { 12 | std::basic_string s; 13 | is >> s; 14 | if (!is) 15 | return is; 16 | 17 | if constexpr (is_bitflag) { 18 | if (const auto v = enchantum::cast_bitflag(s)) 19 | value = *v; 20 | else 21 | is.setstate(std::ios_base::failbit); 22 | } 23 | else { 24 | if (const auto v = enchantum::cast(s)) 25 | value = *v; 26 | else 27 | is.setstate(std::ios_base::failbit); 28 | } 29 | return is; 30 | } 31 | 32 | } // namespace enchantum::istream_operators 33 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/next_value.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.hpp" 4 | #include "details/optional.hpp" 5 | #include "enchantum.hpp" 6 | #include 7 | 8 | namespace enchantum { 9 | namespace details { 10 | template 11 | struct next_value_functor { 12 | template 13 | [[nodiscard]] constexpr optional operator()(const E value, const std::ptrdiff_t n = 1) const noexcept 14 | { 15 | if (!enchantum::contains(value)) 16 | return optional{}; 17 | 18 | const auto index = static_cast(*enchantum::enum_to_index(value)) + (n * N); 19 | if (index >= 0 && index < static_cast(count)) 20 | return optional{values[static_cast(index)]}; 21 | return optional{}; 22 | } 23 | }; 24 | 25 | template 26 | struct next_value_circular_functor { 27 | template 28 | [[nodiscard]] constexpr E operator()(const E value, const std::ptrdiff_t n = 1) const noexcept 29 | { 30 | ENCHANTUM_ASSERT(enchantum::contains(value), "next/prev_value_circular requires 'value' to be a valid enum member", value); 31 | const auto i = static_cast(*enchantum::enum_to_index(value)); 32 | constexpr auto count = static_cast(enchantum::count); 33 | return values[static_cast(((i + (n * N)) % count + count) % count)]; // handles wrap around and negative n 34 | } 35 | }; 36 | } // namespace details 37 | 38 | 39 | inline constexpr details::next_value_functor<1> next_value{}; 40 | inline constexpr details::next_value_functor<-1> prev_value{}; 41 | inline constexpr details::next_value_circular_functor<1> next_value_circular{}; 42 | inline constexpr details::next_value_circular_functor<-1> prev_value_circular{}; 43 | 44 | } // namespace enchantum 45 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/ostream.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "details/format_util.hpp" 4 | #include "enchantum.hpp" 5 | #include 6 | 7 | namespace enchantum::ostream_operators { 8 | template 9 | std::basic_ostream& operator<<(std::basic_ostream& os, const E e) 10 | { 11 | return os << details::format(e); 12 | } 13 | } // namespace enchantum::ostream_operators -------------------------------------------------------------------------------- /enchantum/include/enchantum/scoped.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bitflags.hpp" 4 | #include "details/string.hpp" 5 | #include "details/string_view.hpp" 6 | #include "enchantum.hpp" 7 | #include "type_name.hpp" 8 | 9 | namespace enchantum::scoped { 10 | namespace details { 11 | constexpr string_view extract_name_from_type_name(const string_view type_name) noexcept 12 | { 13 | if (const auto n = type_name.rfind(':'); n != type_name.npos) 14 | return type_name.substr(n + 1); 15 | else 16 | return type_name; 17 | } 18 | 19 | constexpr string_view remove_scope_or_empty(string_view string, const string_view full_type_name) noexcept 20 | { 21 | const auto type_name = extract_name_from_type_name(full_type_name); 22 | if (!string.starts_with(type_name)) 23 | return string_view(); 24 | string.remove_prefix(type_name.size()); 25 | if (!string.starts_with("::")) 26 | return string_view(); 27 | string.remove_prefix(2); 28 | return string; 29 | } 30 | } // namespace details 31 | 32 | template 33 | [[nodiscard]] constexpr bool contains(const string_view name) noexcept 34 | { 35 | const auto n = details::remove_scope_or_empty(name, type_name); 36 | return !n.empty() && enchantum::contains(n); 37 | } 38 | 39 | template BinaryPredicate> 40 | [[nodiscard]] constexpr bool contains(const string_view name, const BinaryPredicate binary_predicate) noexcept 41 | { 42 | const auto n = details::remove_scope_or_empty(name, type_name); 43 | return !n.empty() && enchantum::contains(n, binary_predicate); 44 | } 45 | 46 | namespace details { 47 | template 48 | struct scoped_cast_functor { 49 | [[nodiscard]] constexpr optional operator()(const string_view name) const noexcept 50 | { 51 | const auto n = details::remove_scope_or_empty(name, type_name); 52 | if (n.empty()) 53 | return optional(); 54 | return cast(n); 55 | } 56 | 57 | template BinaryPred> 58 | [[nodiscard]] constexpr optional operator()(const string_view name, const BinaryPred binary_predicate) const noexcept 59 | { 60 | const auto n = details::remove_scope_or_empty(name, type_name); 61 | if (n.empty()) 62 | return optional(); 63 | return cast(n, binary_predicate); 64 | } 65 | }; 66 | 67 | struct to_scoped_string_functor { 68 | template 69 | [[nodiscard]] constexpr string operator()(const E value) const noexcept 70 | { 71 | string s; 72 | if (const auto i = enchantum::enum_to_index(value)) { 73 | s += details::extract_name_from_type_name(type_name); 74 | s += "::"; 75 | s += names[*i]; 76 | return s; 77 | } 78 | return s; 79 | } 80 | }; 81 | } // namespace details 82 | 83 | 84 | inline constexpr details::to_scoped_string_functor to_string; 85 | 86 | template 87 | inline constexpr details::scoped_cast_functor cast; 88 | 89 | template BinaryPred> 90 | [[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept 91 | { 92 | std::size_t pos = 0; 93 | for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { 94 | if (!enchantum::scoped::contains(s.substr(pos, i - pos), binary_pred)) 95 | return false; 96 | pos = i + 1; 97 | } 98 | return enchantum::scoped::contains(s.substr(pos), binary_pred); 99 | } 100 | 101 | template 102 | [[nodiscard]] constexpr bool contains_bitflag(const string_view s, const char sep = '|') noexcept 103 | { 104 | std::size_t pos = 0; 105 | for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { 106 | if (!enchantum::scoped::contains(s.substr(pos, i - pos))) 107 | return false; 108 | pos = i + 1; 109 | } 110 | return enchantum::scoped::contains(s.substr(pos)); 111 | } 112 | 113 | 114 | template 115 | [[nodiscard]] constexpr string to_string_bitflag(const E value, const char sep = '|') 116 | { 117 | using T = std::underlying_type_t; 118 | if constexpr (has_zero_flag) 119 | if (static_cast(value) == 0) 120 | return enchantum::scoped::to_string(value); 121 | 122 | string name; 123 | T check_value = 0; 124 | constexpr auto scope_name = details::extract_name_from_type_name(type_name); 125 | for (auto i = std::size_t{has_zero_flag}; i < count; ++i) { 126 | const auto& [v, s] = entries[i]; 127 | if (T(v) == (T(value) & T(v))) { 128 | if (!name.empty()) 129 | name.append(1, sep); // append separator if not the first value 130 | name.append(scope_name.data(), scope_name.size()); 131 | name.append("::", 2); 132 | name.append(s.data(), s.size()); // not using operator += since this may not be std::string_view always 133 | check_value |= static_cast(v); 134 | } 135 | } 136 | if (check_value == static_cast(value)) 137 | return name; 138 | return string(); 139 | } 140 | 141 | 142 | template BinaryPred> 143 | [[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep, const BinaryPred binary_pred) noexcept 144 | { 145 | using T = std::underlying_type_t; 146 | T check_value{}; 147 | std::size_t pos = 0; 148 | for (std::size_t i = s.find(sep); i != s.npos; i = s.find(sep, pos)) { 149 | if (const auto v = enchantum::scoped::cast(s.substr(pos, i - pos), binary_pred)) 150 | check_value |= static_cast(*v); 151 | else 152 | return optional(); 153 | pos = i + 1; 154 | } 155 | 156 | if (const auto v = enchantum::scoped::cast(s.substr(pos), binary_pred)) 157 | return optional(static_cast(check_value | static_cast(*v))); 158 | return optional(); 159 | } 160 | 161 | template 162 | [[nodiscard]] constexpr optional cast_bitflag(const string_view s, const char sep = '|') noexcept 163 | { 164 | return enchantum::scoped::cast_bitflag(s, sep, [](const auto& a, const auto& b) { return a == b; }); 165 | } 166 | 167 | } // namespace enchantum::scoped 168 | -------------------------------------------------------------------------------- /enchantum/include/enchantum/std_format.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "bitflags.hpp" 4 | #include "enchantum.hpp" 5 | 6 | #include 7 | #include 8 | #include "details/format_util.hpp" 9 | 10 | template 11 | struct std::formatter : std::formatter { 12 | template 13 | constexpr auto format(const E e, FmtContext& ctx) const 14 | { 15 | return std::formatter::format(enchantum::details::format(e), ctx); 16 | } 17 | }; -------------------------------------------------------------------------------- /enchantum/include/enchantum/type_name.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "details/string_view.hpp" 3 | #include 4 | 5 | namespace enchantum { 6 | 7 | namespace details { 8 | #define SZC(x) (sizeof(x) - 1) 9 | template 10 | constexpr auto type_name_func() noexcept 11 | { 12 | #if defined(__clang__) 13 | constexpr std::size_t prefix = 0; 14 | constexpr auto s = string_view(__PRETTY_FUNCTION__ + SZC("auto enchantum::details::type_name_func() [_ = "), 15 | SZC(__PRETTY_FUNCTION__) - SZC("auto enchantum::details::type_name_func() [_ = ]")); 16 | #elif defined(_MSC_VER) 17 | constexpr auto s = string_view(__FUNCSIG__ + SZC("auto __cdecl enchantum::details::type_name_func<"), 18 | SZC(__FUNCSIG__) - SZC("auto __cdecl enchantum::details::type_name_func<") - 19 | SZC(">(void) noexcept")); 20 | 21 | // clang-format off 22 | constexpr auto prefix = std::is_enum_v ? SZC("enum ") : 23 | std::is_class_v ? SZC("struct ") - (s[0] == 'c') : 24 | 0; 25 | // clang-format on 26 | #elif defined(__GNUG__) 27 | constexpr std::size_t prefix = 0; 28 | constexpr auto s = string_view(__PRETTY_FUNCTION__ + SZC("constexpr auto enchantum::details::type_name_func() [with _ = "), 29 | SZC(__PRETTY_FUNCTION__) - 30 | SZC("constexpr auto enchantum::details::type_name_func() [with _ = ]")); 31 | #endif 32 | std::array ret{}; 33 | auto* const ret_data = ret.data(); 34 | const auto* const s_data = s.data(); 35 | 36 | for (std::size_t i = 0; i < ret.size() - 1; ++i) 37 | ret_data[i] = s_data[i + prefix]; 38 | return ret; 39 | } 40 | 41 | template 42 | inline constexpr auto type_name_func_var = type_name_func(); 43 | #undef SZC 44 | 45 | } // namespace details 46 | 47 | template 48 | inline constexpr auto type_name = string_view(details::type_name_func_var.data(), 49 | details::type_name_func_var.size() - 1); 50 | 51 | } // namespace enchantum -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | FetchContent_Declare( 3 | Catch2 4 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 5 | GIT_TAG devel 6 | ) 7 | FetchContent_MakeAvailable(Catch2) 8 | 9 | target_compile_features(Catch2 PRIVATE cxx_std_20) 10 | target_compile_definitions(Catch2 PUBLIC CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) 11 | add_executable(tests) 12 | target_include_directories(tests PRIVATE "third_party") 13 | target_compile_features(tests PRIVATE cxx_std_20) 14 | 15 | 16 | target_link_libraries(tests PRIVATE enchantum::enchantum Catch2::Catch2WithMain) 17 | 18 | if(ENCHANTUM_RUNTIME_TESTS) 19 | message(STATUS "enchantum tests are being ran at runtime") 20 | target_compile_definitions(tests PRIVATE CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) 21 | endif() 22 | 23 | if(MSVC) 24 | target_compile_options(tests PRIVATE /Za /permissive-) 25 | target_compile_options(tests PRIVATE /WX /W4) 26 | else() 27 | target_compile_options(tests PRIVATE -Werror -Wall -Wextra -Wshadow -Wconversion -Wpedantic) 28 | endif() 29 | 30 | file(GLOB_RECURSE SRCS "*.cpp" "*.hpp") 31 | 32 | 33 | if(MSVC) 34 | set(CMAKE_REQUIRED_FLAGS "/std:c++20") 35 | else() 36 | set(CMAKE_REQUIRED_FLAGS "-std=c++20") 37 | endif() 38 | check_cxx_source_compiles(" 39 | #include 40 | int main() { 41 | auto s = std::format(\"{}\",42); 42 | (void)s; 43 | return 0; 44 | }" 45 | HAS_STD_FORMAT 46 | ) 47 | 48 | if(NOT HAS_STD_FORMAT) 49 | message(STATUS "This compiler does not support header not running std_format.cpp tests") 50 | list(REMOVE_ITEM SRCS "${CMAKE_CURRENT_SOURCE_DIR}/std_format.cpp") 51 | endif() 52 | 53 | target_sources(tests PRIVATE ${SRCS}) 54 | 55 | include(CTest) 56 | include(Catch) 57 | catch_discover_tests(tests) 58 | -------------------------------------------------------------------------------- /tests/algorithms.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | TEMPLATE_LIST_TEST_CASE("for_each", "[algorithms][for_each]", AllEnumsTestTypes) 7 | { 8 | std::vector names; 9 | 10 | enchantum::for_each([&names](const auto c) { 11 | if constexpr (c != enchantum::values[0]) 12 | names.emplace_back(enchantum::to_string(c.value)); 13 | }); 14 | CHECK(names.size() + 1 == enchantum::count); 15 | 16 | for (std::size_t i = 0; i < names.size(); ++i) 17 | CHECK(names[i] == enchantum::names[i+1]); 18 | } 19 | -------------------------------------------------------------------------------- /tests/array.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | 5 | enum class Country : std::int16_t { 6 | Brandon = -6, 7 | London = -3, 8 | French, 9 | Earth, 10 | Asia, 11 | Europe = 8, 12 | LeInternet = 12, 13 | Atlantis, 14 | }; 15 | 16 | 17 | TEST_CASE("enum array", "[containers][array]") 18 | { 19 | enchantum::array array; 20 | 21 | array[Country::London] = "Capital, not a country"; 22 | array[Country::French] = "Le French"; 23 | array[Country::Brandon] = "Wait who's brandon?"; 24 | array[Country::Earth] = "Not in the UN yet"; 25 | array[Country::Asia] = "Anime"; 26 | array[Country::Europe] = "The great country of Europe"; 27 | array[Country::LeInternet] = "Online-only country"; 28 | array[Country::Atlantis] = "Underwater with Posidon"; 29 | CHECK(array[Country::London] == "Capital, not a country"); 30 | CHECK(array[Country::French] == "Le French"); 31 | CHECK(array[Country::Brandon] == "Wait who's brandon?"); 32 | CHECK(array[Country::Earth] == "Not in the UN yet"); 33 | CHECK(array[Country::Asia] == "Anime"); 34 | CHECK(array[Country::Europe] == "The great country of Europe"); 35 | CHECK(array[Country::LeInternet] == "Online-only country"); 36 | CHECK(array[Country::Atlantis] == "Underwater with Posidon"); 37 | CHECK(array.size() == 8); 38 | CHECK(array.size() == enchantum::count); 39 | 40 | CHECK_THROWS_AS(array.at(static_cast(0xdead / 2)), std::out_of_range); 41 | 42 | 43 | const enchantum::array array2 = {{ 44 | "Wait who's brandon?", 45 | "Capital, not a country", 46 | "Le French", 47 | "Not in the UN yet", 48 | "Anime", 49 | "The great country of Europe", 50 | "Online-only country", 51 | "Underwater with Posidon", 52 | }}; 53 | CHECK(array == array2); 54 | CHECK_FALSE(array != array2); 55 | } -------------------------------------------------------------------------------- /tests/bitflags.cpp: -------------------------------------------------------------------------------- 1 | #include "case_insensitive.hpp" 2 | #include "test_utility.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | namespace { 17 | enum class EntityStatus { 18 | Active = 1, 19 | Inactive = 2, 20 | Pending = 4 21 | }; 22 | ENCHANTUM_DEFINE_BITWISE_FOR(EntityStatus) 23 | 24 | } // namespace 25 | 26 | enum class Permission : int { 27 | None = 0, 28 | Read = 1 << 1, 29 | Write = 1 << 2, 30 | Execute = 1 << 3, 31 | }; 32 | 33 | struct DirectionFlags_Struct { 34 | enum Type : std::uint64_t { 35 | NoDirection = 0, 36 | Left = std::uint64_t{1} << 10, 37 | Right = std::uint64_t{1} << 20, 38 | Up = std::uint64_t{1} << 31, 39 | Down = std::uint64_t{1} << 63, 40 | }; 41 | }; 42 | using DirectionFlags = DirectionFlags_Struct::Type; 43 | 44 | enum Level : int { 45 | NoLevel = 0, 46 | Level1 = 1 << 1, 47 | Level2 = 1 << 2, 48 | Level3 = 1 << 3, 49 | Level4 = 1 << 4, 50 | 51 | Alias1 = Level1, 52 | Alias2 = Level2, 53 | Alias3 = Level3, 54 | Alias4 = Level4 55 | }; 56 | 57 | ENCHANTUM_DEFINE_BITWISE_FOR(Permission) 58 | ENCHANTUM_DEFINE_BITWISE_FOR(DirectionFlags) 59 | ENCHANTUM_DEFINE_BITWISE_FOR(Level) 60 | 61 | TEST_CASE("cast cast_bitflags", "[casts][bitflags]") 62 | { 63 | SECTION("from strings") 64 | { 65 | STATIC_CHECK(enchantum::cast("Active") == EntityStatus::Active); 66 | STATIC_CHECK(enchantum::cast("Inactive") == EntityStatus::Inactive); 67 | STATIC_CHECK(enchantum::cast("pending", case_insensitive) == EntityStatus::Pending); 68 | STATIC_CHECK(enchantum::cast_bitflag("peNdIng|AcTive", '|', case_insensitive).has_value()); 69 | } 70 | 71 | STATIC_CHECK(enchantum::cast_bitflag("Inactive|Active") == (EntityStatus::Inactive | EntityStatus::Active)); 72 | 73 | STATIC_CHECK(enchantum::cast("Read") == Permission::Read); 74 | STATIC_CHECK(enchantum::cast("Write") == Permission::Write); 75 | STATIC_CHECK(enchantum::cast("Execute") == Permission::Execute); 76 | STATIC_CHECK(enchantum::cast("None").has_value()); 77 | 78 | STATIC_CHECK(enchantum::cast("Up") == DirectionFlags::Up); 79 | STATIC_CHECK(enchantum::cast("Down") == DirectionFlags::Down); 80 | STATIC_CHECK(enchantum::cast("Right") == DirectionFlags::Right); 81 | STATIC_CHECK(enchantum::cast("Left") == DirectionFlags::Left); 82 | STATIC_CHECK(enchantum::cast("NoDirection").has_value()); 83 | 84 | STATIC_CHECK(enchantum::cast("Level1") == Level::Level1); 85 | STATIC_CHECK(enchantum::cast("Level2") == Level::Level2); 86 | STATIC_CHECK(enchantum::cast("Level3") == Level::Level3); 87 | STATIC_CHECK(enchantum::cast("Level4") == Level::Level4); 88 | STATIC_CHECK(enchantum::cast_bitflag("Level3|Level1") == (Level::Level3 | Level::Level1)); 89 | STATIC_CHECK(enchantum::cast("NoLevel").has_value()); 90 | } 91 | 92 | TEST_CASE("invalid_casts", "[cast][bitflags]") 93 | { 94 | SECTION("Invalid strings for enum cast") 95 | { 96 | STATIC_CHECK_FALSE(enchantum::cast("Unknown").has_value()); 97 | STATIC_CHECK_FALSE(enchantum::cast("ExecuteAll").has_value()); 98 | STATIC_CHECK_FALSE(enchantum::cast("UPWARD").has_value()); 99 | } 100 | 101 | SECTION("Invalid case without case_insensitive flag") 102 | { 103 | STATIC_CHECK_FALSE(enchantum::cast("pending").has_value()); 104 | STATIC_CHECK_FALSE(enchantum::cast("read").has_value()); 105 | } 106 | 107 | SECTION("Valid mixed-case cast with case_insensitive") 108 | { 109 | STATIC_CHECK(enchantum::cast("eXecuTe", case_insensitive) == Permission::Execute); 110 | } 111 | 112 | SECTION("Invalid separator or format in bitflag cast") 113 | { 114 | STATIC_CHECK_FALSE(enchantum::cast_bitflag("Level1,Level2", '|', case_insensitive).has_value()); 115 | STATIC_CHECK_FALSE(enchantum::cast_bitflag("|Write|Read", '|', case_insensitive).has_value()); 116 | STATIC_CHECK_FALSE(enchantum::cast_bitflag("Write||Read", '|', case_insensitive).has_value()); 117 | STATIC_CHECK_FALSE(enchantum::cast_bitflag("Write|Read|", '|', case_insensitive).has_value()); 118 | } 119 | 120 | SECTION("Empty input returns none") { STATIC_CHECK_FALSE(enchantum::cast_bitflag("").has_value()); } 121 | } 122 | TEST_CASE("complex_bitflag_combinations", "[bitflags]") 123 | { 124 | SECTION("Mixed ordering") 125 | { 126 | STATIC_CHECK(enchantum::cast_bitflag("Level4|Level2|Level1") == (Level::Level4 | Level::Level2 | Level::Level1)); 127 | } 128 | 129 | SECTION("Duplicate flags") 130 | { 131 | STATIC_CHECK(enchantum::cast_bitflag("Read|Read|Write") == (Permission::Read | Permission::Write)); 132 | } 133 | } 134 | 135 | TEST_CASE("alias_and_zero_behavior", "[bitflags][aliases]") 136 | { 137 | SECTION("Zero flag handling") 138 | { 139 | STATIC_CHECK(enchantum::cast("None") == Permission::None); 140 | STATIC_CHECK(enchantum::contains_bitflag(Permission::None)); 141 | } 142 | } 143 | 144 | TEST_CASE("contains_bitflag", "[contains][bitflags]") 145 | { 146 | { 147 | // shut down warnings about unneeded internal declarations 148 | EntityStatus e{}; 149 | EntityStatus e2{}; 150 | (void)(e & e); 151 | (void)(e &= e2); 152 | (void)(e |= e2); 153 | (void)(e | e); 154 | (void)(e ^ e); 155 | (void)(e ^= e2); 156 | (void)(~e); 157 | } 158 | 159 | SECTION("DirectionFlags") 160 | { 161 | STATIC_CHECK(enchantum::contains_bitflag(DirectionFlags::NoDirection)); 162 | STATIC_CHECK(enchantum::contains_bitflag(DirectionFlags::Up | DirectionFlags::Down)); 163 | STATIC_CHECK_FALSE(enchantum::contains_bitflag(DirectionFlags(0xdeadbeef))); 164 | } 165 | 166 | SECTION("Level") 167 | { 168 | STATIC_CHECK(enchantum::contains_bitflag(Level::NoLevel)); 169 | STATIC_CHECK(enchantum::contains_bitflag(Level::Level1 | Level::Level3)); 170 | } 171 | 172 | SECTION("contains_bitflag string") 173 | { 174 | STATIC_CHECK(enchantum::contains_bitflag("Active")); 175 | STATIC_CHECK(enchantum::contains_bitflag("Inactive")); 176 | STATIC_CHECK(enchantum::contains_bitflag("pendinG",'|', case_insensitive)); 177 | STATIC_CHECK(enchantum::contains_bitflag("peNdIng|AcTive", '|', case_insensitive)); 178 | STATIC_CHECK(enchantum::contains_bitflag("Inactive.Active",'.')); 179 | 180 | STATIC_CHECK_FALSE(enchantum::contains_bitflag("Hello.Active", '.')); 181 | STATIC_CHECK_FALSE(enchantum::contains_bitflag("Hello|Active")); 182 | STATIC_CHECK_FALSE(enchantum::contains_bitflag("hello.active",'.',case_insensitive)); 183 | 184 | 185 | STATIC_CHECK(enchantum::contains_bitflag("Read")); 186 | STATIC_CHECK(enchantum::contains_bitflag("Write")); 187 | STATIC_CHECK(enchantum::contains_bitflag("Execute")); 188 | STATIC_CHECK(enchantum::contains_bitflag("None")); 189 | 190 | STATIC_CHECK(enchantum::contains_bitflag("Up")); 191 | STATIC_CHECK(enchantum::contains_bitflag("Down")); 192 | STATIC_CHECK(enchantum::contains_bitflag("Right")); 193 | STATIC_CHECK(enchantum::contains_bitflag("Left")); 194 | STATIC_CHECK(enchantum::contains_bitflag("NoDirection")); 195 | 196 | STATIC_CHECK(enchantum::contains_bitflag("Level1")); 197 | STATIC_CHECK(enchantum::contains_bitflag("Level2")); 198 | STATIC_CHECK(enchantum::contains_bitflag("Level3")); 199 | STATIC_CHECK(enchantum::contains_bitflag("Level4")); 200 | STATIC_CHECK(enchantum::contains_bitflag("Level3|Level1")); 201 | STATIC_CHECK(enchantum::contains_bitflag("NoLevel")); 202 | } 203 | } 204 | 205 | TEST_CASE("contains_bitflag_with_invalid_bits", "[bitflags]") 206 | { 207 | SECTION("Contains invalid bits in DirectionFlags") 208 | { 209 | STATIC_CHECK_FALSE(enchantum::contains_bitflag(DirectionFlags(1 << 5))); // Not defined 210 | STATIC_CHECK_FALSE(enchantum::contains_bitflag(DirectionFlags(0xFFFFFFFFFFFFFFFF))); 211 | } 212 | 213 | SECTION("Valid combinations still pass") 214 | { 215 | STATIC_CHECK(enchantum::contains_bitflag(DirectionFlags::Left | DirectionFlags::Right)); 216 | } 217 | } 218 | 219 | TEMPLATE_LIST_TEST_CASE("bitflags", "[bitflags]", AllFlagsTestTypes) 220 | { 221 | constexpr auto count = enchantum::count; 222 | constexpr auto& values = enchantum::values; 223 | using T = std::underlying_type_t; 224 | std::vector combinations; 225 | combinations.reserve(1u << count); 226 | constexpr auto total = std::uint64_t{1} << (count - enchantum::has_zero_flag); 227 | for (std::uint64_t mask = 1; mask < total; ++mask) { 228 | T value{}; 229 | for (auto bit = std::size_t{enchantum::has_zero_flag}; bit < count; ++bit) { 230 | if (mask & static_cast(values[bit])) { 231 | value |= static_cast(values[bit]); 232 | } 233 | } 234 | if (static_cast(value) != 0) 235 | combinations.push_back(static_cast(value)); 236 | } 237 | // test for 0 238 | if (enchantum::has_zero_flag) 239 | combinations.emplace_back(); 240 | 241 | SECTION("cast_bitflag(to_string_bitflag(enum)) == enum") 242 | { 243 | for (const auto comb : combinations) { 244 | CHECK(comb == enchantum::cast_bitflag(enchantum::to_string_bitflag(comb))); 245 | } 246 | } 247 | 248 | SECTION("contains_bitflag(to_string_bitflag(enum))") 249 | { 250 | for (const auto comb : combinations) { 251 | CHECK(enchantum::contains_bitflag(enchantum::to_string_bitflag(comb))); 252 | } 253 | } 254 | 255 | SECTION("contains_bitflag(enum)") 256 | { 257 | for (const auto comb : combinations) { 258 | CHECK(enchantum::contains_bitflag(comb)); 259 | } 260 | } 261 | 262 | SECTION("contains_bitflag(std::underlying_type_t(enum))") 263 | { 264 | for (const auto comb : combinations) { 265 | CHECK(enchantum::contains_bitflag(static_cast(comb))); 266 | } 267 | } 268 | 269 | SECTION("Value ORs") 270 | { 271 | const auto string = []() { 272 | std::string ret; 273 | for (const auto& [e, s] : enchantum::entries) { 274 | if (T(e) != 0) { 275 | ret += s; 276 | if (e != enchantum::max) 277 | ret += '|'; 278 | } 279 | } 280 | return ret; 281 | }(); 282 | CHECK(enchantum::to_string_bitflag(enchantum::value_ors) == string); 283 | } 284 | } 285 | 286 | TEMPLATE_LIST_TEST_CASE("contains_bitflag returns false for invalid combinations", "[bitflags]", AllFlagsTestTypes) 287 | { 288 | using Underlying = std::underlying_type_t; 289 | 290 | std::vector invalid_combinations; 291 | 292 | for (std::size_t bit = 0; bit < sizeof(Underlying) * CHAR_BIT; ++bit) { 293 | const auto test_bit = static_cast(Underlying{1} << bit); 294 | 295 | if (!static_cast(enchantum::value_ors & test_bit)) { 296 | // This bit is not part of the valid mask, so it's invalid 297 | invalid_combinations.push_back(test_bit); 298 | } 299 | } 300 | 301 | if constexpr (enchantum::has_zero_flag) 302 | CHECK(enchantum::contains_bitflag(TestType{})); 303 | 304 | for (const auto comb : invalid_combinations) 305 | CHECK_FALSE(enchantum::contains_bitflag(comb)); 306 | } -------------------------------------------------------------------------------- /tests/bitset.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | TEMPLATE_LIST_TEST_CASE("bitset identities", "[bitset]", AllEnumsTestTypes) 8 | { 9 | enchantum::bitset bitset; 10 | 11 | SECTION("set and reset") 12 | { 13 | for (const auto value : enchantum::values) { 14 | REQUIRE_FALSE(bitset.test(value)); 15 | bitset.set(value); 16 | REQUIRE(bitset.test(value)); 17 | bitset.reset(value); 18 | REQUIRE_FALSE(bitset.test(value)); 19 | } 20 | } 21 | 22 | SECTION("flip") 23 | { 24 | for (const auto value : enchantum::values) { 25 | bitset.flip(value); 26 | REQUIRE(bitset.test(value)); 27 | bitset.flip(value); 28 | REQUIRE_FALSE(bitset.test(value)); 29 | } 30 | } 31 | 32 | SECTION("operator[]") 33 | { 34 | for (const auto value : enchantum::values) { 35 | bitset[value] = true; 36 | REQUIRE(bitset.test(value)); 37 | bitset[value] = false; 38 | REQUIRE_FALSE(bitset.test(value)); 39 | } 40 | 41 | const auto& cbitset = bitset; 42 | for (std::size_t i = 0; i < cbitset.size(); ++i) 43 | REQUIRE_FALSE(cbitset[i]); 44 | } 45 | 46 | SECTION("count") 47 | { 48 | for (const auto value : enchantum::values) 49 | bitset.set(value); 50 | REQUIRE(bitset.count() == bitset.size()); 51 | } 52 | } 53 | 54 | TEST_CASE("bitset: default construction", "[bitset]") 55 | { 56 | constexpr enchantum::bitset colors; 57 | 58 | REQUIRE(colors.none()); 59 | REQUIRE_FALSE(colors.any()); 60 | REQUIRE(colors.count() == 0); 61 | } 62 | 63 | TEST_CASE("bitset: initializer_list construction", "[bitset]") 64 | { 65 | enchantum::bitset colors{Color::Aqua, Color::Green}; 66 | 67 | REQUIRE(colors.test(Color::Aqua)); 68 | REQUIRE(colors.test(Color::Green)); 69 | REQUIRE_FALSE(colors.test(Color::Purple)); 70 | REQUIRE(colors.count() == 2); 71 | } 72 | 73 | TEST_CASE("bitset: set and reset", "[bitset]") 74 | { 75 | enchantum::bitset colors; 76 | 77 | colors.set(Color::Red); 78 | REQUIRE(colors.test(Color::Red)); 79 | 80 | colors.reset(Color::Red); 81 | REQUIRE_FALSE(colors.test(Color::Red)); 82 | } 83 | 84 | TEST_CASE("bitset: flip", "[bitset]") 85 | { 86 | enchantum::bitset colors; 87 | 88 | colors.flip(Color::Blue); 89 | REQUIRE(colors.test(Color::Blue)); 90 | 91 | colors.flip(Color::Blue); 92 | REQUIRE_FALSE(colors.test(Color::Blue)); 93 | } 94 | 95 | TEST_CASE("bitset: to_string named output", "[bitset]") 96 | { 97 | enchantum::bitset colors{Color::Aqua, Color::Red}; 98 | 99 | std::string result = colors.to_string(); 100 | REQUIRE(colors.to_string('.') == "Aqua.Red"); 101 | REQUIRE(colors.to_string() == "Aqua|Red"); 102 | 103 | REQUIRE(colors.to_string('0', '1') == std::bitset>(colors.to_ulong()).to_string()); 104 | } 105 | 106 | TEST_CASE("bitset: to_string binary", "[bitset]") 107 | { 108 | enchantum::bitset colors{Color::Red}; 109 | 110 | std::string binary = colors.to_string('0', '1'); 111 | REQUIRE(std::count(binary.begin(), binary.end(), '1') == 1); 112 | } 113 | 114 | TEST_CASE("bitset: operator[] and reference access", "[bitset]") 115 | { 116 | enchantum::bitset colors; 117 | 118 | colors[Color::Purple] = true; 119 | REQUIRE(colors.test(Color::Purple)); 120 | 121 | colors[Color::Purple] = false; 122 | REQUIRE_FALSE(colors.test(Color::Purple)); 123 | } 124 | -------------------------------------------------------------------------------- /tests/case_insensitive.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | struct CaseInsenitive { 4 | template 5 | constexpr bool operator()(const String& lhs, const String& rhs) const 6 | { 7 | if (lhs.size() != rhs.size()) 8 | return false; 9 | 10 | constexpr auto tolower = [](const char c) { return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }; 11 | for (std::size_t i = 0; i < lhs.size(); ++i) 12 | if (tolower(lhs[i]) != tolower(rhs[i])) 13 | return false; 14 | return true; 15 | } 16 | }; 17 | 18 | inline constexpr CaseInsenitive case_insensitive; 19 | -------------------------------------------------------------------------------- /tests/concepts.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | 5 | TEST_CASE("Concept checks", "[concepts]") 6 | { 7 | SECTION("Enum concept check") 8 | { 9 | STATIC_CHECK(!enchantum::Enum); 10 | STATIC_CHECK(enchantum::Enum); 11 | STATIC_CHECK(enchantum::Enum); 12 | STATIC_CHECK(enchantum::Enum); 13 | } 14 | 15 | SECTION("ContiguousEnum concept check") 16 | { 17 | STATIC_CHECK(!enchantum::ContiguousEnum); 18 | STATIC_CHECK(enchantum::ContiguousEnum); 19 | STATIC_CHECK(enchantum::ContiguousEnum); 20 | STATIC_CHECK(!enchantum::ContiguousEnum); 21 | STATIC_CHECK(!enchantum::ContiguousEnum); 22 | } 23 | 24 | SECTION("ScopedEnum concept check") 25 | { 26 | STATIC_CHECK(!enchantum::ScopedEnum); 27 | STATIC_CHECK(enchantum::ScopedEnum); 28 | STATIC_CHECK(enchantum::ScopedEnum); 29 | STATIC_CHECK(enchantum::ScopedEnum); 30 | STATIC_CHECK(enchantum::ScopedEnum); 31 | STATIC_CHECK(!enchantum::ScopedEnum); 32 | } 33 | 34 | SECTION("UnscopedEnum concept check") 35 | { 36 | STATIC_CHECK(!enchantum::UnscopedEnum); 37 | STATIC_CHECK(!enchantum::UnscopedEnum); 38 | STATIC_CHECK(!enchantum::UnscopedEnum); 39 | STATIC_CHECK(!enchantum::UnscopedEnum); 40 | STATIC_CHECK(!enchantum::UnscopedEnum); 41 | STATIC_CHECK(enchantum::UnscopedEnum); 42 | } 43 | 44 | SECTION("SignedEnum concept check") 45 | { 46 | STATIC_CHECK(!enchantum::SignedEnum); 47 | STATIC_CHECK(enchantum::SignedEnum); 48 | STATIC_CHECK(enchantum::SignedEnum); 49 | STATIC_CHECK(!enchantum::SignedEnum); 50 | STATIC_CHECK(!enchantum::SignedEnum); 51 | STATIC_CHECK(enchantum::SignedEnum); 52 | } 53 | 54 | SECTION("UnsignedEnum concept check") 55 | { 56 | STATIC_CHECK(!enchantum::UnsignedEnum); 57 | STATIC_CHECK(!enchantum::UnsignedEnum); 58 | STATIC_CHECK(!enchantum::UnsignedEnum); 59 | STATIC_CHECK(enchantum::UnsignedEnum); 60 | STATIC_CHECK(enchantum::UnsignedEnum); 61 | STATIC_CHECK(!enchantum::UnsignedEnum); 62 | } 63 | 64 | 65 | SECTION("BitFlagEnum concept check") 66 | { 67 | STATIC_CHECK(!enchantum::BitFlagEnum); 68 | STATIC_CHECK(!enchantum::BitFlagEnum); 69 | STATIC_CHECK(!enchantum::BitFlagEnum); 70 | STATIC_CHECK(!enchantum::BitFlagEnum); 71 | STATIC_CHECK(!enchantum::BitFlagEnum); 72 | STATIC_CHECK(enchantum::BitFlagEnum); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/different_entries_types.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | struct ValueAndString { 9 | E value; 10 | std::string_view string; 11 | }; 12 | 13 | TEMPLATE_LIST_TEST_CASE("KV as entries key-value pair", "[entries][override_key_value_pair]", AllEnumsTestTypes) 14 | { 15 | for (const auto& kv : enchantum::entries>) { 16 | CHECK(kv.string == enchantum::to_string(kv.value)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/double_include.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #if __has_include() 16 | #include 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #if __has_include() 34 | #include 35 | #endif 36 | -------------------------------------------------------------------------------- /tests/enchantum.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | TEMPLATE_LIST_TEST_CASE("array size checks", "[constants]", AllEnumsTestTypes) 8 | { 9 | constexpr auto count = enchantum::count; 10 | //constexpr auto min = enchantum::min; 11 | //constexpr auto max = enchantum::max; 12 | constexpr auto& names = enchantum::names; 13 | constexpr auto& values = enchantum::values; 14 | constexpr auto& entries = enchantum::entries; 15 | 16 | 17 | SECTION("count") { STATIC_CHECK(count == entries.size()); } 18 | SECTION("names") { STATIC_CHECK(names.size() == entries.size()); } 19 | SECTION("values") { STATIC_CHECK(values.size() == entries.size()); } 20 | 21 | SECTION("names and values equal to entries") 22 | { 23 | for (std::size_t i = 0; i < count; ++i) { 24 | CHECK(values[i] == entries[i].first); 25 | CHECK(names[i] == entries[i].second); 26 | } 27 | } 28 | 29 | SECTION("enum_to_index identities") 30 | { 31 | for (std::size_t i = 0; i < count; ++i) { 32 | CHECK(i == enchantum::enum_to_index(values[i])); 33 | } 34 | } 35 | 36 | SECTION("index_to_enum identities") 37 | { 38 | for (std::size_t i = 0; i < count; ++i) { 39 | CHECK(enchantum::index_to_enum(i) == values[i]); 40 | CHECK_FALSE(enchantum::index_to_enum(i + count).has_value()); 41 | } 42 | } 43 | 44 | SECTION("cast(to_string()) identities") 45 | { 46 | for (std::size_t i = 0; i < count; ++i) { 47 | CHECK(values[i] == enchantum::cast(enchantum::to_string(values[i]))); 48 | } 49 | } 50 | 51 | } 52 | 53 | 54 | TEST_CASE("Color enum to_string", "[to_string]") 55 | { 56 | STATIC_CHECK(enchantum::to_string(Color::Green) == "Green"); 57 | STATIC_CHECK(enchantum::to_string(Color::Red) == "Red"); 58 | STATIC_CHECK(enchantum::to_string(Color::Blue) == "Blue"); 59 | STATIC_CHECK(enchantum::to_string(Color::Purple) == "Purple"); 60 | STATIC_CHECK(enchantum::to_string(Color::Aqua) == "Aqua"); 61 | } 62 | 63 | TEST_CASE("Color enum cast", "[cast]") 64 | { 65 | STATIC_CHECK(enchantum::cast("Green") == Color::Green); 66 | STATIC_CHECK(enchantum::cast("Red") == Color::Red); 67 | STATIC_CHECK(enchantum::cast("Blue") == Color::Blue); 68 | STATIC_CHECK(enchantum::cast("Purple") == Color::Purple); 69 | STATIC_CHECK(enchantum::cast("Aqua") == Color::Aqua); 70 | 71 | STATIC_CHECK_FALSE(enchantum::cast("ZXShady")); 72 | STATIC_CHECK_FALSE(enchantum::cast("red")); // case-sensitive 73 | STATIC_CHECK_FALSE(enchantum::cast("GREEN")); // all caps 74 | } 75 | 76 | TEST_CASE("Color enum min/max", "[range][min_max]") 77 | { 78 | STATIC_CHECK(enchantum::min == Color::Aqua); 79 | STATIC_CHECK(enchantum::max == Color::Blue); 80 | 81 | STATIC_CHECK(static_cast>(enchantum::min) == -42); 82 | STATIC_CHECK(static_cast>(enchantum::max) == 214); 83 | } 84 | 85 | TEST_CASE("Color enum cast from underlying type", "[cast]") 86 | { 87 | using enchantum::cast; 88 | using T = std::underlying_type_t; 89 | STATIC_CHECK(enchantum::cast(T(Color::Green)) == Color::Green); 90 | STATIC_CHECK(enchantum::cast(T(Color::Red)) == Color::Red); 91 | STATIC_CHECK(enchantum::cast(T(Color::Blue)) == Color::Blue); 92 | STATIC_CHECK(enchantum::cast(T(Color::Purple)) == Color::Purple); 93 | STATIC_CHECK(enchantum::cast(T(Color::Aqua)) == Color::Aqua); 94 | STATIC_CHECK_FALSE(enchantum::cast(T(2138))); 95 | } 96 | 97 | TEST_CASE("Color enum cast from string_view", "[cast]") 98 | { 99 | STATIC_CHECK(enchantum::cast("Green") == Color::Green); 100 | STATIC_CHECK(enchantum::cast("Red") == Color::Red); 101 | STATIC_CHECK(enchantum::cast("Blue") == Color::Blue); 102 | STATIC_CHECK(enchantum::cast("Purple") == Color::Purple); 103 | STATIC_CHECK(enchantum::cast("Aqua") == Color::Aqua); 104 | 105 | STATIC_CHECK_FALSE(enchantum::cast("Chartreuse")); 106 | STATIC_CHECK_FALSE(enchantum::cast("BLUE")); 107 | } 108 | 109 | 110 | TEST_CASE("Color enum cast with custom binary predicate (case insensitive)", "[cast]") 111 | { 112 | STATIC_CHECK(enchantum::cast("green", case_insensitive) == Color::Green); 113 | STATIC_CHECK(enchantum::cast("RED", case_insensitive) == Color::Red); 114 | STATIC_CHECK(enchantum::cast("bLuE", case_insensitive) == Color::Blue); 115 | STATIC_CHECK(enchantum::cast("purple", case_insensitive) == Color::Purple); 116 | STATIC_CHECK(enchantum::cast("AQUA", case_insensitive) == Color::Aqua); 117 | STATIC_CHECK_FALSE(enchantum::cast("zxSHADY", case_insensitive)); 118 | } 119 | 120 | TEST_CASE("Color enum index_to_enum", "[index_to_enum]") 121 | { 122 | STATIC_CHECK(enchantum::index_to_enum(0) == Color::Aqua); 123 | STATIC_CHECK(enchantum::index_to_enum(1) == Color::Purple); 124 | STATIC_CHECK(enchantum::index_to_enum(2) == Color::Green); 125 | STATIC_CHECK(enchantum::index_to_enum(3) == Color::Red); 126 | STATIC_CHECK(enchantum::index_to_enum(4) == Color::Blue); 127 | } 128 | TEST_CASE("Color count", "[count]") 129 | { 130 | STATIC_CHECK(enchantum::count == 10); 131 | STATIC_CHECK(enchantum::count == 10); 132 | STATIC_CHECK(enchantum::count == 10); 133 | 134 | STATIC_CHECK(enchantum::count == 5); 135 | STATIC_CHECK(enchantum::count == 2); 136 | STATIC_CHECK(enchantum::count == 26); 137 | STATIC_CHECK(enchantum::count == 2); 138 | STATIC_CHECK(enchantum::count == 5); 139 | STATIC_CHECK(enchantum::count == 7); 140 | } 141 | -------------------------------------------------------------------------------- /tests/fmt_format.cpp: -------------------------------------------------------------------------------- 1 | #define FMT_HEADER_ONLY 2 | #define FMT_UNICODE 0 // we don't need unicode 3 | #include 4 | 5 | #include "test_utility.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | TEMPLATE_LIST_TEST_CASE("fmt::format", "[stringify][fmt_format]", AllEnumsTestTypes) 13 | { 14 | for (const auto& [value, string] : enchantum::entries) 15 | CHECK(fmt::format("{}", value) == string); 16 | using T = std::underlying_type_t; 17 | if constexpr (!enchantum::is_bitflag && !std::is_same_v>) 18 | if constexpr (123 <= T(enchantum::max) && !enchantum::contains(123)) 19 | CHECK("123" == fmt::format("{}", TestType(123))); 20 | } 21 | 22 | 23 | TEST_CASE("Color enum fmt::format", "[stringify][fmt_format]") 24 | { 25 | CHECK(fmt::format("{}", Color::Green) == "Green"); 26 | CHECK(fmt::format("{}", Color::Red) == "Red"); 27 | CHECK(fmt::format("{}", Color::Blue) == "Blue"); 28 | CHECK(fmt::format("{}", Color::Purple) == "Purple"); 29 | CHECK(fmt::format("{}", Color::Aqua) == "Aqua"); 30 | } 31 | 32 | TEST_CASE("UnscopedColor enum fmt::format", "[stringify][fmt_format]") 33 | { 34 | CHECK(fmt::format("{}", UnscopedColor::Green) == "Green"); 35 | CHECK(fmt::format("{}", UnscopedColor::Red) == "Red"); 36 | CHECK(fmt::format("{}", UnscopedColor::Blue) == "Blue"); 37 | CHECK(fmt::format("{}", UnscopedColor::Purple) == "Purple"); 38 | CHECK(fmt::format("{}", UnscopedColor::Aqua) == "Aqua"); 39 | } 40 | 41 | TEST_CASE("Flags enum fmt::format", "[stringify][fmt_format]") 42 | { 43 | SECTION("Normal fmt::format") 44 | { 45 | CHECK(fmt::format("{}", Flags::Flag0) == "Flag0"); 46 | CHECK(fmt::format("{}", Flags::Flag1) == "Flag1"); 47 | CHECK(fmt::format("{}", Flags::Flag2) == "Flag2"); 48 | CHECK(fmt::format("{}", Flags::Flag3) == "Flag3"); 49 | CHECK(fmt::format("{}", Flags::Flag4) == "Flag4"); 50 | } 51 | SECTION("fmt::format with enchantum::to_string_bitflag") 52 | { 53 | CHECK(fmt::format("{}", Flags::Flag0 | Flags::Flag4) == "Flag0|Flag4"); 54 | CHECK(fmt::format("{}", Flags::Flag0 | Flags::Flag4 | Flags(200)) == "217"); 55 | CHECK(fmt::format("{}", enchantum::value_ors) == "Flag0|Flag1|Flag2|Flag3|Flag4|Flag5|Flag6"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/functors.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | void transform_unwrap(It begin, const It end, Out out, const Func f) 10 | { 11 | while (begin != end) 12 | *out++ = f(**begin++); 13 | } 14 | 15 | TEMPLATE_LIST_TEST_CASE("cast is a functor", "[functors]", AllEnumsTestTypes) 16 | { 17 | constexpr auto& strings = enchantum::names; 18 | std::array, strings.size()> values; 19 | 20 | std::transform(strings.begin(), strings.end(), values.begin(), enchantum::cast); 21 | 22 | CHECK(std::equal(enchantum::values.begin(), enchantum::values.end(), values.begin())); 23 | } 24 | 25 | TEMPLATE_LIST_TEST_CASE("enum_to_index and index_to_enum are functors", "[functors]", AllEnumsTestTypes) 26 | { 27 | constexpr auto& values = enchantum::values; 28 | std::array, values.size()> indices; 29 | std::array, values.size()> values_transformed; 30 | 31 | std::transform(values.begin(), values.end(), indices.begin(), enchantum::enum_to_index); 32 | transform_unwrap(indices.begin(), indices.end(), values_transformed.begin(), enchantum::index_to_enum); 33 | 34 | for (std::size_t i = 0; i < indices.size(); ++i) { 35 | CHECK(indices[i] == i); 36 | } 37 | 38 | for (std::size_t i = 0; i < values_transformed.size(); ++i) { 39 | CHECK(values_transformed[i] == enchantum::values[i]); 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /tests/istream.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | using namespace enchantum::istream_operators; 10 | TEMPLATE_LIST_TEST_CASE("istream operator>>", "[istream]", AllEnumsTestTypes) 11 | { 12 | SECTION("Parses correctly") 13 | { 14 | for (const auto& [value, string] : enchantum::entries) { 15 | auto iss = std::istringstream(std::string(string)); 16 | TestType input{}; 17 | iss >> input; 18 | CHECK(input == value); 19 | CHECK(iss); 20 | } 21 | } 22 | 23 | SECTION("Invalid names") 24 | { 25 | for (const auto& [value, string] : enchantum::entries) { 26 | auto iss = std::istringstream("WorldHello" + std::string(string) + "HelloWorld"); 27 | TestType input{}; 28 | iss >> input; 29 | CHECK(input == TestType{}); 30 | CHECK_FALSE(iss); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/next_value.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | TEMPLATE_LIST_TEST_CASE("next_value identities", "[next_value]", AllEnumsTestTypes) 8 | { 9 | constexpr auto count = enchantum::count; 10 | for (const auto v : enchantum::values) { 11 | CHECK(v == enchantum::next_value_circular(v, count)); 12 | CHECK(v == enchantum::prev_value_circular(v, count)); 13 | 14 | std::optional value = v; 15 | for (std::size_t i = 0; i < count - *enchantum::enum_to_index(v); ++i) 16 | value = enchantum::next_value(*value, 1); 17 | CHECK_FALSE(value.has_value()); 18 | 19 | CHECK_FALSE(enchantum::next_value(v, count).has_value()); 20 | CHECK_FALSE(enchantum::prev_value(v, count).has_value()); 21 | } 22 | } 23 | 24 | TEST_CASE("Color next_value", "[next_value]") 25 | { 26 | STATIC_REQUIRE(enchantum::next_value(Color::Aqua) == Color::Purple); 27 | STATIC_REQUIRE(enchantum::next_value(Color::Purple) == Color::Green); 28 | STATIC_REQUIRE(enchantum::next_value(Color::Green) == Color::Red); 29 | STATIC_REQUIRE(enchantum::next_value(Color::Red) == Color::Blue); 30 | STATIC_REQUIRE_FALSE(enchantum::next_value(Color::Blue).has_value()); 31 | } 32 | 33 | TEST_CASE("Color prev_value", "[prev_value]") 34 | { 35 | STATIC_REQUIRE(enchantum::prev_value(Color::Blue) == Color::Red); 36 | STATIC_REQUIRE(enchantum::prev_value(Color::Red) == Color::Green); 37 | STATIC_REQUIRE(enchantum::prev_value(Color::Green) == Color::Purple); 38 | STATIC_REQUIRE(enchantum::prev_value(Color::Purple) == Color::Aqua); 39 | STATIC_REQUIRE_FALSE(enchantum::prev_value(Color::Aqua).has_value()); 40 | } 41 | 42 | TEST_CASE("Flags next_value", "[next_value]") 43 | { 44 | STATIC_REQUIRE(enchantum::next_value(Flags::Flag0) == Flags::Flag1); 45 | STATIC_REQUIRE(enchantum::next_value(Flags::Flag1) == Flags::Flag2); 46 | STATIC_REQUIRE(enchantum::next_value(Flags::Flag2) == Flags::Flag3); 47 | STATIC_REQUIRE(enchantum::next_value(Flags::Flag3) == Flags::Flag4); 48 | STATIC_REQUIRE(enchantum::next_value(Flags::Flag4) == Flags::Flag5); 49 | STATIC_REQUIRE(enchantum::next_value(Flags::Flag5) == Flags::Flag6); 50 | STATIC_REQUIRE_FALSE(enchantum::next_value(Flags::Flag6).has_value()); 51 | } 52 | 53 | TEST_CASE("Flags prev_value", "[prev_value]") 54 | { 55 | STATIC_REQUIRE(enchantum::prev_value(Flags::Flag6) == Flags::Flag5); 56 | STATIC_REQUIRE(enchantum::prev_value(Flags::Flag1) == Flags::Flag0); 57 | STATIC_REQUIRE_FALSE(enchantum::prev_value(Flags::Flag0).has_value()); 58 | } 59 | 60 | TEST_CASE("UnscopedColor next_value", "[next_value]") 61 | { 62 | STATIC_REQUIRE(enchantum::next_value(UnscopedColor::Aqua) == UnscopedColor::Purple); 63 | STATIC_REQUIRE(enchantum::next_value(UnscopedColor::Purple) == UnscopedColor::Green); 64 | STATIC_REQUIRE(enchantum::next_value(UnscopedColor::Red) == UnscopedColor::Blue); 65 | STATIC_REQUIRE_FALSE(enchantum::next_value(UnscopedColor::Blue).has_value()); 66 | } 67 | 68 | TEST_CASE("UnscopedColor prev_value", "[prev_value]") 69 | { 70 | STATIC_REQUIRE(enchantum::prev_value(UnscopedColor::Blue) == UnscopedColor::Red); 71 | STATIC_REQUIRE(enchantum::prev_value(UnscopedColor::Green) == UnscopedColor::Purple); 72 | STATIC_REQUIRE_FALSE(enchantum::prev_value(UnscopedColor::Aqua).has_value()); 73 | } 74 | 75 | TEST_CASE("Color next_value_circular", "[next_value_circular]") 76 | { 77 | STATIC_REQUIRE(enchantum::next_value_circular(Color::Aqua) == Color::Purple); 78 | STATIC_REQUIRE(enchantum::next_value_circular(Color::Purple) == Color::Green); 79 | STATIC_REQUIRE(enchantum::next_value_circular(Color::Green) == Color::Red); 80 | STATIC_REQUIRE(enchantum::next_value_circular(Color::Red) == Color::Blue); 81 | STATIC_REQUIRE(enchantum::next_value_circular(Color::Blue) == Color::Aqua); 82 | } 83 | 84 | TEST_CASE("Flags next_value_circular", "[next_value_circular]") 85 | { 86 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag0) == Flags::Flag1); 87 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag1) == Flags::Flag2); 88 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag2) == Flags::Flag3); 89 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag3) == Flags::Flag4); 90 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag4) == Flags::Flag5); 91 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag5) == Flags::Flag6); 92 | STATIC_REQUIRE(enchantum::next_value_circular(Flags::Flag6) == Flags::Flag0); 93 | } 94 | 95 | TEST_CASE("UnscopedColor next_value_circular", "[next_value_circular]") 96 | { 97 | STATIC_REQUIRE(enchantum::next_value_circular(UnscopedColor::Aqua) == UnscopedColor::Purple); 98 | STATIC_REQUIRE(enchantum::next_value_circular(UnscopedColor::Purple) == UnscopedColor::Green); 99 | STATIC_REQUIRE(enchantum::next_value_circular(UnscopedColor::Green) == UnscopedColor::Red); 100 | STATIC_REQUIRE(enchantum::next_value_circular(UnscopedColor::Red) == UnscopedColor::Blue); 101 | STATIC_REQUIRE(enchantum::next_value_circular(UnscopedColor::Blue) == UnscopedColor::Aqua); 102 | } 103 | 104 | TEST_CASE("Color prev_value_circular", "[prev_value_circular]") 105 | { 106 | STATIC_REQUIRE(enchantum::prev_value_circular(Color::Blue) == Color::Red); 107 | STATIC_REQUIRE(enchantum::prev_value_circular(Color::Red) == Color::Green); 108 | STATIC_REQUIRE(enchantum::prev_value_circular(Color::Green) == Color::Purple); 109 | STATIC_REQUIRE(enchantum::prev_value_circular(Color::Purple) == Color::Aqua); 110 | STATIC_REQUIRE(enchantum::prev_value_circular(Color::Aqua) == Color::Blue); 111 | } 112 | 113 | TEST_CASE("Flags prev_value_circular", "[prev_value_circular]") 114 | { 115 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag6) == Flags::Flag5); 116 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag5) == Flags::Flag4); 117 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag4) == Flags::Flag3); 118 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag3) == Flags::Flag2); 119 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag2) == Flags::Flag1); 120 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag1) == Flags::Flag0); 121 | STATIC_REQUIRE(enchantum::prev_value_circular(Flags::Flag0) == Flags::Flag6); 122 | } 123 | 124 | TEST_CASE("UnscopedColor prev_value_circular", "[prev_value_circular]") 125 | { 126 | STATIC_REQUIRE(enchantum::prev_value_circular(UnscopedColor::Blue) == UnscopedColor::Red); 127 | STATIC_REQUIRE(enchantum::prev_value_circular(UnscopedColor::Red) == UnscopedColor::Green); 128 | STATIC_REQUIRE(enchantum::prev_value_circular(UnscopedColor::Green) == UnscopedColor::Purple); 129 | STATIC_REQUIRE(enchantum::prev_value_circular(UnscopedColor::Purple) == UnscopedColor::Aqua); 130 | STATIC_REQUIRE(enchantum::prev_value_circular(UnscopedColor::Aqua) == UnscopedColor::Blue); 131 | } 132 | -------------------------------------------------------------------------------- /tests/null_terminated.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | TEMPLATE_LIST_TEST_CASE("entries strings null-terminated character arrays", "[entries][null_termination]", AllEnumsTestTypes) 8 | { 9 | SECTION("Null terminated") 10 | { 11 | for (const auto& s : enchantum::names) { 12 | CHECK(s.size() == std::strlen(s.data())); 13 | } 14 | } 15 | 16 | SECTION("Not Null Terminated") 17 | { 18 | constexpr auto names = enchantum::names; 19 | const auto* names_ptr = names[0].data(); // implementation detail but all strings are allocated next to each other 20 | for (std::ptrdiff_t i = 0; i < static_cast(names.size()) - 1; ++i) { 21 | const auto& name = names[static_cast(i)]; 22 | CHECK(*names_ptr != '\0'); 23 | names_ptr += name.size(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/ostream.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace enchantum::ostream_operators; 9 | 10 | TEMPLATE_LIST_TEST_CASE("ostream operator<<", "[ostream]", AllEnumsTestTypes) 11 | { 12 | SECTION("Valid enums return to_string") 13 | { 14 | for (const auto& [value, string] : enchantum::entries) { 15 | auto oss = std::ostringstream(); 16 | oss << value; 17 | CHECK(oss.str() == string); 18 | CHECK(oss); 19 | } 20 | } 21 | } 22 | 23 | TEST_CASE("ostream operator<< return numbers if invalid enum", "[ostream]") 24 | { 25 | const auto tostringoss = [](auto x) { 26 | auto oss = std::ostringstream(); 27 | oss << x; 28 | return std::move(oss).str(); 29 | }; 30 | 31 | CHECK(tostringoss(Color::Red) == "Red"); 32 | CHECK(tostringoss(Color(300)) == "300"); 33 | CHECK(tostringoss(Color(-300)) == "-300"); 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /tests/prefix_length.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | enum class ImGuiFreeTypeBuilderFlags_Classy { 9 | NoHinting = 1 << 0, 10 | NoAutoHint = 1 << 1, 11 | ForceAutoHint = 1 << 2, 12 | LightHinting = 1 << 3, 13 | MonoHinting = 1 << 4, 14 | Bold = 1 << 5, 15 | Oblique = 1 << 6, 16 | Monochrome = 1 << 7, 17 | LoadColor = 1 << 8, 18 | Bitmap = 1 << 9 19 | }; 20 | ENCHANTUM_DEFINE_BITWISE_FOR(ImGuiFreeTypeBuilderFlags_Classy) 21 | 22 | 23 | TEST_CASE("prefix_length", "[count]") 24 | { 25 | constexpr auto count = enchantum::count; 26 | STATIC_CHECK(count == enchantum::count); 27 | for (std::size_t i = 0; i < count; ++i) { 28 | CHECK(enchantum::names[i] == enchantum::names[i]); 29 | } 30 | } -------------------------------------------------------------------------------- /tests/scoped.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | TEMPLATE_LIST_TEST_CASE("scoped::to_string", "[stringify]", AllEnumsTestTypes) 8 | { 9 | for (const auto v : enchantum::values) { 10 | const auto string = enchantum::scoped::to_string(v); 11 | REQUIRE(string.find("::") != std::string::npos); 12 | REQUIRE(string.find("::") == string.rfind("::")); 13 | 14 | CHECK(enchantum::scoped::cast(string).value() == v); 15 | CHECK(enchantum::scoped::contains(string)); 16 | CHECK_FALSE(enchantum::scoped::cast(enchantum::to_string(v))); 17 | CHECK_FALSE(enchantum::scoped::cast("::" + std::string(enchantum::to_string(v)))); 18 | CHECK_FALSE(enchantum::cast(string)); 19 | } 20 | } 21 | 22 | TEST_CASE("scoped::to_string") 23 | { 24 | SECTION("Color") 25 | { 26 | CHECK(enchantum::scoped::to_string(Color::Red) == "Color::Red"); 27 | CHECK(enchantum::scoped::to_string(Color::Aqua) == "Color::Aqua"); 28 | CHECK(enchantum::scoped::to_string(Color::Purple) == "Color::Purple"); 29 | CHECK(enchantum::scoped::to_string(Color::Green) == "Color::Green"); 30 | CHECK(enchantum::scoped::to_string(Color::Blue) == "Color::Blue"); 31 | } 32 | 33 | SECTION("Flags") 34 | { 35 | CHECK(enchantum::scoped::to_string(Flags::Flag0) == "Flags::Flag0"); 36 | CHECK(enchantum::scoped::to_string(Flags::Flag1) == "Flags::Flag1"); 37 | CHECK(enchantum::scoped::to_string(Flags::Flag2) == "Flags::Flag2"); 38 | CHECK(enchantum::scoped::to_string(Flags::Flag3) == "Flags::Flag3"); 39 | CHECK(enchantum::scoped::to_string(Flags::Flag4) == "Flags::Flag4"); 40 | CHECK(enchantum::scoped::to_string(Flags::Flag5) == "Flags::Flag5"); 41 | CHECK(enchantum::scoped::to_string(Flags::Flag6) == "Flags::Flag6"); 42 | } 43 | 44 | SECTION("UnscopedColor") 45 | { 46 | CHECK(enchantum::scoped::to_string(UnscopedColor::Aqua) == "UnscopedColor::Aqua"); 47 | CHECK(enchantum::scoped::to_string(UnscopedColor::Purple) == "UnscopedColor::Purple"); 48 | CHECK(enchantum::scoped::to_string(UnscopedColor::Green) == "UnscopedColor::Green"); 49 | CHECK(enchantum::scoped::to_string(UnscopedColor::Red) == "UnscopedColor::Red"); 50 | CHECK(enchantum::scoped::to_string(UnscopedColor::Blue) == "UnscopedColor::Blue"); 51 | } 52 | 53 | 54 | SECTION("ImGuiFreeTypeBuilderFlags") 55 | { 56 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_NoHinting) == 57 | "ImGuiFreeTypeBuilderFlags::NoHinting"); 58 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_NoAutoHint) == 59 | "ImGuiFreeTypeBuilderFlags::NoAutoHint"); 60 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_ForceAutoHint) == 61 | "ImGuiFreeTypeBuilderFlags::ForceAutoHint"); 62 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_LightHinting) == 63 | "ImGuiFreeTypeBuilderFlags::LightHinting"); 64 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_MonoHinting) == 65 | "ImGuiFreeTypeBuilderFlags::MonoHinting"); 66 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_Bold) == 67 | "ImGuiFreeTypeBuilderFlags::Bold"); 68 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_Oblique) == 69 | "ImGuiFreeTypeBuilderFlags::Oblique"); 70 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_Monochrome) == 71 | "ImGuiFreeTypeBuilderFlags::Monochrome"); 72 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_LoadColor) == 73 | "ImGuiFreeTypeBuilderFlags::LoadColor"); 74 | CHECK(enchantum::scoped::to_string(ImGuiFreeTypeBuilderFlags::ImGuiFreeTypeBuilderFlags_Bitmap) == 75 | "ImGuiFreeTypeBuilderFlags::Bitmap"); 76 | } 77 | 78 | 79 | SECTION("MinMaxValuesCStyle") 80 | { 81 | CHECK(enchantum::scoped::to_string(MinMaxValuesCStyle::MinMaxValuesCStyle_min) == 82 | "MinMaxValuesCStyle::MinMaxValuesCStyle_min"); 83 | CHECK(enchantum::scoped::to_string(MinMaxValuesCStyle::MinMaxValuesCStyle_max) == 84 | "MinMaxValuesCStyle::MinMaxValuesCStyle_max"); 85 | } 86 | 87 | SECTION("MinMaxValues") 88 | { 89 | CHECK(enchantum::scoped::to_string(MinMaxValues::min) == "MinMaxValues::min"); 90 | CHECK(enchantum::scoped::to_string(MinMaxValues::max) == "MinMaxValues::max"); 91 | } 92 | 93 | 94 | SECTION("Letters") 95 | { 96 | for (const auto letter : enchantum::values) { 97 | CHECK(enchantum::scoped::to_string(letter) == "Letters::" + std::string(enchantum::to_string(letter))); 98 | } 99 | } 100 | } 101 | 102 | TEMPLATE_LIST_TEST_CASE("scoped::cast_bitflag identities", "", AllFlagsTestTypes) 103 | { 104 | constexpr auto value_ors = enchantum::value_ors; 105 | CHECK(value_ors == enchantum::scoped::cast_bitflag(enchantum::scoped::to_string_bitflag(value_ors))); 106 | CHECK(value_ors == enchantum::scoped::cast_bitflag(enchantum::scoped::to_string_bitflag(value_ors, ','),',')); 107 | } 108 | 109 | TEST_CASE("scoped::to_string_bitflag") 110 | { 111 | CHECK(enchantum::scoped::to_string_bitflag(enchantum::value_ors) == 112 | "Flags::Flag0|Flags::Flag1|Flags::Flag2|Flags::Flag3|Flags::Flag4|Flags::Flag5|Flags::Flag6"); 113 | CHECK(enchantum::scoped::to_string_bitflag(enchantum::value_ors, ',') == 114 | "Flags::Flag0,Flags::Flag1,Flags::Flag2,Flags::Flag3,Flags::Flag4,Flags::Flag5,Flags::Flag6"); 115 | } -------------------------------------------------------------------------------- /tests/std_format.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | TEMPLATE_LIST_TEST_CASE("std::format", "[stringify][std_format]", AllEnumsTestTypes) 9 | { 10 | 11 | for (const auto& [value, string] : enchantum::entries) 12 | CHECK(std::format("{}", value) == string); 13 | using T = std::underlying_type_t; 14 | if constexpr (!enchantum::is_bitflag && !std::is_same_v>) 15 | if constexpr (123 <= T(enchantum::max) && !enchantum::contains(123)) 16 | CHECK("123" == std::format("{}", TestType(123))); 17 | } 18 | 19 | 20 | TEST_CASE("Color enum std::format", "[stringify][std_format]") 21 | { 22 | CHECK(std::format("{}", Color::Green) == "Green"); 23 | CHECK(std::format("{}", Color::Red) == "Red"); 24 | CHECK(std::format("{}", Color::Blue) == "Blue"); 25 | CHECK(std::format("{}", Color::Purple) == "Purple"); 26 | CHECK(std::format("{}", Color::Aqua) == "Aqua"); 27 | } 28 | 29 | TEST_CASE("UnscopedColor enum std::format", "[stringify][std_format]") 30 | { 31 | CHECK(std::format("{}", UnscopedColor::Green) == "Green"); 32 | CHECK(std::format("{}", UnscopedColor::Red) == "Red"); 33 | CHECK(std::format("{}", UnscopedColor::Blue) == "Blue"); 34 | CHECK(std::format("{}", UnscopedColor::Purple) == "Purple"); 35 | CHECK(std::format("{}", UnscopedColor::Aqua) == "Aqua"); 36 | } 37 | 38 | TEST_CASE("Flags enum std::format", "[stringify][std_format]") 39 | { 40 | SECTION("Normal std::format") 41 | { 42 | CHECK(std::format("{}", Flags::Flag0) == "Flag0"); 43 | CHECK(std::format("{}", Flags::Flag1) == "Flag1"); 44 | CHECK(std::format("{}", Flags::Flag2) == "Flag2"); 45 | CHECK(std::format("{}", Flags::Flag3) == "Flag3"); 46 | CHECK(std::format("{}", Flags::Flag4) == "Flag4"); 47 | } 48 | SECTION("std::format with enchantum::to_string_bitflag") 49 | { 50 | CHECK(std::format("{}", Flags::Flag0 | Flags::Flag4) == "Flag0|Flag4"); 51 | CHECK(std::format("{}", Flags::Flag0 | Flags::Flag4 | Flags(200)) == "217"); 52 | CHECK(std::format("{}", enchantum::value_ors) == "Flag0|Flag1|Flag2|Flag3|Flag4|Flag5|Flag6"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/test_utility.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "case_insensitive.hpp" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | template 12 | struct TypeWithCommas; 13 | 14 | // Tests whether string parsing will break if commas occur in typename 15 | namespace LongNamespaced::Namespace2::inline InlineNamespace { 16 | template 17 | struct Really_Unreadable_Class_Name { 18 | 19 | enum class Color : std::int16_t { 20 | Aqua = -42, 21 | Purple = 21, 22 | Green = 124, 23 | Red = 213, 24 | Blue, 25 | }; 26 | 27 | 28 | enum class Flags : std::uint8_t { 29 | Flag0 = 1 << 0, 30 | Flag1 = 1 << 1, 31 | Flag2 = 1 << 2, 32 | Flag3 = 1 << 3, 33 | Flag4 = 1 << 4, 34 | Flag5 = 1 << 5, 35 | Flag6 = 1 << 6, 36 | }; 37 | 38 | enum UnscopedColor : std::int64_t { 39 | Aqua = -42, 40 | Purple = 21, 41 | Green = 124, 42 | Red = 213, 43 | Blue, 44 | }; 45 | 46 | enum BlockType_ { 47 | Dirt = -42, 48 | Stone = 21, 49 | Obsidian = 124, 50 | }; 51 | using BlockType = decltype(BlockType_::Dirt); 52 | }; 53 | 54 | //template 55 | //[[nodiscard]] constexpr auto operator~(typename Really_Unreadable_Class_Name::Flags a) noexcept 56 | //{ 57 | // using T = std::underlying_type_t; 58 | // return static_cast(~static_cast(a)); 59 | //} 60 | // 61 | //template 62 | //[[nodiscard]] constexpr auto operator|(typename Really_Unreadable_Class_Name::Flags a, 63 | // typename Really_Unreadable_Class_Name::Flags b) noexcept 64 | //{ 65 | // using T = std::underlying_type_t; 66 | // return static_cast(static_cast(a) | static_cast(b)); 67 | //} 68 | // 69 | //template 70 | //[[nodiscard]] constexpr auto operator&(typename Really_Unreadable_Class_Name::Flags a, 71 | // typename Really_Unreadable_Class_Name::Flags b) noexcept 72 | //{ 73 | // using T = std::underlying_type_t; 74 | // return static_cast(static_cast(a) & static_cast(b)); 75 | //} 76 | // 77 | //template 78 | //constexpr auto& operator|=(typename Really_Unreadable_Class_Name::Flags& a, 79 | // typename Really_Unreadable_Class_Name::Flags b) noexcept 80 | //{ 81 | // return a = a | b; 82 | //} 83 | 84 | //template 85 | //constexpr auto& operator&=(typename Really_Unreadable_Class_Name::Flags& a, 86 | // typename Really_Unreadable_Class_Name::Flags b) noexcept 87 | //{ 88 | // return a = a & b; 89 | //} 90 | 91 | using UglyType = Really_Unreadable_Class_Name>>; 92 | using BlockType = UglyType::BlockType; 93 | 94 | 95 | // Clang seems to have weird behavior with enum in templates 96 | // It does not display them in pretty function names unless atleast 1 member of the enum was 97 | // used. 98 | #ifdef __clang__ 99 | using Color = decltype(UglyType::Color::Aqua); 100 | using Flags = decltype(UglyType::Flags::Flag0); 101 | using UnscopedColor = decltype(UglyType::UnscopedColor::Aqua); 102 | #else 103 | using Color = UglyType::Color; 104 | using Flags = UglyType::Flags; 105 | using UnscopedColor = UglyType::UnscopedColor; 106 | #endif 107 | 108 | ENCHANTUM_DEFINE_BITWISE_FOR(Flags) 109 | 110 | } // namespace LongNamespaced::Namespace2::inline InlineNamespace 111 | 112 | // taken from ImGui. 113 | // https://github.com/ocornut/imgui/blob/46235e91f602b663f9b0f1f1a300177b61b193f5/misc/freetype/imgui_freetype.h#L26 114 | 115 | enum ImGuiFreeTypeBuilderFlags { 116 | ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, 117 | ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, 118 | ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, 119 | ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, 120 | ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, 121 | ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, 122 | ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, 123 | ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, 124 | ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, 125 | ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 126 | }; 127 | 128 | template<> 129 | struct enchantum::enum_traits { 130 | static constexpr std::size_t prefix_length = sizeof("ImGuiFreeTypeBuilderFlags_") - 1; 131 | static constexpr auto min = ENCHANTUM_MIN_RANGE; 132 | static constexpr auto max = ENCHANTUM_MAX_RANGE; 133 | }; 134 | template<> 135 | inline constexpr bool enchantum::is_bitflag = true; 136 | 137 | namespace { 138 | 139 | enum MinMaxValuesCStyle { 140 | MinMaxValuesCStyle_min = ENCHANTUM_MIN_RANGE, 141 | MinMaxValuesCStyle_max = ENCHANTUM_MAX_RANGE, 142 | }; 143 | 144 | enum class MinMaxValues { 145 | min = ENCHANTUM_MIN_RANGE, 146 | max = ENCHANTUM_MAX_RANGE, 147 | }; 148 | 149 | enum class ContigNonZero { 150 | _0 = -10, 151 | _1, 152 | _2, 153 | _3, 154 | _4, 155 | _5, 156 | _6, 157 | _7, 158 | _8, 159 | _9, 160 | }; 161 | 162 | enum ContigNonZeroCStyle { 163 | ContigNonZeroCStyle_0 = -1, 164 | ContigNonZeroCStyle_1, 165 | ContigNonZeroCStyle_2, 166 | ContigNonZeroCStyle_3, 167 | ContigNonZeroCStyle_4, 168 | ContigNonZeroCStyle_5, 169 | ContigNonZeroCStyle_6, 170 | ContigNonZeroCStyle_7, 171 | ContigNonZeroCStyle_8, 172 | ContigNonZeroCStyle_9, 173 | }; 174 | 175 | 176 | enum ContigNonZeroStartWith5CStyle { 177 | ContigNonZeroStartWith5CStyle_0 = 5, 178 | ContigNonZeroStartWith5CStyle_1, 179 | ContigNonZeroStartWith5CStyle_2, 180 | ContigNonZeroStartWith5CStyle_3, 181 | ContigNonZeroStartWith5CStyle_4, 182 | ContigNonZeroStartWith5CStyle_5, 183 | ContigNonZeroStartWith5CStyle_6, 184 | ContigNonZeroStartWith5CStyle_7, 185 | ContigNonZeroStartWith5CStyle_8, 186 | ContigNonZeroStartWith5CStyle_9, 187 | }; 188 | 189 | enum class UnderlyingTypeWChar_t : wchar_t { 190 | _0, 191 | _1, 192 | _2, 193 | _3, 194 | _4, 195 | _5, 196 | _6, 197 | _7, 198 | _8, 199 | _9, 200 | }; 201 | enum class UnderlyingTypeChar16_t : char16_t { 202 | _0, 203 | _1, 204 | _2, 205 | _3, 206 | _4, 207 | _5, 208 | _6, 209 | _7, 210 | _8, 211 | _9, 212 | }; 213 | enum class UnderlyingTypeChar32_t : char32_t { 214 | _0, 215 | _1, 216 | _2, 217 | _3, 218 | _4, 219 | _5, 220 | _6, 221 | _7, 222 | _8, 223 | _9, 224 | }; 225 | #ifdef __cpp_char8_t 226 | enum class UnderlyingTypeChar8_t : char8_t { 227 | _0, 228 | _1, 229 | _2, 230 | _3, 231 | _4, 232 | _5, 233 | _6, 234 | _7, 235 | _8, 236 | _9, 237 | }; 238 | #endif 239 | enum class Letters { 240 | a, 241 | b, 242 | c, 243 | d, 244 | e, 245 | f, 246 | g, 247 | h, 248 | i, 249 | j, 250 | k, 251 | l, 252 | m, 253 | n, 254 | o, 255 | p, 256 | q, 257 | r, 258 | s, 259 | t, 260 | u, 261 | v, 262 | w, 263 | x, 264 | y, 265 | z 266 | }; 267 | } // namespace 268 | 269 | enum NonContigFlagsWithNoneCStyle : unsigned char { 270 | None = 0, 271 | Flag0 = 1 << 0, 272 | Flag1 = 1 << 1, 273 | Flag2 = 1 << 2, 274 | Flag6 = 1 << 6, 275 | }; 276 | ENCHANTUM_DEFINE_BITWISE_FOR(NonContigFlagsWithNoneCStyle) 277 | 278 | enum class FlagsWithNone : unsigned char { 279 | None = 0, 280 | Flag0 = 1 << 0, 281 | Flag1 = 1 << 1, 282 | Flag2 = 1 << 2, 283 | Flag3 = 1 << 3, 284 | Flag4 = 1 << 4, 285 | Flag5 = 1 << 5, 286 | Flag6 = 1 << 6, 287 | Flag7 = 1 << 7, 288 | }; 289 | ENCHANTUM_DEFINE_BITWISE_FOR(FlagsWithNone) 290 | 291 | enum class Direction2D : std::uint8_t { 292 | None = 0, 293 | Left = 1, 294 | Right = 3, 295 | Up = 2, 296 | Down = 4, 297 | 298 | East = Right, 299 | West = Left, 300 | North = Up, 301 | South = Down 302 | }; 303 | 304 | enum class Direction3D : std::int16_t { 305 | None = 0, 306 | Left = 1, 307 | Right = 3, 308 | Up = 2, 309 | Down = 4, 310 | Front = 5, 311 | Back = 7, 312 | 313 | Top = Up, 314 | }; 315 | 316 | 317 | enum Unscoped : int { 318 | }; 319 | 320 | enum UnscopedCStyle { 321 | Unscoped_CStyle_Val0 = -11, 322 | Unscoped_CStyle_Value1 = 0, 323 | Unscoped_CStyle_Value2 = 55, 324 | Unscoped_CStyle_Value3 = 32, 325 | Unscoped_CStyle_Value4 = 11, 326 | }; 327 | 328 | 329 | enum class BoolEnum : bool { 330 | False, 331 | True 332 | }; 333 | 334 | enum BoolEnumCStyle : bool { 335 | BoolEnumCStyle_False, 336 | BoolEnumCStyle_True 337 | }; 338 | 339 | 340 | using namespace LongNamespaced::Namespace2; 341 | template 342 | struct type_list {}; // not wanting to include tuple to detect if I am missing a header in tests 343 | 344 | template 345 | using concat = decltype([](type_list, type_list) { 346 | return type_list{}; 347 | }(A{}, B{})); 348 | 349 | using AllFlagsTestTypes = type_list; 350 | using AllEnumsTestTypes = concat< 351 | AllFlagsTestTypes, 352 | type_list< 353 | #ifdef __cpp_char8_t 354 | UnderlyingTypeChar8_t, 355 | #endif 356 | UnderlyingTypeChar16_t, 357 | UnderlyingTypeChar32_t, 358 | UnderlyingTypeWChar_t, 359 | 360 | MinMaxValuesCStyle, 361 | BlockType, 362 | ContigNonZeroStartWith5CStyle, 363 | BoolEnumCStyle, 364 | ContigNonZeroCStyle, 365 | ContigNonZero, 366 | MinMaxValues, 367 | Color, 368 | UnscopedColor, 369 | UnscopedCStyle, 370 | BoolEnum, 371 | Direction2D, 372 | Direction3D, 373 | Letters>>; 374 | 375 | template 376 | struct Catch::StringMaker { 377 | static std::string convert(E e) 378 | { 379 | if constexpr (enchantum::BitFlagEnum) 380 | return "\"" + enchantum::to_string_bitflag(e) + "\" (" + 381 | std::to_string(static_cast>(e)) + ")"; 382 | else 383 | return "\"" + std::string(enchantum::to_string(e)) + "\" (" + 384 | std::to_string(static_cast>(e)) + ")"; 385 | } 386 | }; 387 | -------------------------------------------------------------------------------- /tests/third_party/fmt/args.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - dynamic argument lists 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_ARGS_H_ 9 | #define FMT_ARGS_H_ 10 | 11 | #ifndef FMT_MODULE 12 | # include // std::reference_wrapper 13 | # include // std::unique_ptr 14 | # include 15 | #endif 16 | 17 | #include "format.h" // std_string_view 18 | 19 | FMT_BEGIN_NAMESPACE 20 | namespace detail { 21 | 22 | template struct is_reference_wrapper : std::false_type {}; 23 | template 24 | struct is_reference_wrapper> : std::true_type {}; 25 | 26 | template auto unwrap(const T& v) -> const T& { return v; } 27 | template 28 | auto unwrap(const std::reference_wrapper& v) -> const T& { 29 | return static_cast(v); 30 | } 31 | 32 | // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC 33 | // 2022 (v17.10.0). 34 | // 35 | // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for 36 | // templates it doesn't complain about inability to deduce single translation 37 | // unit for placing vtable. So node is made a fake template. 38 | template struct node { 39 | virtual ~node() = default; 40 | std::unique_ptr> next; 41 | }; 42 | 43 | class dynamic_arg_list { 44 | template struct typed_node : node<> { 45 | T value; 46 | 47 | template 48 | FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} 49 | 50 | template 51 | FMT_CONSTEXPR typed_node(const basic_string_view& arg) 52 | : value(arg.data(), arg.size()) {} 53 | }; 54 | 55 | std::unique_ptr> head_; 56 | 57 | public: 58 | template auto push(const Arg& arg) -> const T& { 59 | auto new_node = std::unique_ptr>(new typed_node(arg)); 60 | auto& value = new_node->value; 61 | new_node->next = std::move(head_); 62 | head_ = std::move(new_node); 63 | return value; 64 | } 65 | }; 66 | } // namespace detail 67 | 68 | /** 69 | * A dynamic list of formatting arguments with storage. 70 | * 71 | * It can be implicitly converted into `fmt::basic_format_args` for passing 72 | * into type-erased formatting functions such as `fmt::vformat`. 73 | */ 74 | template class dynamic_format_arg_store { 75 | private: 76 | using char_type = typename Context::char_type; 77 | 78 | template struct need_copy { 79 | static constexpr detail::type mapped_type = 80 | detail::mapped_type_constant::value; 81 | 82 | enum { 83 | value = !(detail::is_reference_wrapper::value || 84 | std::is_same>::value || 85 | std::is_same>::value || 86 | (mapped_type != detail::type::cstring_type && 87 | mapped_type != detail::type::string_type && 88 | mapped_type != detail::type::custom_type)) 89 | }; 90 | }; 91 | 92 | template 93 | using stored_t = conditional_t< 94 | std::is_convertible>::value && 95 | !detail::is_reference_wrapper::value, 96 | std::basic_string, T>; 97 | 98 | // Storage of basic_format_arg must be contiguous. 99 | std::vector> data_; 100 | std::vector> named_info_; 101 | 102 | // Storage of arguments not fitting into basic_format_arg must grow 103 | // without relocation because items in data_ refer to it. 104 | detail::dynamic_arg_list dynamic_args_; 105 | 106 | friend class basic_format_args; 107 | 108 | auto data() const -> const basic_format_arg* { 109 | return named_info_.empty() ? data_.data() : data_.data() + 1; 110 | } 111 | 112 | template void emplace_arg(const T& arg) { 113 | data_.emplace_back(arg); 114 | } 115 | 116 | template 117 | void emplace_arg(const detail::named_arg& arg) { 118 | if (named_info_.empty()) 119 | data_.insert(data_.begin(), basic_format_arg(nullptr, 0)); 120 | data_.emplace_back(detail::unwrap(arg.value)); 121 | auto pop_one = [](std::vector>* data) { 122 | data->pop_back(); 123 | }; 124 | std::unique_ptr>, decltype(pop_one)> 125 | guard{&data_, pop_one}; 126 | named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); 127 | data_[0] = {named_info_.data(), named_info_.size()}; 128 | guard.release(); 129 | } 130 | 131 | public: 132 | constexpr dynamic_format_arg_store() = default; 133 | 134 | operator basic_format_args() const { 135 | return basic_format_args(data(), static_cast(data_.size()), 136 | !named_info_.empty()); 137 | } 138 | 139 | /** 140 | * Adds an argument into the dynamic store for later passing to a formatting 141 | * function. 142 | * 143 | * Note that custom types and string types (but not string views) are copied 144 | * into the store dynamically allocating memory if necessary. 145 | * 146 | * **Example**: 147 | * 148 | * fmt::dynamic_format_arg_store store; 149 | * store.push_back(42); 150 | * store.push_back("abc"); 151 | * store.push_back(1.5f); 152 | * std::string result = fmt::vformat("{} and {} and {}", store); 153 | */ 154 | template void push_back(const T& arg) { 155 | if (detail::const_check(need_copy::value)) 156 | emplace_arg(dynamic_args_.push>(arg)); 157 | else 158 | emplace_arg(detail::unwrap(arg)); 159 | } 160 | 161 | /** 162 | * Adds a reference to the argument into the dynamic store for later passing 163 | * to a formatting function. 164 | * 165 | * **Example**: 166 | * 167 | * fmt::dynamic_format_arg_store store; 168 | * char band[] = "Rolling Stones"; 169 | * store.push_back(std::cref(band)); 170 | * band[9] = 'c'; // Changing str affects the output. 171 | * std::string result = fmt::vformat("{}", store); 172 | * // result == "Rolling Scones" 173 | */ 174 | template void push_back(std::reference_wrapper arg) { 175 | static_assert( 176 | need_copy::value, 177 | "objects of built-in types and string views are always copied"); 178 | emplace_arg(arg.get()); 179 | } 180 | 181 | /** 182 | * Adds named argument into the dynamic store for later passing to a 183 | * formatting function. `std::reference_wrapper` is supported to avoid 184 | * copying of the argument. The name is always copied into the store. 185 | */ 186 | template 187 | void push_back(const detail::named_arg& arg) { 188 | const char_type* arg_name = 189 | dynamic_args_.push>(arg.name).c_str(); 190 | if (detail::const_check(need_copy::value)) { 191 | emplace_arg( 192 | fmt::arg(arg_name, dynamic_args_.push>(arg.value))); 193 | } else { 194 | emplace_arg(fmt::arg(arg_name, arg.value)); 195 | } 196 | } 197 | 198 | /// Erase all elements from the store. 199 | void clear() { 200 | data_.clear(); 201 | named_info_.clear(); 202 | dynamic_args_ = {}; 203 | } 204 | 205 | /// Reserves space to store at least `new_cap` arguments including 206 | /// `new_cap_named` named arguments. 207 | void reserve(size_t new_cap, size_t new_cap_named) { 208 | FMT_ASSERT(new_cap >= new_cap_named, 209 | "set of arguments includes set of named arguments"); 210 | data_.reserve(new_cap); 211 | named_info_.reserve(new_cap_named); 212 | } 213 | 214 | /// Returns the number of elements in the store. 215 | size_t size() const noexcept { return data_.size(); } 216 | }; 217 | 218 | FMT_END_NAMESPACE 219 | 220 | #endif // FMT_ARGS_H_ 221 | -------------------------------------------------------------------------------- /tests/third_party/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - experimental format string compilation 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich and fmt contributors 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_COMPILE_H_ 9 | #define FMT_COMPILE_H_ 10 | 11 | #ifndef FMT_MODULE 12 | # include // std::back_inserter 13 | #endif 14 | 15 | #include "format.h" 16 | 17 | FMT_BEGIN_NAMESPACE 18 | 19 | // A compile-time string which is compiled into fast formatting code. 20 | FMT_EXPORT class compiled_string {}; 21 | 22 | template 23 | struct is_compiled_string : std::is_base_of {}; 24 | 25 | namespace detail { 26 | 27 | /** 28 | * Converts a string literal `s` into a format string that will be parsed at 29 | * compile time and converted into efficient formatting code. Requires C++17 30 | * `constexpr if` compiler support. 31 | * 32 | * **Example**: 33 | * 34 | * // Converts 42 into std::string using the most efficient method and no 35 | * // runtime format string processing. 36 | * std::string s = fmt::format(FMT_COMPILE("{}"), 42); 37 | */ 38 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 39 | # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string) 40 | #else 41 | # define FMT_COMPILE(s) FMT_STRING(s) 42 | #endif 43 | 44 | template 45 | auto first(const T& value, const Tail&...) -> const T& { 46 | return value; 47 | } 48 | 49 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 50 | template struct type_list {}; 51 | 52 | // Returns a reference to the argument at index N from [first, rest...]. 53 | template 54 | constexpr const auto& get([[maybe_unused]] const T& first, 55 | [[maybe_unused]] const Args&... rest) { 56 | static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); 57 | if constexpr (N == 0) 58 | return first; 59 | else 60 | return detail::get(rest...); 61 | } 62 | 63 | # if FMT_USE_NONTYPE_TEMPLATE_ARGS 64 | template 65 | constexpr auto get_arg_index_by_name(basic_string_view name) -> int { 66 | if constexpr (is_static_named_arg()) { 67 | if (name == T::name) return N; 68 | } 69 | if constexpr (sizeof...(Args) > 0) 70 | return get_arg_index_by_name(name); 71 | (void)name; // Workaround an MSVC bug about "unused" parameter. 72 | return -1; 73 | } 74 | # endif 75 | 76 | template 77 | FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { 78 | # if FMT_USE_NONTYPE_TEMPLATE_ARGS 79 | if constexpr (sizeof...(Args) > 0) 80 | return get_arg_index_by_name<0, Args...>(name); 81 | # endif 82 | (void)name; 83 | return -1; 84 | } 85 | 86 | template 87 | constexpr int get_arg_index_by_name(basic_string_view name, 88 | type_list) { 89 | return get_arg_index_by_name(name); 90 | } 91 | 92 | template struct get_type_impl; 93 | 94 | template struct get_type_impl> { 95 | using type = 96 | remove_cvref_t(std::declval()...))>; 97 | }; 98 | 99 | template 100 | using get_type = typename get_type_impl::type; 101 | 102 | template struct is_compiled_format : std::false_type {}; 103 | 104 | template struct text { 105 | basic_string_view data; 106 | using char_type = Char; 107 | 108 | template 109 | constexpr OutputIt format(OutputIt out, const Args&...) const { 110 | return write(out, data); 111 | } 112 | }; 113 | 114 | template 115 | struct is_compiled_format> : std::true_type {}; 116 | 117 | template 118 | constexpr text make_text(basic_string_view s, size_t pos, 119 | size_t size) { 120 | return {{&s[pos], size}}; 121 | } 122 | 123 | template struct code_unit { 124 | Char value; 125 | using char_type = Char; 126 | 127 | template 128 | constexpr OutputIt format(OutputIt out, const Args&...) const { 129 | *out++ = value; 130 | return out; 131 | } 132 | }; 133 | 134 | // This ensures that the argument type is convertible to `const T&`. 135 | template 136 | constexpr const T& get_arg_checked(const Args&... args) { 137 | const auto& arg = detail::get(args...); 138 | if constexpr (detail::is_named_arg>()) { 139 | return arg.value; 140 | } else { 141 | return arg; 142 | } 143 | } 144 | 145 | template 146 | struct is_compiled_format> : std::true_type {}; 147 | 148 | // A replacement field that refers to argument N. 149 | template struct field { 150 | using char_type = Char; 151 | 152 | template 153 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 154 | const T& arg = get_arg_checked(args...); 155 | if constexpr (std::is_convertible>::value) { 156 | auto s = basic_string_view(arg); 157 | return copy(s.begin(), s.end(), out); 158 | } else { 159 | return write(out, arg); 160 | } 161 | } 162 | }; 163 | 164 | template 165 | struct is_compiled_format> : std::true_type {}; 166 | 167 | // A replacement field that refers to argument with name. 168 | template struct runtime_named_field { 169 | using char_type = Char; 170 | basic_string_view name; 171 | 172 | template 173 | constexpr static bool try_format_argument( 174 | OutputIt& out, 175 | // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 176 | [[maybe_unused]] basic_string_view arg_name, const T& arg) { 177 | if constexpr (is_named_arg::type>::value) { 178 | if (arg_name == arg.name) { 179 | out = write(out, arg.value); 180 | return true; 181 | } 182 | } 183 | return false; 184 | } 185 | 186 | template 187 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 188 | bool found = (try_format_argument(out, name, args) || ...); 189 | if (!found) { 190 | FMT_THROW(format_error("argument with specified name is not found")); 191 | } 192 | return out; 193 | } 194 | }; 195 | 196 | template 197 | struct is_compiled_format> : std::true_type {}; 198 | 199 | // A replacement field that refers to argument N and has format specifiers. 200 | template struct spec_field { 201 | using char_type = Char; 202 | formatter fmt; 203 | 204 | template 205 | constexpr FMT_INLINE OutputIt format(OutputIt out, 206 | const Args&... args) const { 207 | const auto& vargs = 208 | fmt::make_format_args>(args...); 209 | basic_format_context ctx(out, vargs); 210 | return fmt.format(get_arg_checked(args...), ctx); 211 | } 212 | }; 213 | 214 | template 215 | struct is_compiled_format> : std::true_type {}; 216 | 217 | template struct concat { 218 | L lhs; 219 | R rhs; 220 | using char_type = typename L::char_type; 221 | 222 | template 223 | constexpr OutputIt format(OutputIt out, const Args&... args) const { 224 | out = lhs.format(out, args...); 225 | return rhs.format(out, args...); 226 | } 227 | }; 228 | 229 | template 230 | struct is_compiled_format> : std::true_type {}; 231 | 232 | template 233 | constexpr concat make_concat(L lhs, R rhs) { 234 | return {lhs, rhs}; 235 | } 236 | 237 | struct unknown_format {}; 238 | 239 | template 240 | constexpr size_t parse_text(basic_string_view str, size_t pos) { 241 | for (size_t size = str.size(); pos != size; ++pos) { 242 | if (str[pos] == '{' || str[pos] == '}') break; 243 | } 244 | return pos; 245 | } 246 | 247 | template 248 | constexpr auto compile_format_string(S fmt); 249 | 250 | template 251 | constexpr auto parse_tail(T head, S fmt) { 252 | if constexpr (POS != basic_string_view(fmt).size()) { 253 | constexpr auto tail = compile_format_string(fmt); 254 | if constexpr (std::is_same, 255 | unknown_format>()) 256 | return tail; 257 | else 258 | return make_concat(head, tail); 259 | } else { 260 | return head; 261 | } 262 | } 263 | 264 | template struct parse_specs_result { 265 | formatter fmt; 266 | size_t end; 267 | int next_arg_id; 268 | }; 269 | 270 | enum { manual_indexing_id = -1 }; 271 | 272 | template 273 | constexpr parse_specs_result parse_specs(basic_string_view str, 274 | size_t pos, int next_arg_id) { 275 | str.remove_prefix(pos); 276 | auto ctx = 277 | compile_parse_context(str, max_value(), nullptr, next_arg_id); 278 | auto f = formatter(); 279 | auto end = f.parse(ctx); 280 | return {f, pos + fmt::detail::to_unsigned(end - str.data()), 281 | next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; 282 | } 283 | 284 | template struct arg_id_handler { 285 | arg_id_kind kind; 286 | arg_ref arg_id; 287 | 288 | constexpr int on_auto() { 289 | FMT_ASSERT(false, "handler cannot be used with automatic indexing"); 290 | return 0; 291 | } 292 | constexpr int on_index(int id) { 293 | kind = arg_id_kind::index; 294 | arg_id = arg_ref(id); 295 | return 0; 296 | } 297 | constexpr int on_name(basic_string_view id) { 298 | kind = arg_id_kind::name; 299 | arg_id = arg_ref(id); 300 | return 0; 301 | } 302 | }; 303 | 304 | template struct parse_arg_id_result { 305 | arg_id_kind kind; 306 | arg_ref arg_id; 307 | const Char* arg_id_end; 308 | }; 309 | 310 | template 311 | constexpr auto parse_arg_id(const Char* begin, const Char* end) { 312 | auto handler = arg_id_handler{arg_id_kind::none, arg_ref{}}; 313 | auto arg_id_end = parse_arg_id(begin, end, handler); 314 | return parse_arg_id_result{handler.kind, handler.arg_id, arg_id_end}; 315 | } 316 | 317 | template struct field_type { 318 | using type = remove_cvref_t; 319 | }; 320 | 321 | template 322 | struct field_type::value>> { 323 | using type = remove_cvref_t; 324 | }; 325 | 326 | template 328 | constexpr auto parse_replacement_field_then_tail(S fmt) { 329 | using char_type = typename S::char_type; 330 | constexpr auto str = basic_string_view(fmt); 331 | constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); 332 | if constexpr (c == '}') { 333 | return parse_tail( 334 | field::type, ARG_INDEX>(), fmt); 335 | } else if constexpr (c != ':') { 336 | FMT_THROW(format_error("expected ':'")); 337 | } else { 338 | constexpr auto result = parse_specs::type>( 339 | str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); 340 | if constexpr (result.end >= str.size() || str[result.end] != '}') { 341 | FMT_THROW(format_error("expected '}'")); 342 | return 0; 343 | } else { 344 | return parse_tail( 345 | spec_field::type, ARG_INDEX>{ 346 | result.fmt}, 347 | fmt); 348 | } 349 | } 350 | } 351 | 352 | // Compiles a non-empty format string and returns the compiled representation 353 | // or unknown_format() on unrecognized input. 354 | template 355 | constexpr auto compile_format_string(S fmt) { 356 | using char_type = typename S::char_type; 357 | constexpr auto str = basic_string_view(fmt); 358 | if constexpr (str[POS] == '{') { 359 | if constexpr (POS + 1 == str.size()) 360 | FMT_THROW(format_error("unmatched '{' in format string")); 361 | if constexpr (str[POS + 1] == '{') { 362 | return parse_tail(make_text(str, POS, 1), fmt); 363 | } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { 364 | static_assert(ID != manual_indexing_id, 365 | "cannot switch from manual to automatic argument indexing"); 366 | constexpr auto next_id = 367 | ID != manual_indexing_id ? ID + 1 : manual_indexing_id; 368 | return parse_replacement_field_then_tail, Args, 369 | POS + 1, ID, next_id>(fmt); 370 | } else { 371 | constexpr auto arg_id_result = 372 | parse_arg_id(str.data() + POS + 1, str.data() + str.size()); 373 | constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data(); 374 | constexpr char_type c = 375 | arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); 376 | static_assert(c == '}' || c == ':', "missing '}' in format string"); 377 | if constexpr (arg_id_result.kind == arg_id_kind::index) { 378 | static_assert( 379 | ID == manual_indexing_id || ID == 0, 380 | "cannot switch from automatic to manual argument indexing"); 381 | constexpr auto arg_index = arg_id_result.arg_id.index; 382 | return parse_replacement_field_then_tail, 383 | Args, arg_id_end_pos, 384 | arg_index, manual_indexing_id>( 385 | fmt); 386 | } else if constexpr (arg_id_result.kind == arg_id_kind::name) { 387 | constexpr auto arg_index = 388 | get_arg_index_by_name(arg_id_result.arg_id.name, Args{}); 389 | if constexpr (arg_index >= 0) { 390 | constexpr auto next_id = 391 | ID != manual_indexing_id ? ID + 1 : manual_indexing_id; 392 | return parse_replacement_field_then_tail< 393 | decltype(get_type::value), Args, arg_id_end_pos, 394 | arg_index, next_id>(fmt); 395 | } else if constexpr (c == '}') { 396 | return parse_tail( 397 | runtime_named_field{arg_id_result.arg_id.name}, fmt); 398 | } else if constexpr (c == ':') { 399 | return unknown_format(); // no type info for specs parsing 400 | } 401 | } 402 | } 403 | } else if constexpr (str[POS] == '}') { 404 | if constexpr (POS + 1 == str.size()) 405 | FMT_THROW(format_error("unmatched '}' in format string")); 406 | return parse_tail(make_text(str, POS, 1), fmt); 407 | } else { 408 | constexpr auto end = parse_text(str, POS + 1); 409 | if constexpr (end - POS > 1) { 410 | return parse_tail(make_text(str, POS, end - POS), fmt); 411 | } else { 412 | return parse_tail(code_unit{str[POS]}, fmt); 413 | } 414 | } 415 | } 416 | 417 | template ::value)> 419 | constexpr auto compile(S fmt) { 420 | constexpr auto str = basic_string_view(fmt); 421 | if constexpr (str.size() == 0) { 422 | return detail::make_text(str, 0, 0); 423 | } else { 424 | constexpr auto result = 425 | detail::compile_format_string, 0, 0>(fmt); 426 | return result; 427 | } 428 | } 429 | #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 430 | } // namespace detail 431 | 432 | FMT_BEGIN_EXPORT 433 | 434 | #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) 435 | 436 | template ::value)> 439 | FMT_INLINE std::basic_string format(const CompiledFormat& cf, 440 | const Args&... args) { 441 | auto s = std::basic_string(); 442 | cf.format(std::back_inserter(s), args...); 443 | return s; 444 | } 445 | 446 | template ::value)> 448 | constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, 449 | const Args&... args) { 450 | return cf.format(out, args...); 451 | } 452 | 453 | template ::value)> 455 | FMT_INLINE std::basic_string format(const S&, 456 | Args&&... args) { 457 | if constexpr (std::is_same::value) { 458 | constexpr auto str = basic_string_view(S()); 459 | if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { 460 | const auto& first = detail::first(args...); 461 | if constexpr (detail::is_named_arg< 462 | remove_cvref_t>::value) { 463 | return fmt::to_string(first.value); 464 | } else { 465 | return fmt::to_string(first); 466 | } 467 | } 468 | } 469 | constexpr auto compiled = detail::compile(S()); 470 | if constexpr (std::is_same, 471 | detail::unknown_format>()) { 472 | return fmt::format( 473 | static_cast>(S()), 474 | std::forward(args)...); 475 | } else { 476 | return fmt::format(compiled, std::forward(args)...); 477 | } 478 | } 479 | 480 | template ::value)> 482 | FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { 483 | constexpr auto compiled = detail::compile(S()); 484 | if constexpr (std::is_same, 485 | detail::unknown_format>()) { 486 | return fmt::format_to( 487 | out, static_cast>(S()), 488 | std::forward(args)...); 489 | } else { 490 | return fmt::format_to(out, compiled, std::forward(args)...); 491 | } 492 | } 493 | #endif 494 | 495 | template ::value)> 497 | auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) 498 | -> format_to_n_result { 499 | using traits = detail::fixed_buffer_traits; 500 | auto buf = detail::iterator_buffer(out, n); 501 | fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); 502 | return {buf.out(), buf.count()}; 503 | } 504 | 505 | template ::value)> 507 | FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) 508 | -> size_t { 509 | auto buf = detail::counting_buffer<>(); 510 | fmt::format_to(appender(buf), fmt, args...); 511 | return buf.count(); 512 | } 513 | 514 | template ::value)> 516 | void print(std::FILE* f, const S& fmt, const Args&... args) { 517 | auto buf = memory_buffer(); 518 | fmt::format_to(appender(buf), fmt, args...); 519 | detail::print(f, {buf.data(), buf.size()}); 520 | } 521 | 522 | template ::value)> 524 | void print(const S& fmt, const Args&... args) { 525 | print(stdout, fmt, args...); 526 | } 527 | 528 | #if FMT_USE_NONTYPE_TEMPLATE_ARGS 529 | inline namespace literals { 530 | template constexpr auto operator""_cf() { 531 | return FMT_COMPILE(Str.data); 532 | } 533 | } // namespace literals 534 | #endif 535 | 536 | FMT_END_EXPORT 537 | FMT_END_NAMESPACE 538 | 539 | #endif // FMT_COMPILE_H_ 540 | -------------------------------------------------------------------------------- /tests/third_party/fmt/core.h: -------------------------------------------------------------------------------- 1 | // This file is only provided for compatibility and may be removed in future 2 | // versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h 3 | // otherwise. 4 | 5 | #include "format.h" 6 | -------------------------------------------------------------------------------- /tests/third_party/fmt/os.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional OS-specific functionality 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OS_H_ 9 | #define FMT_OS_H_ 10 | 11 | #include "format.h" 12 | 13 | #ifndef FMT_MODULE 14 | # include 15 | # include 16 | # include 17 | # include // std::system_error 18 | 19 | # if FMT_HAS_INCLUDE() 20 | # include // LC_NUMERIC_MASK on macOS 21 | # endif 22 | #endif // FMT_MODULE 23 | 24 | #ifndef FMT_USE_FCNTL 25 | // UWP doesn't provide _pipe. 26 | # if FMT_HAS_INCLUDE("winapifamily.h") 27 | # include 28 | # endif 29 | # if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ 30 | defined(__linux__)) && \ 31 | (!defined(WINAPI_FAMILY) || \ 32 | (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) 33 | # include // for O_RDONLY 34 | # define FMT_USE_FCNTL 1 35 | # else 36 | # define FMT_USE_FCNTL 0 37 | # endif 38 | #endif 39 | 40 | #ifndef FMT_POSIX 41 | # if defined(_WIN32) && !defined(__MINGW32__) 42 | // Fix warnings about deprecated symbols. 43 | # define FMT_POSIX(call) _##call 44 | # else 45 | # define FMT_POSIX(call) call 46 | # endif 47 | #endif 48 | 49 | // Calls to system functions are wrapped in FMT_SYSTEM for testability. 50 | #ifdef FMT_SYSTEM 51 | # define FMT_HAS_SYSTEM 52 | # define FMT_POSIX_CALL(call) FMT_SYSTEM(call) 53 | #else 54 | # define FMT_SYSTEM(call) ::call 55 | # ifdef _WIN32 56 | // Fix warnings about deprecated symbols. 57 | # define FMT_POSIX_CALL(call) ::_##call 58 | # else 59 | # define FMT_POSIX_CALL(call) ::call 60 | # endif 61 | #endif 62 | 63 | // Retries the expression while it evaluates to error_result and errno 64 | // equals to EINTR. 65 | #ifndef _WIN32 66 | # define FMT_RETRY_VAL(result, expression, error_result) \ 67 | do { \ 68 | (result) = (expression); \ 69 | } while ((result) == (error_result) && errno == EINTR) 70 | #else 71 | # define FMT_RETRY_VAL(result, expression, error_result) result = (expression) 72 | #endif 73 | 74 | #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) 75 | 76 | FMT_BEGIN_NAMESPACE 77 | FMT_BEGIN_EXPORT 78 | 79 | /** 80 | * A reference to a null-terminated string. It can be constructed from a C 81 | * string or `std::string`. 82 | * 83 | * You can use one of the following type aliases for common character types: 84 | * 85 | * +---------------+-----------------------------+ 86 | * | Type | Definition | 87 | * +===============+=============================+ 88 | * | cstring_view | basic_cstring_view | 89 | * +---------------+-----------------------------+ 90 | * | wcstring_view | basic_cstring_view | 91 | * +---------------+-----------------------------+ 92 | * 93 | * This class is most useful as a parameter type for functions that wrap C APIs. 94 | */ 95 | template class basic_cstring_view { 96 | private: 97 | const Char* data_; 98 | 99 | public: 100 | /// Constructs a string reference object from a C string. 101 | basic_cstring_view(const Char* s) : data_(s) {} 102 | 103 | /// Constructs a string reference from an `std::string` object. 104 | basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} 105 | 106 | /// Returns the pointer to a C string. 107 | auto c_str() const -> const Char* { return data_; } 108 | }; 109 | 110 | using cstring_view = basic_cstring_view; 111 | using wcstring_view = basic_cstring_view; 112 | 113 | #ifdef _WIN32 114 | FMT_API const std::error_category& system_category() noexcept; 115 | 116 | namespace detail { 117 | FMT_API void format_windows_error(buffer& out, int error_code, 118 | const char* message) noexcept; 119 | } 120 | 121 | FMT_API std::system_error vwindows_error(int error_code, string_view fmt, 122 | format_args args); 123 | 124 | /** 125 | * Constructs a `std::system_error` object with the description of the form 126 | * 127 | * : 128 | * 129 | * where `` is the formatted message and `` is the 130 | * system message corresponding to the error code. 131 | * `error_code` is a Windows error code as given by `GetLastError`. 132 | * If `error_code` is not a valid error code such as -1, the system message 133 | * will look like "error -1". 134 | * 135 | * **Example**: 136 | * 137 | * // This throws a system_error with the description 138 | * // cannot open file 'madeup': The system cannot find the file 139 | * specified. 140 | * // or similar (system message may vary). 141 | * const char *filename = "madeup"; 142 | * LPOFSTRUCT of = LPOFSTRUCT(); 143 | * HFILE file = OpenFile(filename, &of, OF_READ); 144 | * if (file == HFILE_ERROR) { 145 | * throw fmt::windows_error(GetLastError(), 146 | * "cannot open file '{}'", filename); 147 | * } 148 | */ 149 | template 150 | auto windows_error(int error_code, string_view message, const T&... args) 151 | -> std::system_error { 152 | return vwindows_error(error_code, message, vargs{{args...}}); 153 | } 154 | 155 | // Reports a Windows error without throwing an exception. 156 | // Can be used to report errors from destructors. 157 | FMT_API void report_windows_error(int error_code, const char* message) noexcept; 158 | #else 159 | inline auto system_category() noexcept -> const std::error_category& { 160 | return std::system_category(); 161 | } 162 | #endif // _WIN32 163 | 164 | // std::system is not available on some platforms such as iOS (#2248). 165 | #ifdef __OSX__ 166 | template > 167 | void say(const S& fmt, Args&&... args) { 168 | std::system(format("say \"{}\"", format(fmt, args...)).c_str()); 169 | } 170 | #endif 171 | 172 | // A buffered file. 173 | class buffered_file { 174 | private: 175 | FILE* file_; 176 | 177 | friend class file; 178 | 179 | inline explicit buffered_file(FILE* f) : file_(f) {} 180 | 181 | public: 182 | buffered_file(const buffered_file&) = delete; 183 | void operator=(const buffered_file&) = delete; 184 | 185 | // Constructs a buffered_file object which doesn't represent any file. 186 | inline buffered_file() noexcept : file_(nullptr) {} 187 | 188 | // Destroys the object closing the file it represents if any. 189 | FMT_API ~buffered_file() noexcept; 190 | 191 | public: 192 | inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) { 193 | other.file_ = nullptr; 194 | } 195 | 196 | inline auto operator=(buffered_file&& other) -> buffered_file& { 197 | close(); 198 | file_ = other.file_; 199 | other.file_ = nullptr; 200 | return *this; 201 | } 202 | 203 | // Opens a file. 204 | FMT_API buffered_file(cstring_view filename, cstring_view mode); 205 | 206 | // Closes the file. 207 | FMT_API void close(); 208 | 209 | // Returns the pointer to a FILE object representing this file. 210 | inline auto get() const noexcept -> FILE* { return file_; } 211 | 212 | FMT_API auto descriptor() const -> int; 213 | 214 | template 215 | inline void print(string_view fmt, const T&... args) { 216 | fmt::vargs vargs = {{args...}}; 217 | detail::is_locking() ? fmt::vprint_buffered(file_, fmt, vargs) 218 | : fmt::vprint(file_, fmt, vargs); 219 | } 220 | }; 221 | 222 | #if FMT_USE_FCNTL 223 | 224 | // A file. Closed file is represented by a file object with descriptor -1. 225 | // Methods that are not declared with noexcept may throw 226 | // fmt::system_error in case of failure. Note that some errors such as 227 | // closing the file multiple times will cause a crash on Windows rather 228 | // than an exception. You can get standard behavior by overriding the 229 | // invalid parameter handler with _set_invalid_parameter_handler. 230 | class FMT_API file { 231 | private: 232 | int fd_; // File descriptor. 233 | 234 | // Constructs a file object with a given descriptor. 235 | explicit file(int fd) : fd_(fd) {} 236 | 237 | friend struct pipe; 238 | 239 | public: 240 | // Possible values for the oflag argument to the constructor. 241 | enum { 242 | RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. 243 | WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. 244 | RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. 245 | CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. 246 | APPEND = FMT_POSIX(O_APPEND), // Open in append mode. 247 | TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file. 248 | }; 249 | 250 | // Constructs a file object which doesn't represent any file. 251 | inline file() noexcept : fd_(-1) {} 252 | 253 | // Opens a file and constructs a file object representing this file. 254 | file(cstring_view path, int oflag); 255 | 256 | public: 257 | file(const file&) = delete; 258 | void operator=(const file&) = delete; 259 | 260 | inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } 261 | 262 | // Move assignment is not noexcept because close may throw. 263 | inline auto operator=(file&& other) -> file& { 264 | close(); 265 | fd_ = other.fd_; 266 | other.fd_ = -1; 267 | return *this; 268 | } 269 | 270 | // Destroys the object closing the file it represents if any. 271 | ~file() noexcept; 272 | 273 | // Returns the file descriptor. 274 | inline auto descriptor() const noexcept -> int { return fd_; } 275 | 276 | // Closes the file. 277 | void close(); 278 | 279 | // Returns the file size. The size has signed type for consistency with 280 | // stat::st_size. 281 | auto size() const -> long long; 282 | 283 | // Attempts to read count bytes from the file into the specified buffer. 284 | auto read(void* buffer, size_t count) -> size_t; 285 | 286 | // Attempts to write count bytes from the specified buffer to the file. 287 | auto write(const void* buffer, size_t count) -> size_t; 288 | 289 | // Duplicates a file descriptor with the dup function and returns 290 | // the duplicate as a file object. 291 | static auto dup(int fd) -> file; 292 | 293 | // Makes fd be the copy of this file descriptor, closing fd first if 294 | // necessary. 295 | void dup2(int fd); 296 | 297 | // Makes fd be the copy of this file descriptor, closing fd first if 298 | // necessary. 299 | void dup2(int fd, std::error_code& ec) noexcept; 300 | 301 | // Creates a buffered_file object associated with this file and detaches 302 | // this file object from the file. 303 | auto fdopen(const char* mode) -> buffered_file; 304 | 305 | # if defined(_WIN32) && !defined(__MINGW32__) 306 | // Opens a file and constructs a file object representing this file by 307 | // wcstring_view filename. Windows only. 308 | static file open_windows_file(wcstring_view path, int oflag); 309 | # endif 310 | }; 311 | 312 | struct FMT_API pipe { 313 | file read_end; 314 | file write_end; 315 | 316 | // Creates a pipe setting up read_end and write_end file objects for reading 317 | // and writing respectively. 318 | pipe(); 319 | }; 320 | 321 | // Returns the memory page size. 322 | auto getpagesize() -> long; 323 | 324 | namespace detail { 325 | 326 | struct buffer_size { 327 | constexpr buffer_size() = default; 328 | size_t value = 0; 329 | FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size { 330 | auto bs = buffer_size(); 331 | bs.value = val; 332 | return bs; 333 | } 334 | }; 335 | 336 | struct ostream_params { 337 | int oflag = file::WRONLY | file::CREATE | file::TRUNC; 338 | size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; 339 | 340 | constexpr ostream_params() {} 341 | 342 | template 343 | ostream_params(T... params, int new_oflag) : ostream_params(params...) { 344 | oflag = new_oflag; 345 | } 346 | 347 | template 348 | ostream_params(T... params, detail::buffer_size bs) 349 | : ostream_params(params...) { 350 | this->buffer_size = bs.value; 351 | } 352 | 353 | // Intel has a bug that results in failure to deduce a constructor 354 | // for empty parameter packs. 355 | # if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 356 | ostream_params(int new_oflag) : oflag(new_oflag) {} 357 | ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} 358 | # endif 359 | }; 360 | 361 | } // namespace detail 362 | 363 | FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); 364 | 365 | /// A fast buffered output stream for writing from a single thread. Writing from 366 | /// multiple threads without external synchronization may result in a data race. 367 | class FMT_API ostream : private detail::buffer { 368 | private: 369 | file file_; 370 | 371 | ostream(cstring_view path, const detail::ostream_params& params); 372 | 373 | static void grow(buffer& buf, size_t); 374 | 375 | public: 376 | ostream(ostream&& other) noexcept; 377 | ~ostream(); 378 | 379 | operator writer() { 380 | detail::buffer& buf = *this; 381 | return buf; 382 | } 383 | 384 | inline void flush() { 385 | if (size() == 0) return; 386 | file_.write(data(), size() * sizeof(data()[0])); 387 | clear(); 388 | } 389 | 390 | template 391 | friend auto output_file(cstring_view path, T... params) -> ostream; 392 | 393 | inline void close() { 394 | flush(); 395 | file_.close(); 396 | } 397 | 398 | /// Formats `args` according to specifications in `fmt` and writes the 399 | /// output to the file. 400 | template void print(format_string fmt, T&&... args) { 401 | vformat_to(appender(*this), fmt.str, vargs{{args...}}); 402 | } 403 | }; 404 | 405 | /** 406 | * Opens a file for writing. Supported parameters passed in `params`: 407 | * 408 | * - ``: Flags passed to [open]( 409 | * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) 410 | * (`file::WRONLY | file::CREATE | file::TRUNC` by default) 411 | * - `buffer_size=`: Output buffer size 412 | * 413 | * **Example**: 414 | * 415 | * auto out = fmt::output_file("guide.txt"); 416 | * out.print("Don't {}", "Panic"); 417 | */ 418 | template 419 | inline auto output_file(cstring_view path, T... params) -> ostream { 420 | return {path, detail::ostream_params(params...)}; 421 | } 422 | #endif // FMT_USE_FCNTL 423 | 424 | FMT_END_EXPORT 425 | FMT_END_NAMESPACE 426 | 427 | #endif // FMT_OS_H_ 428 | -------------------------------------------------------------------------------- /tests/third_party/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #ifndef FMT_MODULE 12 | # include // std::filebuf 13 | #endif 14 | 15 | #ifdef _WIN32 16 | # ifdef __GLIBCXX__ 17 | # include 18 | # include 19 | # endif 20 | # include 21 | #endif 22 | 23 | #include "chrono.h" // formatbuf 24 | 25 | #ifdef _MSVC_STL_UPDATE 26 | # define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE 27 | #elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5 28 | # define FMT_MSVC_STL_UPDATE _MSVC_LANG 29 | #else 30 | # define FMT_MSVC_STL_UPDATE 0 31 | #endif 32 | 33 | FMT_BEGIN_NAMESPACE 34 | namespace detail { 35 | 36 | // Generate a unique explicit instantion in every translation unit using a tag 37 | // type in an anonymous namespace. 38 | namespace { 39 | struct file_access_tag {}; 40 | } // namespace 41 | template 42 | class file_access { 43 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } 44 | }; 45 | 46 | #if FMT_MSVC_STL_UPDATE 47 | template class file_access; 49 | auto get_file(std::filebuf&) -> FILE*; 50 | #endif 51 | 52 | // Write the content of buf to os. 53 | // It is a separate function rather than a part of vprint to simplify testing. 54 | template 55 | void write_buffer(std::basic_ostream& os, buffer& buf) { 56 | const Char* buf_data = buf.data(); 57 | using unsigned_streamsize = make_unsigned_t; 58 | unsigned_streamsize size = buf.size(); 59 | unsigned_streamsize max_size = to_unsigned(max_value()); 60 | do { 61 | unsigned_streamsize n = size <= max_size ? size : max_size; 62 | os.write(buf_data, static_cast(n)); 63 | buf_data += n; 64 | size -= n; 65 | } while (size != 0); 66 | } 67 | 68 | template struct streamed_view { 69 | const T& value; 70 | }; 71 | } // namespace detail 72 | 73 | // Formats an object of type T that has an overloaded ostream operator<<. 74 | template 75 | struct basic_ostream_formatter : formatter, Char> { 76 | void set_debug_format() = delete; 77 | 78 | template 79 | auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { 80 | auto buffer = basic_memory_buffer(); 81 | auto&& formatbuf = detail::formatbuf>(buffer); 82 | auto&& output = std::basic_ostream(&formatbuf); 83 | output.imbue(std::locale::classic()); // The default is always unlocalized. 84 | output << value; 85 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 86 | return formatter, Char>::format( 87 | {buffer.data(), buffer.size()}, ctx); 88 | } 89 | }; 90 | 91 | using ostream_formatter = basic_ostream_formatter; 92 | 93 | template 94 | struct formatter, Char> 95 | : basic_ostream_formatter { 96 | template 97 | auto format(detail::streamed_view view, Context& ctx) const 98 | -> decltype(ctx.out()) { 99 | return basic_ostream_formatter::format(view.value, ctx); 100 | } 101 | }; 102 | 103 | /** 104 | * Returns a view that formats `value` via an ostream `operator<<`. 105 | * 106 | * **Example**: 107 | * 108 | * fmt::print("Current thread id: {}\n", 109 | * fmt::streamed(std::this_thread::get_id())); 110 | */ 111 | template 112 | constexpr auto streamed(const T& value) -> detail::streamed_view { 113 | return {value}; 114 | } 115 | 116 | inline void vprint(std::ostream& os, string_view fmt, format_args args) { 117 | auto buffer = memory_buffer(); 118 | detail::vformat_to(buffer, fmt, args); 119 | FILE* f = nullptr; 120 | #if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI 121 | if (auto* buf = dynamic_cast(os.rdbuf())) 122 | f = detail::get_file(*buf); 123 | #elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI 124 | auto* rdbuf = os.rdbuf(); 125 | if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) 126 | f = sfbuf->file(); 127 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) 128 | f = fbuf->file(); 129 | #endif 130 | #ifdef _WIN32 131 | if (f) { 132 | int fd = _fileno(f); 133 | if (_isatty(fd)) { 134 | os.flush(); 135 | if (detail::write_console(fd, {buffer.data(), buffer.size()})) return; 136 | } 137 | } 138 | #endif 139 | detail::ignore_unused(f); 140 | detail::write_buffer(os, buffer); 141 | } 142 | 143 | /** 144 | * Prints formatted data to the stream `os`. 145 | * 146 | * **Example**: 147 | * 148 | * fmt::print(cerr, "Don't {}!", "panic"); 149 | */ 150 | FMT_EXPORT template 151 | void print(std::ostream& os, format_string fmt, T&&... args) { 152 | fmt::vargs vargs = {{args...}}; 153 | if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs); 154 | auto buffer = memory_buffer(); 155 | detail::vformat_to(buffer, fmt.str, vargs); 156 | detail::write_buffer(os, buffer); 157 | } 158 | 159 | FMT_EXPORT template 160 | void println(std::ostream& os, format_string fmt, T&&... args) { 161 | fmt::print(os, FMT_STRING("{}\n"), 162 | fmt::format(fmt, std::forward(args)...)); 163 | } 164 | 165 | FMT_END_NAMESPACE 166 | 167 | #endif // FMT_OSTREAM_H_ 168 | -------------------------------------------------------------------------------- /tests/third_party/fmt/xchar.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - optional wchar_t and exotic character support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_XCHAR_H_ 9 | #define FMT_XCHAR_H_ 10 | 11 | #include "color.h" 12 | #include "format.h" 13 | #include "ostream.h" 14 | #include "ranges.h" 15 | 16 | #ifndef FMT_MODULE 17 | # include 18 | # if FMT_USE_LOCALE 19 | # include 20 | # endif 21 | #endif 22 | 23 | FMT_BEGIN_NAMESPACE 24 | namespace detail { 25 | 26 | template 27 | using is_exotic_char = bool_constant::value>; 28 | 29 | template struct format_string_char {}; 30 | 31 | template 32 | struct format_string_char< 33 | S, void_t())))>> { 34 | using type = char_t; 35 | }; 36 | 37 | template 38 | struct format_string_char< 39 | S, enable_if_t::value>> { 40 | using type = typename S::char_type; 41 | }; 42 | 43 | template 44 | using format_string_char_t = typename format_string_char::type; 45 | 46 | inline auto write_loc(basic_appender out, loc_value value, 47 | const format_specs& specs, locale_ref loc) -> bool { 48 | #if FMT_USE_LOCALE 49 | auto& numpunct = 50 | std::use_facet>(loc.get()); 51 | auto separator = std::wstring(); 52 | auto grouping = numpunct.grouping(); 53 | if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep()); 54 | return value.visit(loc_writer{out, specs, separator, grouping, {}}); 55 | #endif 56 | return false; 57 | } 58 | } // namespace detail 59 | 60 | FMT_BEGIN_EXPORT 61 | 62 | using wstring_view = basic_string_view; 63 | using wformat_parse_context = parse_context; 64 | using wformat_context = buffered_context; 65 | using wformat_args = basic_format_args; 66 | using wmemory_buffer = basic_memory_buffer; 67 | 68 | template struct basic_fstring { 69 | private: 70 | basic_string_view str_; 71 | 72 | static constexpr int num_static_named_args = 73 | detail::count_static_named_args(); 74 | 75 | using checker = detail::format_string_checker< 76 | Char, static_cast(sizeof...(T)), num_static_named_args, 77 | num_static_named_args != detail::count_named_args()>; 78 | 79 | using arg_pack = detail::arg_pack; 80 | 81 | public: 82 | using t = basic_fstring; 83 | 84 | template >::value)> 87 | FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) { 88 | if (FMT_USE_CONSTEVAL) 89 | detail::parse_format_string(s, checker(s, arg_pack())); 90 | } 91 | template ::value&& 93 | std::is_same::value)> 94 | FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) { 95 | FMT_CONSTEXPR auto sv = basic_string_view(S()); 96 | FMT_CONSTEXPR int ignore = 97 | (parse_format_string(sv, checker(sv, arg_pack())), 0); 98 | detail::ignore_unused(ignore); 99 | } 100 | basic_fstring(runtime_format_string fmt) : str_(fmt.str) {} 101 | 102 | operator basic_string_view() const { return str_; } 103 | auto get() const -> basic_string_view { return str_; } 104 | }; 105 | 106 | template 107 | using basic_format_string = basic_fstring; 108 | 109 | template 110 | using wformat_string = typename basic_format_string::t; 111 | inline auto runtime(wstring_view s) -> runtime_format_string { 112 | return {{s}}; 113 | } 114 | 115 | #ifdef __cpp_char8_t 116 | template <> struct is_char : bool_constant {}; 117 | #endif 118 | 119 | template 120 | constexpr auto make_wformat_args(T&... args) 121 | -> decltype(fmt::make_format_args(args...)) { 122 | return fmt::make_format_args(args...); 123 | } 124 | 125 | #if !FMT_USE_NONTYPE_TEMPLATE_ARGS 126 | inline namespace literals { 127 | inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg { 128 | return {s}; 129 | } 130 | } // namespace literals 131 | #endif 132 | 133 | template 134 | auto join(It begin, Sentinel end, wstring_view sep) 135 | -> join_view { 136 | return {begin, end, sep}; 137 | } 138 | 139 | template ::value)> 140 | auto join(Range&& range, wstring_view sep) 141 | -> join_view { 143 | return join(std::begin(range), std::end(range), sep); 144 | } 145 | 146 | template 147 | auto join(std::initializer_list list, wstring_view sep) 148 | -> join_view { 149 | return join(std::begin(list), std::end(list), sep); 150 | } 151 | 152 | template ::value)> 153 | auto join(const Tuple& tuple, basic_string_view sep) 154 | -> tuple_join_view { 155 | return {tuple, sep}; 156 | } 157 | 158 | template ::value)> 159 | auto vformat(basic_string_view fmt, 160 | typename detail::vformat_args::type args) 161 | -> std::basic_string { 162 | auto buf = basic_memory_buffer(); 163 | detail::vformat_to(buf, fmt, args); 164 | return {buf.data(), buf.size()}; 165 | } 166 | 167 | template 168 | auto format(wformat_string fmt, T&&... args) -> std::wstring { 169 | return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); 170 | } 171 | 172 | template 173 | auto format_to(OutputIt out, wformat_string fmt, T&&... args) 174 | -> OutputIt { 175 | return vformat_to(out, fmt::wstring_view(fmt), 176 | fmt::make_wformat_args(args...)); 177 | } 178 | 179 | // Pass char_t as a default template parameter instead of using 180 | // std::basic_string> to reduce the symbol size. 181 | template , 183 | FMT_ENABLE_IF(!std::is_same::value && 184 | !std::is_same::value)> 185 | auto format(const S& fmt, T&&... args) -> std::basic_string { 186 | return vformat(detail::to_string_view(fmt), 187 | fmt::make_format_args>(args...)); 188 | } 189 | 190 | template , 192 | FMT_ENABLE_IF(detail::is_locale::value&& 193 | detail::is_exotic_char::value)> 194 | inline auto vformat(const Locale& loc, const S& fmt, 195 | typename detail::vformat_args::type args) 196 | -> std::basic_string { 197 | auto buf = basic_memory_buffer(); 198 | detail::vformat_to(buf, detail::to_string_view(fmt), args, 199 | detail::locale_ref(loc)); 200 | return {buf.data(), buf.size()}; 201 | } 202 | 203 | template , 205 | FMT_ENABLE_IF(detail::is_locale::value&& 206 | detail::is_exotic_char::value)> 207 | inline auto format(const Locale& loc, const S& fmt, T&&... args) 208 | -> std::basic_string { 209 | return vformat(loc, detail::to_string_view(fmt), 210 | fmt::make_format_args>(args...)); 211 | } 212 | 213 | template , 215 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 216 | detail::is_exotic_char::value)> 217 | auto vformat_to(OutputIt out, const S& fmt, 218 | typename detail::vformat_args::type args) -> OutputIt { 219 | auto&& buf = detail::get_buffer(out); 220 | detail::vformat_to(buf, detail::to_string_view(fmt), args); 221 | return detail::get_iterator(buf, out); 222 | } 223 | 224 | template , 226 | FMT_ENABLE_IF(detail::is_output_iterator::value && 227 | !std::is_same::value && 228 | !std::is_same::value)> 229 | inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { 230 | return vformat_to(out, detail::to_string_view(fmt), 231 | fmt::make_format_args>(args...)); 232 | } 233 | 234 | template , 236 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 237 | detail::is_locale::value&& 238 | detail::is_exotic_char::value)> 239 | inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, 240 | typename detail::vformat_args::type args) 241 | -> OutputIt { 242 | auto&& buf = detail::get_buffer(out); 243 | vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc)); 244 | return detail::get_iterator(buf, out); 245 | } 246 | 247 | template , 249 | bool enable = detail::is_output_iterator::value && 250 | detail::is_locale::value && 251 | detail::is_exotic_char::value> 252 | inline auto format_to(OutputIt out, const Locale& loc, const S& fmt, 253 | T&&... args) -> 254 | typename std::enable_if::type { 255 | return vformat_to(out, loc, detail::to_string_view(fmt), 256 | fmt::make_format_args>(args...)); 257 | } 258 | 259 | template ::value&& 261 | detail::is_exotic_char::value)> 262 | inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view fmt, 263 | typename detail::vformat_args::type args) 264 | -> format_to_n_result { 265 | using traits = detail::fixed_buffer_traits; 266 | auto buf = detail::iterator_buffer(out, n); 267 | detail::vformat_to(buf, fmt, args); 268 | return {buf.out(), buf.count()}; 269 | } 270 | 271 | template , 273 | FMT_ENABLE_IF(detail::is_output_iterator::value&& 274 | detail::is_exotic_char::value)> 275 | inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) 276 | -> format_to_n_result { 277 | return vformat_to_n(out, n, fmt::basic_string_view(fmt), 278 | fmt::make_format_args>(args...)); 279 | } 280 | 281 | template , 283 | FMT_ENABLE_IF(detail::is_exotic_char::value)> 284 | inline auto formatted_size(const S& fmt, T&&... args) -> size_t { 285 | auto buf = detail::counting_buffer(); 286 | detail::vformat_to(buf, detail::to_string_view(fmt), 287 | fmt::make_format_args>(args...)); 288 | return buf.count(); 289 | } 290 | 291 | inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { 292 | auto buf = wmemory_buffer(); 293 | detail::vformat_to(buf, fmt, args); 294 | buf.push_back(L'\0'); 295 | if (std::fputws(buf.data(), f) == -1) 296 | FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); 297 | } 298 | 299 | inline void vprint(wstring_view fmt, wformat_args args) { 300 | vprint(stdout, fmt, args); 301 | } 302 | 303 | template 304 | void print(std::FILE* f, wformat_string fmt, T&&... args) { 305 | return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); 306 | } 307 | 308 | template void print(wformat_string fmt, T&&... args) { 309 | return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); 310 | } 311 | 312 | template 313 | void println(std::FILE* f, wformat_string fmt, T&&... args) { 314 | return print(f, L"{}\n", fmt::format(fmt, std::forward(args)...)); 315 | } 316 | 317 | template void println(wformat_string fmt, T&&... args) { 318 | return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); 319 | } 320 | 321 | inline auto vformat(text_style ts, wstring_view fmt, wformat_args args) 322 | -> std::wstring { 323 | auto buf = wmemory_buffer(); 324 | detail::vformat_to(buf, ts, fmt, args); 325 | return {buf.data(), buf.size()}; 326 | } 327 | 328 | template 329 | inline auto format(text_style ts, wformat_string fmt, T&&... args) 330 | -> std::wstring { 331 | return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); 332 | } 333 | 334 | template 335 | FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string fmt, 336 | const T&... args) { 337 | vprint(f, ts, fmt, fmt::make_wformat_args(args...)); 338 | } 339 | 340 | template 341 | FMT_DEPRECATED void print(text_style ts, wformat_string fmt, 342 | const T&... args) { 343 | return print(stdout, ts, fmt, args...); 344 | } 345 | 346 | inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { 347 | auto buffer = basic_memory_buffer(); 348 | detail::vformat_to(buffer, fmt, args); 349 | detail::write_buffer(os, buffer); 350 | } 351 | 352 | template 353 | void print(std::wostream& os, wformat_string fmt, T&&... args) { 354 | vprint(os, fmt, fmt::make_format_args>(args...)); 355 | } 356 | 357 | template 358 | void println(std::wostream& os, wformat_string fmt, T&&... args) { 359 | print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); 360 | } 361 | 362 | /// Converts `value` to `std::wstring` using the default format for type `T`. 363 | template inline auto to_wstring(const T& value) -> std::wstring { 364 | return format(FMT_STRING(L"{}"), value); 365 | } 366 | FMT_END_EXPORT 367 | FMT_END_NAMESPACE 368 | 369 | #endif // FMT_XCHAR_H_ 370 | -------------------------------------------------------------------------------- /tests/type_name.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test_utility.hpp" 4 | #include 5 | 6 | namespace Namespace { 7 | enum UnscopedEnum : int; 8 | enum class Enum; 9 | struct Struct; 10 | class Class; 11 | } // namespace Namespace 12 | 13 | enum UnscopedEnum : int; 14 | enum class Enum; 15 | struct Struct; 16 | class Class; 17 | 18 | TEST_CASE("type_name", "[type_name]") 19 | { 20 | STATIC_CHECK(enchantum::type_name == "Namespace::UnscopedEnum"); 21 | STATIC_CHECK(enchantum::type_name == "Namespace::Enum"); 22 | STATIC_CHECK(enchantum::type_name == "Namespace::Struct"); 23 | STATIC_CHECK(enchantum::type_name == "Namespace::Class"); 24 | STATIC_CHECK(enchantum::type_name == "UnscopedEnum"); 25 | STATIC_CHECK(enchantum::type_name == "Enum"); 26 | STATIC_CHECK(enchantum::type_name == "Struct"); 27 | STATIC_CHECK(enchantum::type_name == "Class"); 28 | } 29 | 30 | 31 | TEMPLATE_LIST_TEST_CASE("type_name is null terminated", "[type_name]", AllEnumsTestTypes) 32 | { 33 | STATIC_CHECK('\0' == enchantum::type_name.data()[enchantum::type_name.size()]); 34 | } 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /tests/unscoped_test.cpp: -------------------------------------------------------------------------------- 1 | #include "test_utility.hpp" 2 | #include 3 | #include 4 | 5 | TEST_CASE("Unscoped C enum") 6 | { 7 | STATIC_CHECK(enchantum::count == 5); 8 | STATIC_CHECK(enchantum::entries.size() == 5); 9 | } --------------------------------------------------------------------------------