diff options
author | kcgen <kcgen@users.noreply.github.com> | 2022-08-03 17:32:32 +0300 |
---|---|---|
committer | kcgen <kcgen@users.noreply.github.com> | 2022-08-03 18:48:05 +0300 |
commit | 0d5ce539886705e5cab854eb77df883c145121ea (patch) | |
tree | deb4e613daa8bc7782c98ba57d8de3b7ce41ed4d | |
parent | e7ce5b2a428b24bde9116b526599c8c2e925ecc2 (diff) |
Remove magic_enum due to cons outweighing proskc/drop-magic-enum-1
Although **magic_enum** does provide elegant reflective `enum` support
that avoids both the programming burden of writing string-to-enum
conversion routines (and vice-versa) as well as the runtime cost of
evaluating those string comparisons (yes: magic_enum makes those
conversions compile-time `constexpr`s), it turns out it only does so
with `enum` values between -128 and 127.
Additional code changes are needed to expand this range up to 16-bits,
either in general or on an enum-specific basis. To expand the range, a
macro generates 65k `constexpr` cases which the compiler then has to
dissolve back down, adding compile time burden.
`enum` values beyond 16-bit are not supported by magic_enum, probably
because it would require the generation of 2^32 `constexpr` cases,
producing an insurmountable compile-time burden.
Our use of `enum` goes beyond the prosaic: we use values matching
specific DOS constants that can range from single bits up to full
integers. So unfortunately, it turns out the use-case for magic_enum
is significantly narrowed, and even worse: inconsistently available as
a language feature.
That last point makes it a deal-breaker because it doubles the burden
on the maintainers, encouraging the use of reflective coding style for
`enum`s that happen to have sub-16-bit values while using the
traditional non-reflective style for `enum`s having 16-bit and greater
values.
I prefer we avoid carrying two programming styles for the same concept
(enum), as it avoids confusion and keeps things clearer for future
maintainers as well.
We will wait until a better implementation of reflective `enum`
emerges that supports the full value range without compromises, either
from the community or via the language standard itself.
-rw-r--r-- | LICENSE | 1 | ||||
-rw-r--r-- | include/enum.h | 45 | ||||
-rw-r--r-- | src/dos/dos_memory.cpp | 33 | ||||
-rw-r--r-- | src/hardware/mixer.cpp | 74 | ||||
-rw-r--r-- | src/libs/magic_enum/LICENSE | 21 | ||||
-rw-r--r-- | src/libs/magic_enum/README.md | 288 | ||||
-rw-r--r-- | src/libs/magic_enum/include/magic_enum.hpp | 1389 | ||||
-rw-r--r-- | src/libs/magic_enum/include/magic_enum_format.hpp | 79 | ||||
-rw-r--r-- | src/libs/magic_enum/include/magic_enum_fuse.hpp | 89 |
9 files changed, 78 insertions, 1941 deletions
@@ -34,7 +34,6 @@ GPL-2.0-or-later terms, however there are some exceptions: - gui_tk: GPL-3.0-or-later - iir1: MIT - loguru: Public Domain - - magic_enum: MIT - PDcurses: Public Domain - whereami: MIT or WTFPL - YM7128B_emu: BSD 2-Clause diff --git a/include/enum.h b/include/enum.h deleted file mode 100644 index 78a0a6a40..000000000 --- a/include/enum.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * SPDX-License-Identifier: GPL-2.0-or-later - * - * Copyright (C) 2022-2022 The DOSBox Staging Team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef DOSBOX_ENUM_H -#define DOSBOX_ENUM_H - -// A reflective enum gap-filler until it's standardized in the langauge. -// -// Usage: https://github.com/Neargye/magic_enum/blob/master/README.md -// -// Why prefer 'Magic Enum C++' over others, like 'Better Enum'? -// -// Magic Enum C++ provides a C++17-and-newer augmentation of enum -// specification, which means its primarily easy to read for maintainers -// and secondarily (should be) easy to remove/upgrade when the language -// supports similar calls. -// -// See more here: https://github.com/aantron/better-enums/issues/78 - -#include "../src/libs/magic_enum/include/magic_enum.hpp" -#include "../src/libs/magic_enum/include/magic_enum_fuse.hpp" - -// We're interested in native-feeling functionality and don't want -// "magic_enum::" prefixes poluting each point-of-use. -// -using namespace magic_enum; - -#endif diff --git a/src/dos/dos_memory.cpp b/src/dos/dos_memory.cpp index 5a750cec7..451970c50 100644 --- a/src/dos/dos_memory.cpp +++ b/src/dos/dos_memory.cpp @@ -19,10 +19,12 @@ #include "dosbox.h" #include "dos_inc.h" -#include "enum.h" #include "mem.h" +#include "support.h" -enum class McbFaultStrategy { deny, repair, report, allow }; +#include <string_view> + +enum class McbFaultStrategy { Deny, Repair, Report, Allow }; // Constants // ~~~~~~~~~ @@ -34,16 +36,27 @@ constexpr uint16_t umb_start_seg = 0x9fff; static uint16_t allocation_strategy = 0x00; -static auto mcb_fault_strategy = McbFaultStrategy::repair; +static auto mcb_fault_strategy = McbFaultStrategy::Repair; // not based on anything in particular. Player Manager 2 requires ~17 corrections. constexpr uint16_t max_allowed_faults = 100; -void DOS_SetMcbFaultStrategy(const char *mcb_fault_strategy_pref) +void DOS_SetMcbFaultStrategy(const char * mcb_fault_strategy_pref) { assert(mcb_fault_strategy_pref); - mcb_fault_strategy = - enum_cast<McbFaultStrategy>(mcb_fault_strategy_pref).value(); + const std::string_view pref = mcb_fault_strategy_pref; + + if (pref == "deny") + mcb_fault_strategy = McbFaultStrategy::Deny; + else if (pref == "repair") + mcb_fault_strategy = McbFaultStrategy::Repair; + else if (pref == "report") + mcb_fault_strategy = McbFaultStrategy::Report; + else if (pref == "allow") + mcb_fault_strategy = McbFaultStrategy::Allow; + else + // the conf system programmatically guarantees only the above prefs are used + assertm(false, "Unhandled MCB fault strategy"); } // returns true if the MCB block needed triaging @@ -57,11 +70,11 @@ static bool triage_block(DOS_MCB &mcb, const uint8_t repair_type) return false; switch (mcb_fault_strategy) { - case McbFaultStrategy::deny: + case McbFaultStrategy::Deny: E_Exit("DOS_MEMORY: Exiting due corrupt MCB chain"); break; - case McbFaultStrategy::repair: + case McbFaultStrategy::Repair: LOG_INFO("DOS_MEMORY: Repairing MCB block in segment %04xh from type '%02x' to '%02x'", mcb.GetPSPSeg(), mcb.GetType(), @@ -70,13 +83,13 @@ static bool triage_block(DOS_MCB &mcb, const uint8_t repair_type) assert(mcb_type_is_valid()); break; - case McbFaultStrategy::report: + case McbFaultStrategy::Report: LOG_WARNING("DOS_MEMORY: Reporting MCB block in segment %04xh with corrupt type '%02x'", mcb.GetPSPSeg(), mcb.GetType()); break; - case McbFaultStrategy::allow: + case McbFaultStrategy::Allow: break; } return true; diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp index 20dba9d2a..717d35e04 100644 --- a/src/hardware/mixer.cpp +++ b/src/hardware/mixer.cpp @@ -49,7 +49,6 @@ #include "ansi_code_markup.h" #include "control.h" #include "cross.h" -#include "enum.h" #include "hardware.h" #include "mapper.h" #include "mem.h" @@ -99,7 +98,7 @@ using highpass_filter_t = std::array<Iir::Butterworth::HighPass<2>, 2>; using EmVerb = MVerb<float>; -enum class ReverbPreset { none, tiny, small, medium, large, huge }; +enum class ReverbPreset { None, Tiny, Small, Medium, Large, Huge }; struct reverb_settings_t { EmVerb mverb = {}; @@ -110,7 +109,7 @@ struct reverb_settings_t { // resulting in a more pleasant sound. highpass_filter_t highpass_filter = {}; - ReverbPreset preset = ReverbPreset::none; + ReverbPreset preset = ReverbPreset::None; float synthesizer_send_level = 0.0f; float digital_audio_send_level = 0.0f; float highpass_cutoff_freq = 1.0f; @@ -142,12 +141,12 @@ struct reverb_settings_t { } }; -enum class ChorusPreset { none, light, normal, strong }; +enum class ChorusPreset { None, Light, Normal, Strong }; struct chorus_settings_t { ChorusEngine chorus_engine = ChorusEngine(48000); - ChorusPreset preset = ChorusPreset::none; + ChorusPreset preset = ChorusPreset::None; float synthesizer_send_level = 0.0f; float digital_audio_send_level = 0.0f; @@ -286,6 +285,26 @@ static void set_global_chorus(const mixer_channel_t channel) channel->SetChorusLevel(mixer.chorus.digital_audio_send_level); } +constexpr ReverbPreset reverb_pref_to_preset(const std::string_view pref) +{ + if (pref == "off") + return ReverbPreset::None; + if (pref == "tiny") + return ReverbPreset::Tiny; + if (pref == "small") + return ReverbPreset::Small; + if (pref == "medium" || pref == "on") + return ReverbPreset::Medium; + if (pref == "large") + return ReverbPreset::Large; + if (pref == "huge") + return ReverbPreset::Huge; + + // the conf system programmatically guarantees only the above prefs are used + assertm(false, "Unhandled revert preset"); + return ReverbPreset::None; +} + static void configure_reverb(std::string reverb_pref) { auto was_reverb_on = mixer.do_reverb; @@ -307,7 +326,7 @@ static void configure_reverb(std::string reverb_pref) auto &r = mixer.reverb; // short-hand reference auto current_preset = r.preset; - auto new_preset = enum_cast<ReverbPreset>(reverb_pref).value(); + auto new_preset = reverb_pref_to_preset(reverb_pref); auto preset_unchanged = (current_preset == new_preset); if (was_reverb_on && preset_unchanged) @@ -329,12 +348,12 @@ static void configure_reverb(std::string reverb_pref) // clang-format off switch (r.preset) { // PDELAY EARLY SIZE DNSITY BWFREQ DECAY DAMPLV -SYNLV -DIGLV HIPASSHZ RATE_HZ - case ReverbPreset::none: break; - case ReverbPreset::tiny: r.Setup(0.00f, 1.00f, 0.05f, 0.50f, 0.50f, 0.00f, 1.00f, __5_2dB, __5_2dB, 200.0f, rate_hz); break; - case ReverbPreset::small: r.Setup(0.00f, 1.00f, 0.17f, 0.42f, 0.50f, 0.50f, 0.70f, _24_0dB, _36_8dB, 200.0f, rate_hz); break; - case ReverbPreset::medium: r.Setup(0.00f, 0.75f, 0.50f, 0.50f, 0.95f, 0.42f, 0.21f, _18_4dB, _37_2dB, 170.0f, rate_hz); break; - case ReverbPreset::large: r.Setup(0.00f, 0.75f, 0.75f, 0.50f, 0.95f, 0.52f, 0.21f, _12_0dB, _38_0dB, 140.0f, rate_hz); break; - case ReverbPreset::huge: r.Setup(0.00f, 0.75f, 0.75f, 0.50f, 0.95f, 0.52f, 0.21f, __6_0dB, _38_0dB, 140.0f, rate_hz); break; + case ReverbPreset::None: break; + case ReverbPreset::Tiny: r.Setup(0.00f, 1.00f, 0.05f, 0.50f, 0.50f, 0.00f, 1.00f, __5_2dB, __5_2dB, 200.0f, rate_hz); break; + case ReverbPreset::Small: r.Setup(0.00f, 1.00f, 0.17f, 0.42f, 0.50f, 0.50f, 0.70f, _24_0dB, _36_8dB, 200.0f, rate_hz); break; + case ReverbPreset::Medium: r.Setup(0.00f, 0.75f, 0.50f, 0.50f, 0.95f, 0.42f, 0.21f, _18_4dB, _37_2dB, 170.0f, rate_hz); break; + case ReverbPreset::Large: r.Setup(0.00f, 0.75f, 0.75f, 0.50f, 0.95f, 0.52f, 0.21f, _12_0dB, _38_0dB, 140.0f, rate_hz); break; + case ReverbPreset::Huge: r.Setup(0.00f, 0.75f, 0.75f, 0.50f, 0.95f, 0.52f, 0.21f, __6_0dB, _38_0dB, 140.0f, rate_hz); break; } // clang-format on @@ -345,6 +364,22 @@ static void configure_reverb(std::string reverb_pref) LOG_MSG("MIXER: Reverb enabled ('%s' preset)", reverb_pref.c_str()); } +constexpr ChorusPreset chorus_pref_to_preset(const std::string_view pref) +{ + if (pref == "off") + return ChorusPreset::None; + if (pref == "light") + return ChorusPreset::Light; + if (pref == "normal" || pref == "on") + return ChorusPreset::Normal; + if (pref == "strong") + return ChorusPreset::Strong; + + // the conf system programmatically guarantees only the above prefs are used + assertm(false, "Unhandled chorus preset"); + return ChorusPreset::None; +} + static void configure_chorus(std::string chorus_pref) { auto was_chorus_on = mixer.do_chorus; @@ -368,7 +403,7 @@ static void configure_chorus(std::string chorus_pref) const auto rate_hz = mixer.sample_rate.load(); auto current_preset = c.preset; - auto new_preset = enum_cast<ChorusPreset>(chorus_pref).value(); + auto new_preset = chorus_pref_to_preset(chorus_pref); auto preset_unchanged = (current_preset == new_preset); if (was_chorus_on && preset_unchanged) @@ -383,10 +418,10 @@ static void configure_chorus(std::string chorus_pref) // clang-format off switch (c.preset) { // -SYNLV -DIGLV RATE_HZ - case ChorusPreset::none: break; - case ChorusPreset::light: c.Setup(_16dB, 0.00f, rate_hz); break; - case ChorusPreset::normal: c.Setup(_11dB, 0.00f, rate_hz); break; - case ChorusPreset::strong: c.Setup(__6dB, 0.00f, rate_hz); break; + case ChorusPreset::None: break; + case ChorusPreset::Light: c.Setup(_16dB, 0.00f, rate_hz); break; + case ChorusPreset::Normal: c.Setup(_11dB, 0.00f, rate_hz); break; + case ChorusPreset::Strong: c.Setup(__6dB, 0.00f, rate_hz); break; } // clang-format on @@ -707,8 +742,9 @@ static void log_filter_settings(const std::string &channel_name, const FilterState state, const uint8_t order, const uint16_t cutoff_freq) { - assert(state != FilterState::Off); // we expect only enabled states - assert(enum_count<FilterState>() == 3); // catch enum additions/removals + // we programmatically expect only 'on' and 'forced-on' states: + assert(state != FilterState::Off); + assert(state == FilterState::On || state == FilterState::ForcedOn); constexpr auto db_per_order = 6; diff --git a/src/libs/magic_enum/LICENSE b/src/libs/magic_enum/LICENSE deleted file mode 100644 index 05b298b75..000000000 --- a/src/libs/magic_enum/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2019 - 2022 Daniil Goncharov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/libs/magic_enum/README.md b/src/libs/magic_enum/README.md deleted file mode 100644 index f7e347b54..000000000 --- a/src/libs/magic_enum/README.md +++ /dev/null @@ -1,288 +0,0 @@ -[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner2-direct.svg)](https://bit.ly/3OMysM8) - -```text - __ __ _ ______ _____ -| \/ | (_) | ____| / ____|_ _ -| \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ -| |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| -| | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| -|_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| - __/ | - |___/ -``` - -[![Github releases](https://img.shields.io/github/release/Neargye/magic_enum.svg)](https://github.com/Neargye/magic_enum/releases) -[![Conan package](https://img.shields.io/badge/Conan-package-blueviolet)](https://conan.io/center/magic_enum) -[![Vcpkg package](https://img.shields.io/badge/Vcpkg-package-blueviolet)](https://github.com/microsoft/vcpkg/tree/master/ports/magic-enum) -[![Build2 package](https://img.shields.io/badge/Build2-package-blueviolet)](https://www.cppget.org/magic_enum?q=magic_enum) -[![License](https://img.shields.io/github/license/Neargye/magic_enum.svg)](LICENSE) -[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/JPMZqT9mgaUdooyC) -[![Compiler explorer](https://img.shields.io/badge/compiler_explorer-online-blue.svg)](https://godbolt.org/z/BxfmsH) - -# Magic Enum C++ - -Header-only C++17 library provides static reflection for enums, work with any enum type without any macro or boilerplate code. - -* `enum_cast` obtains enum value from string or integer. -* `enum_value` returns enum value at specified index. -* `enum_values` obtains enum value sequence. -* `enum_count` returns number of enum values. -* `enum_integer` obtains integer value from enum value. -* `enum_name` returns name from enum value. -* `enum_names` obtains string enum name sequence. -* `enum_entries` obtains pair (value enum, string enum name) sequence. -* `enum_index` obtains index in enum value sequence from enum value. -* `enum_contains` checks whether enum contains enumerator with such value. -* `enum_type_name` returns name of enum type. -* `enum_fuse` allows multidimensional switch/cases. -* `enum_switch` allows runtime enum value transformation to constexpr context. -* `enum_for_each` calls a function with all enum constexpr value. -* `is_unscoped_enum` checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration). -* `is_scoped_enum` checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations). -* `underlying_type` improved UB-free "SFINAE-friendly" [underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type). -* `ostream_operators` ostream operators for enums. -* `bitwise_operators` bitwise operators for enums. - -## Documentation - -* [Reference](doc/reference.md) -* [Limitations](doc/limitations.md) -* [Integration](#Integration) - -## Features - -* C++17 -* Header-only -* Dependency-free -* Compile-time -* Enum to string -* String to enum -* Iterating over enum - -## [Examples](example/example.cpp) - -```cpp -// For example color enum. -enum class Color { RED = 2, BLUE = 4, GREEN = 8 }; -``` - -* Enum value to string - - ```cpp - Color color = Color::RED; - auto color_name = magic_enum::enum_name(color); - // color_name -> "RED" - ``` - -* String to enum value - - ```cpp - std::string color_name{"GREEN"}; - auto color = magic_enum::enum_cast<Color>(color_name); - if (color.has_value()) { - // color.value() -> Color::GREEN - } - ``` - -* Integer to enum value - - ```cpp - int color_integer = 2; - auto color = magic_enum::enum_cast<Color>(color_integer); - if (color.has_value()) { - // color.value() -> Color::RED - } - ``` - -* Indexed access to enum value - - ```cpp - std::size_t i = 1; - Color color = magic_enum::enum_value<Color>(i); - // color -> Color::BLUE - ``` - -* Enum value sequence - - ```cpp - constexpr auto colors = magic_enum::enum_values<Color>(); - // colors -> {Color::RED, Color::BLUE, Color::GREEN} - // colors[0] -> Color::RED - ``` - -* Number of enum elements - - ```cpp - constexpr std::size_t color_count = magic_enum::enum_count<Color>(); - // color_count -> 3 - ``` - -* Enum value to integer - - ```cpp - Color color = Color::RED; - auto color_integer = magic_enum::enum_integer(color); - // color -> 2 - ``` - -* Enum names sequence - - ```cpp - constexpr auto color_names = magic_enum::enum_names<Color>(); - // color_names -> {"RED", "BLUE", "GREEN"} - // color_names[0] -> "RED" - ``` - -* Enum entries sequence - - ```cpp - constexpr auto color_entries = magic_enum::enum_entries<Color>(); - // color_entries -> {{Color::RED, "RED"}, {Color::BLUE, "BLUE"}, {Color::GREEN, "GREEN"}} - // color_entries[0].first -> Color::RED - // color_entries[0].second -> "RED" - ``` - -* Enum fusion for multi-level switch/case statements - - ```cpp - switch (magic_enum::enum_fuse(color, direction).value()) { - case magic_enum::enum_fuse(Color::RED, Directions::Up).value(): // ... - case magic_enum::enum_fuse(Color::BLUE, Directions::Down).value(): // ... - // ... - } - ``` - -* Enum switch runtime value as constexpr constant - ```cpp - Color color = Color::RED; - - magic_enum::enum_switch([] (auto val) { - constexpr Color c_color = val; - // ... - }, color); - ``` - -* Enum iterate for each enum as constexpr constant - ```cpp - magic_enum::enum_for_each<Color>([] (auto val) { - constexpr Color c_color = val; - // ... - }); - ``` - -* Ostream operator for enum - - ```cpp - using namespace magic_enum::ostream_operators; // out-of-the-box ostream operators for enums. - Color color = Color::BLUE; - std::cout << color << std::endl; // "BLUE" - ``` - -* Bitwise operator for enum - - ```cpp - enum class Flags { A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3 }; - using namespace magic_enum::bitwise_operators; // out-of-the-box bitwise operators for enums. - // Support operators: ~, |, &, ^, |=, &=, ^=. - Flags flags = Flags::A | Flags::B & ~Flags::C; - ``` - -* Checks whether type is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration). - - ```cpp - enum color { red, green, blue }; - enum class direction { left, right }; - - magic_enum::is_unscoped_enum<color>::value -> true - magic_enum::is_unscoped_enum<direction>::value -> false - magic_enum::is_unscoped_enum<int>::value -> false - - // Helper variable template. - magic_enum::is_unscoped_enum_v<color> -> true - ``` - -* Checks whether type is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations). - - ```cpp - enum color { red, green, blue }; - enum class direction { left, right }; - - magic_enum::is_scoped_enum<color>::value -> false - magic_enum::is_scoped_enum<direction>::value -> true - magic_enum::is_scoped_enum<int>::value -> false - - // Helper variable template. - magic_enum::is_scoped_enum_v<direction> -> true - ``` - -* Static storage enum variable to string - This version is much lighter on the compile times and is not restricted to the enum_range [limitation](doc/limitations.md). - - ```cpp - constexpr Color color = Color::BLUE; - constexpr auto color_name = magic_enum::enum_name<color>(); - // color_name -> "BLUE" - ``` - -## Remarks - -* `magic_enum` does not pretend to be a silver bullet for reflection for enums, it was originally designed for small enum. - -* Before use, read the [limitations](doc/limitations.md) of functionality. - -## Integration - -* You should add the required file [magic_enum.hpp](include/magic_enum.hpp). - -* If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [magic-enum package](https://github.com/microsoft/vcpkg/tree/master/ports/magic-enum). - -* If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `magic_enum/x.y.z` to your conan's requires, where `x.y.z` is the release version you want to use. - -* If you are using [Build2](https://build2.org/) to build and manage your dependencies, add `depends: magic_enum ^x.y.z` to the manifest file where `x.y.z` is the release version you want to use. You can then import the target using `magic_enum%lib{magic_enum}`. - -* Alternatively, you can use something like [CPM](https://github.com/TheLartians/CPM) which is based on CMake's `Fetch_Content` module. - - ```cmake - CPMAddPackage( - NAME magic_enum - GITHUB_REPOSITORY Neargye/magic_enum - GIT_TAG x.y.z # Where `x.y.z` is the release version you want to use. - ) - ``` - -* Bazel is also supported, simply add to your WORKSPACE file: - - ``` - http_archive( - name = "magic_enum", - strip_prefix = "magic_enum-<commit>", - urls = ["https://github.com/Neargye/magic_enum/archive/<commit>.zip"], - ) - ``` - - To use bazel inside the repository it's possible to do: - - ``` - bazel build //... - bazel test //... - bazel run //:example - ``` - - (Note that you must use a supported compiler or specify it with `export CC= <compiler>`.) - -* If you are using [Ros](https://www.ros.org/), you can include this package by adding `<depend>magic_enum</depend>` to your package.xml and include this package in your workspace. In your CMakeLists.txt add the following: - ```cmake - find_package(magic_enum CONFIG REQUIRED) - ... - target_link_libraries(your_executable magic_enum::magic_enum) - ``` - -## Compiler compatibility - -* Clang/LLVM >= 6 -* MSVC++ >= 14.11 / Visual Studio >= 2017 -* Xcode >= 10 -* GCC >= 9 -* MinGW >= 9 - -## Licensed under the [MIT License](LICENSE) diff --git a/src/libs/magic_enum/include/magic_enum.hpp b/src/libs/magic_enum/include/magic_enum.hpp deleted file mode 100644 index 5c5932a9f..000000000 --- a/src/libs/magic_enum/include/magic_enum.hpp +++ /dev/null @@ -1,1389 +0,0 @@ -// __ __ _ ______ _____ -// | \/ | (_) | ____| / ____|_ _ -// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ -// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| -// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| -// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| -// __/ | https://github.com/Neargye/magic_enum -// |___/ version 0.8.0 -// -// Licensed under the MIT License <http://opensource.org/licenses/MIT>. -// SPDX-License-Identifier: MIT -// Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef NEARGYE_MAGIC_ENUM_HPP -#define NEARGYE_MAGIC_ENUM_HPP - -#define MAGIC_ENUM_VERSION_MAJOR 0 -#define MAGIC_ENUM_VERSION_MINOR 8 -#define MAGIC_ENUM_VERSION_PATCH 0 - -#include <array> -#include <cassert> -#include <cstdint> -#include <cstddef> -#include <iosfwd> -#include <limits> -#include <type_traits> -#include <utility> -#include <variant> - -#if defined(MAGIC_ENUM_CONFIG_FILE) -#include MAGIC_ENUM_CONFIG_FILE -#endif - -#if !defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) -#include <optional> -#endif -#if !defined(MAGIC_ENUM_USING_ALIAS_STRING) -#include <string> -#endif -#if !defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) -#include <string_view> -#endif - -#if defined(__clang__) -# pragma clang diagnostic push -#elif defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'. -#elif defined(_MSC_VER) -# pragma warning(push) -# pragma warning(disable : 26495) // Variable 'static_string<N>::chars_' is uninitialized. -# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value. -# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. -# pragma warning(disable : 4514) // Unreferenced inline function has been removed. -#endif - -// Checks magic_enum compiler compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910 -# undef MAGIC_ENUM_SUPPORTED -# define MAGIC_ENUM_SUPPORTED 1 -#endif - -// Checks magic_enum compiler aliases compatibility. -#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920 -# undef MAGIC_ENUM_SUPPORTED_ALIASES -# define MAGIC_ENUM_SUPPORTED_ALIASES 1 -#endif - -// Enum value must be greater or equals than MAGIC_ENUM_RANGE_MIN. By default MAGIC_ENUM_RANGE_MIN = -128. -// If need another min range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN. -#if !defined(MAGIC_ENUM_RANGE_MIN) -# define MAGIC_ENUM_RANGE_MIN -128 -#endif - -// Enum value must be less or equals than MAGIC_ENUM_RANGE_MAX. By default MAGIC_ENUM_RANGE_MAX = 128. -// If need another max range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MAX. -#if !defined(MAGIC_ENUM_RANGE_MAX) -# define MAGIC_ENUM_RANGE_MAX 128 -#endif - -namespace magic_enum { - -// If need another optional type, define the macro MAGIC_ENUM_USING_ALIAS_OPTIONAL. -#if defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL) -MAGIC_ENUM_USING_ALIAS_OPTIONAL -#else -using std::optional; -#endif - -// If need another string_view type, define the macro MAGIC_ENUM_USING_ALIAS_STRING_VIEW. -#if defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW) -MAGIC_ENUM_USING_ALIAS_STRING_VIEW -#else -using std::string_view; -#endif - -// If need another string type, define the macro MAGIC_ENUM_USING_ALIAS_STRING. -#if defined(MAGIC_ENUM_USING_ALIAS_STRING) -MAGIC_ENUM_USING_ALIAS_STRING -#else -using std::string; -#endif - -namespace customize { - -// Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]. By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128. -// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX. -// If need another range for specific enum type, add specialization enum_range for necessary enum type. -template <typename E> -struct enum_range { - static_assert(std::is_enum_v<E>, "magic_enum::customize::enum_range requires enum type."); - static constexpr int min = MAGIC_ENUM_RANGE_MIN; - static constexpr int max = MAGIC_ENUM_RANGE_MAX; - static_assert(max > min, "magic_enum::customize::enum_range requires max > min."); -}; - -static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN."); -static_assert((MAGIC_ENUM_RANGE_MAX - MAGIC_ENUM_RANGE_MIN) < (std::numeric_limits<std::uint16_t>::max)(), "MAGIC_ENUM_RANGE must be less than UINT16_MAX."); - -namespace detail { -enum class default_customize_tag {}; -enum class invalid_customize_tag {}; -} // namespace magic_enum::customize::detail - -using customize_t = std::variant<string_view, detail::default_customize_tag, detail::invalid_customize_tag>; - -// Default customize. -inline constexpr auto default_tag = detail::default_customize_tag{}; -// Invalid customize. -inline constexpr auto invalid_tag = detail::invalid_customize_tag{}; - -// If need custom names for enum, add specialization enum_name for necessary enum type. -template <typename E> -constexpr customize_t enum_name(E) noexcept { - return default_tag; -} - -// If need custom type name for enum, add specialization enum_type_name for necessary enum type. -template <typename E> -constexpr customize_t enum_type_name() noexcept { - return default_tag; -} - -} // namespace magic_enum::customize - -namespace detail { - -template <auto V, typename = std::enable_if_t<std::is_enum_v<std::decay_t<decltype(V)>>>> -using enum_constant = std::integral_constant<std::decay_t<decltype(V)>, V>; - -template <typename... T> -inline constexpr bool always_false_v = false; - -template <typename T> -struct supported -#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT) - : std::true_type {}; -#else - : std::false_type {}; -#endif - -template <typename T, typename = void> -struct has_is_flags : std::false_type {}; - -template <typename T> -struct has_is_flags<T, std::void_t<decltype(customize::enum_range<T>::is_flags)>> : std::bool_constant<std::is_same_v<bool, std::decay_t<decltype(customize::enum_range<T>::is_flags)>>> {}; - -template <typename T, typename = void> -struct range_min : std::integral_constant<int, MAGIC_ENUM_RANGE_MIN> {}; - -template <typename T> -struct range_min<T, std::void_t<decltype(customize::enum_range<T>::min)>> : std::integral_constant<decltype(customize::enum_range<T>::min), customize::enum_range<T>::min> {}; - -template <typename T, typename = void> -struct range_max : std::integral_constant<int, MAGIC_ENUM_RANGE_MAX> {}; - -template <typename T> -struct range_max<T, std::void_t<decltype(customize::enum_range<T>::max)>> : std::integral_constant<decltype(customize::enum_range<T>::max), customize::enum_range<T>::max> {}; - -template <std::size_t N> -class static_string { - public: - constexpr explicit static_string(string_view str) noexcept : static_string{str, std::make_index_sequence<N>{}} { - assert(str.size() == N); - } - - constexpr const char* data() const noexcept { return chars_; } - - constexpr std::size_t size() const noexcept { return N; } - - constexpr operator string_view() const noexcept { return {data(), size()}; } - - private: - template <std::size_t... I> - constexpr static_string(string_view str, std::index_sequence<I...>) noexcept : chars_{str[I]..., '\0'} {} - - char chars_[N + 1]; -}; - -template <> -class static_string<0> { - public: - constexpr explicit static_string() = default; - - constexpr explicit static_string(string_view) noexcept {} - - constexpr const char* data() const noexcept { return nullptr; } - - constexpr std::size_t size() const noexcept { return 0; } - - constexpr operator string_view() const noexcept { return {}; } -}; - -constexpr string_view pretty_name(string_view name) noexcept { - for (std::size_t i = name.size(); i > 0; --i) { - if (!((name[i - 1] >= '0' && name[i - 1] <= '9') || - (name[i - 1] >= 'a' && name[i - 1] <= 'z') || - (name[i - 1] >= 'A' && name[i - 1] <= 'Z') || -#if defined(MAGIC_ENUM_ENABLE_NONASCII) - (name[i - 1] & 0x80) || -#endif - (name[i - 1] == '_'))) { - name.remove_prefix(i); - break; - } - } - - if (name.size() > 0 && ((name.front() >= 'a' && name.front() <= 'z') || - (name.front() >= 'A' && name.front() <= 'Z') || -#if defined(MAGIC_ENUM_ENABLE_NONASCII) - (name.front() & 0x80) || -#endif - (name.front() == '_'))) { - return name; - } - - return {}; // Invalid name. -} - -class case_insensitive { - static constexpr char to_lower(char c) noexcept { - return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + ('a' - 'A')) : c; - } - - public: - template <typename L, typename R> - constexpr auto operator()([[maybe_unused]] L lhs, [[maybe_unused]] R rhs) const noexcept -> std::enable_if_t<std::is_same_v<std::decay_t<L>, char> && std::is_same_v<std::decay_t<R>, char>, bool> { -#if defined(MAGIC_ENUM_ENABLE_NONASCII) - static_assert(always_false_v<L, R>, "magic_enum::case_insensitive not supported Non-ASCII feature."); - return false; -#else - return to_lower(lhs) == to_lower(rhs); -#endif - } -}; - -constexpr std::size_t find(string_view str, char c) noexcept { -#if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) -// https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc -// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html - constexpr bool workaround = true; -#else - constexpr bool workaround = false; -#endif - - if constexpr (workaround) { - for (std::size_t i = 0; i < str.size(); ++i) { - if (str[i] == c) { - return i; - } - } - - return string_view::npos; - } else { - return str.find_first_of(c); - } -} - -template <typename T, std::size_t N, std::size_t... I> -constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N], std::index_sequence<I...>) noexcept { - return {{a[I]...}}; -} - -template <typename BinaryPredicate> -constexpr bool is_default_predicate() noexcept { - return std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<string_view::value_type>> || - std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<>>; -} - -template <typename BinaryPredicate> -constexpr bool is_nothrow_invocable() { - return is_default_predicate<BinaryPredicate>() || - std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>; -} - -template <typename BinaryPredicate> -constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(is_nothrow_invocable<BinaryPredicate>()) { -#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__) - // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html - // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html - constexpr bool workaround = true; -#else - constexpr bool workaround = false; -#endif - - if constexpr (!is_default_predicate<BinaryPredicate>() || workaround) { - if (lhs.size() != rhs.size()) { - return false; - } - - const auto size = lhs.size(); - for (std::size_t i = 0; i < size; ++i) { - if (!p(lhs[i], rhs[i])) { - return false; - } - } - - return true; - } else { - return lhs == rhs; - } -} - -template <typename L, typename R> -constexpr bool cmp_less(L lhs, R rhs) noexcept { - static_assert(std::is_integral_v<L> && std::is_integral_v<R>, "magic_enum::detail::cmp_less requires integral type."); - - if constexpr (std::is_same_v<L, R>) { - return lhs < rhs; - } else if constexpr (std::is_signed_v<L> == std::is_signed_v<R>) { - // If same signedness (both signed or both unsigned). - return lhs < rhs; - } else if constexpr (std::is_same_v<L, bool>) { // bool special case - return static_cast<R>(lhs) < rhs; - } else if constexpr (std::is_same_v<R, bool>) { // bool special case - return lhs < static_cast<L>(rhs); - } else if constexpr (std::is_signed_v<R>) { - // If 'right' is negative, then result is 'false', otherwise cast & compare. - return rhs > 0 && lhs < static_cast<std::make_unsigned_t<R>>(rhs); - } else { - // If 'left' is negative, then result is 'true', otherwise cast & compare. - return lhs < 0 || static_cast<std::make_unsigned_t<L>>(lhs) < rhs; - } -} - -template <typename I> -constexpr I log2(I value) noexcept { - static_assert(std::is_integral_v<I>, "magic_enum::detail::log2 requires integral type."); - - if constexpr (std::is_same_v<I, bool>) { // bool special case - return assert(false), value; - } else { - auto ret = I{0}; - for (; value > I{1}; value >>= I{1}, ++ret) {} - - return ret; - } -} - -template <typename T> -inline constexpr bool is_enum_v = std::is_enum_v<T> && std::is_same_v<T, std::decay_t<T>>; - -template <typename E> -constexpr auto n() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::n requires enum type."); - - [[maybe_unused]] constexpr auto custom = customize::enum_type_name<E>(); - static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type."); - if constexpr (custom.index() == 0) { - constexpr auto name = std::get<string_view>(custom); - static_assert(!name.empty(), "magic_enum::customize requires not empty string."); - return static_string<name.size()>{name}; - } else if constexpr (custom.index() == 1 && supported<E>::value) { -#if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); -#elif defined(_MSC_VER) - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -#else - constexpr auto name = string_view{}; -#endif - return static_string<name.size()>{name}; - } else { - return static_string<0>{}; // Unsupported compiler or Invalid customize. - } -} - -template <typename E> -inline constexpr auto type_name_v = n<E>(); - -template <typename E, E V> -constexpr auto n() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::n requires enum type."); - - [[maybe_unused]] constexpr auto custom = customize::enum_name<E>(V); - static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type."); - if constexpr (custom.index() == 0) { - constexpr auto name = std::get<string_view>(custom); - static_assert(!name.empty(), "magic_enum::customize requires not empty string."); - return static_string<name.size()>{name}; - } else if constexpr (custom.index() == 1 && supported<E>::value) { -#if defined(__clang__) || defined(__GNUC__) - constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2}); -#elif defined(_MSC_VER) - constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17}); -#else - constexpr auto name = string_view{}; -#endif - return static_string<name.size()>{name}; - } else { - return static_string<0>{}; // Unsupported compiler or Invalid customize. - } -} - -template <typename E, E V> -inline constexpr auto enum_name_v = n<E, V>(); - -template <typename E, auto V> -constexpr bool is_valid() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::is_valid requires enum type."); - - return n<E, static_cast<E>(V)>().size() != 0; -} - -template <typename E, int O, bool IsFlags, typename U = std::underlying_type_t<E>> -constexpr E value(std::size_t i) noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::value requires enum type."); - - if constexpr (std::is_same_v<U, bool>) { // bool special case - static_assert(O == 0, "magic_enum::detail::value requires valid offset."); - - return static_cast<E>(i); - } else if constexpr (IsFlags) { - return static_cast<E>(U{1} << static_cast<U>(static_cast<int>(i) + O)); - } else { - return static_cast<E>(static_cast<int>(i) + O); - } -} - -template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>> -constexpr int reflected_min() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::reflected_min requires enum type."); - - if constexpr (IsFlags) { - return 0; - } else { - constexpr auto lhs = range_min<E>::value; - constexpr auto rhs = (std::numeric_limits<U>::min)(); - - if constexpr (cmp_less(rhs, lhs)) { - return lhs; - } else { - return rhs; - } - } -} - -template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>> -constexpr int reflected_max() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::reflected_max requires enum type."); - - if constexpr (IsFlags) { - return std::numeric_limits<U>::digits - 1; - } else { - constexpr auto lhs = range_max<E>::value; - constexpr auto rhs = (std::numeric_limits<U>::max)(); - - if constexpr (cmp_less(lhs, rhs)) { - return lhs; - } else { - return rhs; - } - } -} - -template <typename E, bool IsFlags> -inline constexpr auto reflected_min_v = reflected_min<E, IsFlags>(); - -template <typename E, bool IsFlags> -inline constexpr auto reflected_max_v = reflected_max<E, IsFlags>(); - -template <std::size_t N> -constexpr std::size_t values_count(const bool (&valid)[N]) noexcept { - auto count = std::size_t{0}; - for (std::size_t i = 0; i < N; ++i) { - if (valid[i]) { - ++count; - } - } - - return count; -} - -template <typename E, bool IsFlags, int Min, std::size_t... I> -constexpr auto values(std::index_sequence<I...>) noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::values requires enum type."); - constexpr bool valid[sizeof...(I)] = {is_valid<E, value<E, Min, IsFlags>(I)>()...}; - constexpr std::size_t count = values_count(valid); - - if constexpr (count > 0) { - E values[count] = {}; - for (std::size_t i = 0, v = 0; v < count; ++i) { - if (valid[i]) { - values[v++] = value<E, Min, IsFlags>(i); - } - } - - return to_array(values, std::make_index_sequence<count>{}); - } else { - return std::array<E, 0>{}; - } -} - -template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>> -constexpr auto values() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::values requires enum type."); - constexpr auto min = reflected_min_v<E, IsFlags>; - constexpr auto max = reflected_max_v<E, IsFlags>; - constexpr auto range_size = max - min + 1; - static_assert(range_size > 0, "magic_enum::enum_range requires valid size."); - static_assert(range_size < (std::numeric_limits<std::uint16_t>::max)(), "magic_enum::enum_range requires valid size."); - - return values<E, IsFlags, reflected_min_v<E, IsFlags>>(std::make_index_sequence<range_size>{}); -} - -template <typename E, typename U = std::underlying_type_t<E>> -constexpr bool is_flags_enum() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::is_flags_enum requires enum type."); - - if constexpr (has_is_flags<E>::value) { - return customize::enum_range<E>::is_flags; - } else if constexpr (std::is_same_v<U, bool>) { // bool special case - return false; - } else { -#if defined(MAGIC_ENUM_NO_CHECK_FLAGS) - return false; -#else - constexpr auto flags_values = values<E, true>(); - constexpr auto default_values = values<E, false>(); - if (flags_values.size() == 0 || default_values.size() > flags_values.size()) { - return false; - } - for (std::size_t i = 0; i < default_values.size(); ++i) { - const auto v = static_cast<U>(default_values[i]); - if (v != 0 && (v & (v - 1)) != 0) { - return false; - } - } - return flags_values.size() > 0; -#endif - } -} - -template <typename E> -inline constexpr bool is_flags_v = is_flags_enum<E>(); - -template <typename E> -inline constexpr std::array values_v = values<E, is_flags_v<E>>(); - -template <typename E, typename D = std::decay_t<E>> -using values_t = decltype((values_v<D>)); - -template <typename E> -inline constexpr auto count_v = values_v<E>.size(); - -template <typename E, typename U = std::underlying_type_t<E>> -inline constexpr auto min_v = (count_v<E> > 0) ? static_cast<U>(values_v<E>.front()) : U{0}; - -template <typename E, typename U = std::underlying_type_t<E>> -inline constexpr auto max_v = (count_v<E> > 0) ? static_cast<U>(values_v<E>.back()) : U{0}; - -template <typename E, std::size_t... I> -constexpr auto names(std::index_sequence<I...>) noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::names requires enum type."); - - return std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E>[I]>...}}; -} - -template <typename E> -inline constexpr std::array names_v = names<E>(std::make_index_sequence<count_v<E>>{}); - -template <typename E, typename D = std::decay_t<E>> -using names_t = decltype((names_v<D>)); - -template <typename E, std::size_t... I> -constexpr auto entries(std::index_sequence<I...>) noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::entries requires enum type."); - - return std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E>[I], enum_name_v<E, values_v<E>[I]>}...}}; -} - -template <typename E> -inline constexpr std::array entries_v = entries<E>(std::make_index_sequence<count_v<E>>{}); - -template <typename E, typename D = std::decay_t<E>> -using entries_t = decltype((entries_v<D>)); - -template <typename E, typename U = std::underlying_type_t<E>> -constexpr bool is_sparse() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::is_sparse requires enum type."); - - if constexpr (count_v<E> == 0) { - return false; - } else if constexpr (std::is_same_v<U, bool>) { // bool special case - return false; - } else { - constexpr auto max = is_flags_v<E> ? log2(max_v<E>) : max_v<E>; - constexpr auto min = is_flags_v<E> ? log2(min_v<E>) : min_v<E>; - constexpr auto range_size = max - min + 1; - - return range_size != count_v<E>; - } -} - -template <typename E> -inline constexpr bool is_sparse_v = is_sparse<E>(); - -template <typename E, typename U = std::underlying_type_t<E>> -constexpr U values_ors() noexcept { - static_assert(is_enum_v<E>, "magic_enum::detail::values_ors requires enum type."); - - auto ors = U{0}; - for (std::size_t i = 0; i < count_v<E>; ++i) { - ors |= static_cast<U>(values_v<E>[i]); - } - - return ors; -} - -template <bool, typename R> -struct enable_if_enum {}; - -template <typename R> -struct enable_if_enum<true, R> { - using type = R; - static_assert(supported<R>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility)."); -}; - -template <typename T, typename R, typename BinaryPredicate = std::equal_to<>> -using enable_if_t = typename enable_if_enum<std::is_enum_v<std::decay_t<T>> && std::is_invocable_r_v<bool, BinaryPredicate, char, char>, R>::type; - -template <typename T, typename Enable = std::enable_if_t<std::is_enum_v<std::decay_t<T>>>> -using enum_concept = T; - -template <typename T, bool = std::is_enum_v<T>> -struct is_scoped_enum : std::false_type {}; - -template <typename T> -struct is_scoped_enum<T, true> : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {}; - -template <typename T, bool = std::is_enum_v<T>> -struct is_unscoped_enum : std::false_type {}; - -template <typename T> -struct is_unscoped_enum<T, true> : std::bool_constant<std::is_convertible_v<T, std::underlying_type_t<T>>> {}; - -template <typename T, bool = std::is_enum_v<std::decay_t<T>>> -struct underlying_type {}; - -template <typename T> -struct underlying_type<T, true> : std::underlying_type<std::decay_t<T>> {}; - -template <typename Value, typename = void> -struct constexpr_hash_t; - -template <typename Value> -struct constexpr_hash_t<Value, std::enable_if_t<is_enum_v<Value>>> { - constexpr auto operator()(Value value) const noexcept { - using U = typename underlying_type<Value>::type; - if constexpr (std::is_same_v<U, bool>) { // bool special case - return static_cast<std::size_t>(value); - } else { - return static_cast<U>(value); - } - } - using secondary_hash = constexpr_hash_t; -}; - -template <typename Value> -struct constexpr_hash_t<Value, std::enable_if_t<std::is_same_v<Value, string_view>>> { - static constexpr std::uint32_t crc_table[256] { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, - 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L, - 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, - 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, - 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, - 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, - 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, - 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, - 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, - 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, - 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, - 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L, - 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, - 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L, - 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, - 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, - 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, - 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, - 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, - 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, - 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, - 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, - 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, - 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL, - 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, - 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL - }; - constexpr std::uint32_t operator()(string_view value) const noexcept { - auto crc = static_cast<std::uint32_t>(0xffffffffL); - for (const auto c : value) { - crc = (crc >> 8) ^ crc_table[(crc ^ static_cast<std::uint32_t>(c)) & 0xff]; - } - return crc ^ 0xffffffffL; - } - - struct secondary_hash { - constexpr std::uint32_t operator()(string_view value) const noexcept { - auto acc = static_cast<std::uint64_t>(2166136261ULL); - for (const auto c : value) { - acc = ((acc ^ static_cast<std::uint64_t>(c)) * static_cast<std::uint64_t>(16777619ULL)) & (std::numeric_limits<std::uint32_t>::max)(); - } - return static_cast<std::uint32_t>(acc); - } - }; -}; - -template <typename Hash> -constexpr static Hash hash_v{}; - -template <auto* GlobValues, typename Hash> -constexpr auto calculate_cases(std::size_t Page) noexcept { - constexpr std::array values = *GlobValues; - constexpr std::size_t size = values.size(); - - using switch_t = std::invoke_result_t<Hash, typename decltype(values)::value_type>; - static_assert(std::is_integral_v<switch_t> && !std::is_same_v<switch_t, bool>); - const std::size_t values_to = (std::min)(static_cast<std::size_t>(256), size - Page); - - std::array<switch_t, 256> result{}; - auto fill = result.begin(); - { - auto first = values.begin() + static_cast<std::ptrdiff_t>(Page); - auto last = values.begin() + static_cast<std::ptrdiff_t>(Page + values_to); - while (first != last) { - *fill++ = hash_v<Hash>(*first++); - } - } - - // dead cases, try to avoid case collisions - for (switch_t last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits<switch_t>::max)(); *fill++ = ++last_value) { - } - - { - auto it = result.begin(); - auto last_value = (std::numeric_limits<switch_t>::min)(); - for (; fill != result.end(); *fill++ = last_value++) { - while (last_value == *it) { - ++last_value, ++it; - } - } - } - - return result; -} - -template <typename R, typename F, typename... Args> -constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>) { - if constexpr (std::is_void_v<R>) { - std::forward<F>(f)(std::forward<Args>(args)...); - } else { - return static_cast<R>(std::forward<F>(f)(std::forward<Args>(args)...)); - } -} - -enum class case_call_t { - index, value -}; - -template <typename T = void> -inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; }; - -template <> -inline constexpr auto default_result_type_lambda<void> = []() noexcept {}; - -template <auto* Arr, typename Hash> -constexpr bool no_duplicate() noexcept { - using value_t = std::decay_t<decltype((*Arr)[0])>; - using hash_value_t = std::invoke_result_t<Hash, value_t>; - std::array<hash_value_t, Arr->size()> hashes{}; - std::size_t size = 0; - for (auto elem : *Arr) { - hashes[size] = hash_v<Hash>(elem); - for (auto i = size++; i > 0; --i) { - if (hashes[i] < hashes[i - 1]) { - auto tmp = hashes[i]; - hashes[i] = hashes[i - 1]; - hashes[i - 1] = tmp; - } else if (hashes[i] == hashes[i - 1]) { - return false; - } else { - break; - } - } - } - return true; -} - -#define MAGIC_ENUM_FOR_EACH_256(T) T(0)T(1)T(2)T(3)T(4)T(5)T(6)T(7)T(8)T(9)T(10)T(11)T(12)T(13)T(14)T(15)T(16)T(17)T(18)T(19)T(20)T(21)T(22)T(23)T(24)T(25)T(26)T(27)T(28)T(29)T(30)T(31) \ - T(32)T(33)T(34)T(35)T(36)T(37)T(38)T(39)T(40)T(41)T(42)T(43)T(44)T(45)T(46)T(47)T(48)T(49)T(50)T(51)T(52)T(53)T(54)T(55)T(56)T(57)T(58)T(59)T(60)T(61)T(62)T(63) \ - T(64)T(65)T(66)T(67)T(68)T(69)T(70)T(71)T(72)T(73)T(74)T(75)T(76)T(77)T(78)T(79)T(80)T(81)T(82)T(83)T(84)T(85)T(86)T(87)T(88)T(89)T(90)T(91)T(92)T(93)T(94)T(95) \ - T(96)T(97)T(98)T(99)T(100)T(101)T(102)T(103)T(104)T(105)T(106)T(107)T(108)T(109)T(110)T(111)T(112)T(113)T(114)T(115)T(116)T(117)T(118)T(119)T(120)T(121)T(122)T(123)T(124)T(125)T(126)T(127) \ - T(128)T(129)T(130)T(131)T(132)T(133)T(134)T(135)T(136)T(137)T(138)T(139)T(140)T(141)T(142)T(143)T(144)T(145)T(146)T(147)T(148)T(149)T(150)T(151)T(152)T(153)T(154)T(155)T(156)T(157)T(158)T(159) \ - T(160)T(161)T(162)T(163)T(164)T(165)T(166)T(167)T(168)T(169)T(170)T(171)T(172)T(173)T(174)T(175)T(176)T(177)T(178)T(179)T(180)T(181)T(182)T(183)T(184)T(185)T(186)T(187)T(188)T(189)T(190)T(191) \ - T(192)T(193)T(194)T(195)T(196)T(197)T(198)T(199)T(200)T(201)T(202)T(203)T(204)T(205)T(206)T(207)T(208)T(209)T(210)T(211)T(212)T(213)T(214)T(215)T(216)T(217)T(218)T(219)T(220)T(221)T(222)T(223) \ - T(224)T(225)T(226)T(227)T(228)T(229)T(230)T(231)T(232)T(233)T(234)T(235)T(236)T(237)T(238)T(239)T(240)T(241)T(242)T(243)T(244)T(245)T(246)T(247)T(248)T(249)T(250)T(251)T(252)T(253)T(254)T(255) - -#define MAGIC_ENUM_CASE(val) \ - case cases[val]: \ - if constexpr ((val) + Page < size) { \ - if (!pred(values[val + Page], searched)) { \ - break; \ - } \ - if constexpr (CallValue == case_call_t::index) { \ - if constexpr (std::is_invocable_r_v<result_t, Lambda, std::integral_constant<std::size_t, val + Page>>) { \ - return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), std::integral_constant<std::size_t, val + Page>{}); \ - } else if constexpr (std::is_invocable_v<Lambda, std::integral_constant<std::size_t, val + Page>>) { \ - assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ - } \ - } else if constexpr (CallValue == case_call_t::value) { \ - if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \ - return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), enum_constant<values[val + Page]>{}); \ - } else if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \ - assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \ - } \ - } \ - break; \ - } else [[fallthrough]]; - -template <auto* GlobValues, - case_call_t CallValue, - std::size_t Page = 0, - typename Hash = constexpr_hash_t<typename std::decay_t<decltype(*GlobValues)>::value_type>, - typename Lambda, typename ResultGetterType = decltype(default_result_type_lambda<>), - typename BinaryPredicate = std::equal_to<>> -constexpr std::invoke_result_t<ResultGetterType> constexpr_switch( - Lambda&& lambda, - typename std::decay_t<decltype(*GlobValues)>::value_type searched, - ResultGetterType&& def = default_result_type_lambda<>, - BinaryPredicate&& pred = {}) { - using result_t = std::invoke_result_t<ResultGetterType>; - using hash_t = std::conditional_t<no_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>; - constexpr std::array values = *GlobValues; - constexpr std::size_t size = values.size(); - constexpr std::array cases = calculate_cases<GlobValues, hash_t>(Page); - - switch (hash_v<hash_t>(searched)) { - MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE) - default: - if constexpr (size > 256 + Page) { - return constexpr_switch<GlobValues, CallValue, Page + 256, Hash>(std::forward<Lambda>(lambda), searched, std::forward<ResultGetterType>(def)); - } - break; - } - return def(); -} - -#undef MAGIC_ENUM_FOR_EACH_256 -#undef MAGIC_ENUM_CASE - -template <typename E, typename Lambda, std::size_t... I> -constexpr auto for_each(Lambda&& lambda, std::index_sequence<I...>) { - static_assert(is_enum_v<E>, "magic_enum::detail::for_each requires enum type."); - constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> || ...); - constexpr bool all_same_return = (std::is_same_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[0]>>, std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> && ...); - - if constexpr (has_void_return) { - (lambda(enum_constant<values_v<E>[I]>{}), ...); - } else if constexpr (all_same_return) { - return std::array{lambda(enum_constant<values_v<E>[I]>{})...}; - } else { - return std::tuple{lambda(enum_constant<values_v<E>[I]>{})...}; - } -} - -} // namespace magic_enum::detail - -// Checks is magic_enum supported compiler. -inline constexpr bool is_magic_enum_supported = detail::supported<void>::value; - -template <typename T> -using Enum = detail::enum_concept<T>; - -// Checks whether T is an Unscoped enumeration type. -// Provides the member constant value which is equal to true, if T is an [Unscoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Unscoped_enumeration) type. Otherwise, value is equal to false. -template <typename T> -struct is_unscoped_enum : detail::is_unscoped_enum<T> {}; - -template <typename T> -inline constexpr bool is_unscoped_enum_v = is_unscoped_enum<T>::value; - -// Checks whether T is an Scoped enumeration type. -// Provides the member constant value which is equal to true, if T is an [Scoped enumeration](https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations) type. Otherwise, value is equal to false. -template <typename T> -struct is_scoped_enum : detail::is_scoped_enum<T> {}; - -template <typename T> -inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; - -// If T is a complete enumeration type, provides a member typedef type that names the underlying type of T. -// Otherwise, if T is not an enumeration type, there is no member type. Otherwise (T is an incomplete enumeration type), the program is ill-formed. -template <typename T> -struct underlying_type : detail::underlying_type<T> {}; - -template <typename T> -using underlying_type_t = typename underlying_type<T>::type; - -template <auto V> -using enum_constant = detail::enum_constant<V>; - -// Returns type name of enum. -template <typename E> -[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_t<E, string_view> { - constexpr string_view name = detail::type_name_v<std::decay_t<E>>; - static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name."); - - return name; -} - -// Returns number of enum values. -template <typename E> -[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_t<E, std::size_t> { - return detail::count_v<std::decay_t<E>>; -} - -// Returns enum value at specified index. -// No bounds checking is performed: the behavior is undefined if index >= number of enum values. -template <typename E> -[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t<E, std::decay_t<E>> { - using D = std::decay_t<E>; - - if constexpr (detail::is_sparse_v<D>) { - return assert((index < detail::count_v<D>)), detail::values_v<D>[index]; - } else { - constexpr bool is_flag = detail::is_flags_v<D>; - constexpr auto min = is_flag ? detail::log2(detail::min_v<D>) : detail::min_v<D>; - - return assert((index < detail::count_v<D>)), detail::value<D, min, is_flag>(index); - } -} - -// Returns enum value at specified index. -template <typename E, std::size_t I> -[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t<E, std::decay_t<E>> { - using D = std::decay_t<E>; - static_assert(I < detail::count_v<D>, "magic_enum::enum_value out of range."); - - return enum_value<D>(I); -} - -// Returns std::array with enum values, sorted by enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t<E, detail::values_t<E>> { - return detail::values_v<std::decay_t<E>>; -} - -// Returns integer value from enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_t<E, underlying_type_t<E>> { - return static_cast<underlying_type_t<E>>(value); -} - -// Returns underlying value from enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_underlying(E value) noexcept -> detail::enable_if_t<E, underlying_type_t<E>> { - return static_cast<underlying_type_t<E>>(value); -} - -// Obtains index in enum values from enum value. -// Returns optional with index. -template <typename E> -[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> { - using D = std::decay_t<E>; - using U = underlying_type_t<D>; - - if constexpr (detail::count_v<D> == 0) { - return {}; // Empty enum. - } else if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) { - return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>( - [](std::size_t i) { return optional<std::size_t>{i}; }, - value, - detail::default_result_type_lambda<optional<std::size_t>>); - } else { - const auto v = static_cast<U>(value); - if (v >= detail::min_v<D> && v <= detail::max_v<D>) { - return static_cast<std::size_t>(v - detail::min_v<D>); - } - return {}; // Invalid value or out of range. - } -} - -// Obtains index in enum values from static storage enum variable. -template <auto V> -[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> { - constexpr auto index = enum_index<std::decay_t<decltype(V)>>(V); - static_assert(index, "magic_enum::enum_index enum value does not have a index."); - - return *index; -} - -// Returns name from static storage enum variable. -// This version is much lighter on the compile times and is not restricted to the enum_range limitation. -template <auto V> -[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_t<decltype(V), string_view> { - constexpr string_view name = detail::enum_name_v<std::decay_t<decltype(V)>, V>; - static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name."); - - return name; -} - -// Returns name from enum value. -// If enum value does not have name or value out of range, returns empty string. -template <typename E> -[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> { - using D = std::decay_t<E>; - - if (const auto i = enum_index<D>(value)) { - return detail::names_v<D>[*i]; - } - return {}; -} - -// Returns name from enum-flags value. -// If enum-flags value does not have name or value out of range, returns empty string. -template <typename E> -[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t<E, string> { - using D = std::decay_t<E>; - using U = underlying_type_t<D>; - - if constexpr (detail::is_flags_v<D>) { - string name; - auto check_value = U{0}; - for (std::size_t i = 0; i < detail::count_v<D>; ++i) { - if (const auto v = static_cast<U>(enum_value<D>(i)); (static_cast<U>(value) & v) != 0) { - check_value |= v; - const auto n = detail::names_v<D>[i]; - if (!name.empty()) { - name.append(1, '|'); - } - name.append(n.data(), n.size()); - } - } - - if (check_value != 0 && check_value == static_cast<U>(value)) { - return name; - } - - return {}; // Invalid value or out of range. - } else { - return string{enum_name<D>(value)}; - } -} - -// Returns std::array with names, sorted by enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t<E, detail::names_t<E>> { - return detail::names_v<std::decay_t<E>>; -} - -// Returns std::array with pairs (value, name), sorted by enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t<E, detail::entries_t<E>> { - return detail::entries_v<std::decay_t<E>>; -} - -// Allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive); -inline constexpr auto case_insensitive = detail::case_insensitive{}; - -// Obtains enum value from integer value. -// Returns optional with enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> { - using D = std::decay_t<E>; - using U = underlying_type_t<D>; - - if constexpr (detail::count_v<D> == 0) { - return {}; // Empty enum. - } else if constexpr (detail::is_sparse_v<D>) { - if constexpr (detail::is_flags_v<D>) { - constexpr auto count = detail::count_v<D>; - auto check_value = U{0}; - for (std::size_t i = 0; i < count; ++i) { - if (const auto v = static_cast<U>(enum_value<D>(i)); (value & v) != 0) { - check_value |= v; - } - } - - if (check_value != 0 && check_value == value) { - return static_cast<D>(value); - } - return {}; // Invalid value or out of range. - } else { - return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>( - [](D v) { return optional<D>{v}; }, - static_cast<D>(value), - detail::default_result_type_lambda<optional<D>>); - } - } else { - constexpr auto min = detail::min_v<D>; - constexpr auto max = detail::is_flags_v<D> ? detail::values_ors<D>() : detail::max_v<D>; - - if (value >= min && value <= max) { - return static_cast<D>(value); - } - return {}; // Invalid value or out of range. - } -} - -// Obtains enum value from name. -// Returns optional with enum value. -template <typename E, typename BinaryPredicate = std::equal_to<>> -[[nodiscard]] constexpr auto enum_cast(string_view value, [[maybe_unused]] BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, optional<std::decay_t<E>>, BinaryPredicate> { - static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate."); - using D = std::decay_t<E>; - using U = underlying_type_t<D>; - - if constexpr (detail::count_v<D> == 0) { - return {}; // Empty enum. - } else if constexpr (detail::is_flags_v<D>) { - auto result = U{0}; - while (!value.empty()) { - const auto d = detail::find(value, '|'); - const auto s = (d == string_view::npos) ? value : value.substr(0, d); - auto f = U{0}; - for (std::size_t i = 0; i < detail::count_v<D>; ++i) { - if (detail::cmp_equal(s, detail::names_v<D>[i], p)) { - f = static_cast<U>(enum_value<D>(i)); - result |= f; - break; - } - } - if (f == U{0}) { - return {}; // Invalid value or out of range. - } - value.remove_prefix((d == string_view::npos) ? value.size() : d + 1); - } - - if (result != U{0}) { - return static_cast<D>(result); - } - return {}; // Invalid value or out of range. - } else if constexpr (detail::count_v<D> > 0) { - if constexpr (detail::is_default_predicate<BinaryPredicate>()) { - return detail::constexpr_switch<&detail::names_v<D>, detail::case_call_t::index>( - [](std::size_t i) { return optional<D>{detail::values_v<D>[i]}; }, - value, - detail::default_result_type_lambda<optional<D>>, - [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); }); - } else { - for (std::size_t i = 0; i < detail::count_v<D>; ++i) { - if (detail::cmp_equal(value, detail::names_v<D>[i], p)) { - return enum_value<D>(i); - } - } - return {}; // Invalid value or out of range. - } - } -} - -// Checks whether enum contains enumerator with such enum value. -template <typename E> -[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t<E, bool> { - using D = std::decay_t<E>; - using U = underlying_type_t<D>; - - return static_cast<bool>(enum_cast<D>(static_cast<U>(value))); -} - -// Checks whether enum contains enumerator with such integer value. -template <typename E> -[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> { - using D = std::decay_t<E>; - - return static_cast<bool>(enum_cast<D>(value)); -} - -// Checks whether enum contains enumerator with such name. -template <typename E, typename BinaryPredicate = std::equal_to<>> -[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> { - static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_contains requires bool(char, char) invocable predicate."); - using D = std::decay_t<E>; - - return static_cast<bool>(enum_cast<D>(value, std::forward<BinaryPredicate>(p))); -} - -template <typename Result = void, typename E, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t<E, Result> { - using D = std::decay_t<E>; - - return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>( - std::forward<Lambda>(lambda), - value, - detail::default_result_type_lambda<Result>); -} - -template <typename Result, typename E, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t<E, Result> { - using D = std::decay_t<E>; - - return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>( - std::forward<Lambda>(lambda), - value, - [&result] { return std::forward<Result>(result); }); -} - -template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> { - static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate."); - using D = std::decay_t<E>; - - if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) { - return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v); - } - return detail::default_result_type_lambda<Result>(); -} - -template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> { - static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate."); - using D = std::decay_t<E>; - - if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) { - return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result)); - } - return std::forward<Result>(result); -} - -template <typename E, typename Result = void, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value) -> detail::enable_if_t<E, Result> { - using D = std::decay_t<E>; - - if (const auto v = enum_cast<D>(value)) { - return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v); - } - return detail::default_result_type_lambda<Result>(); -} - -template <typename E, typename Result, typename Lambda> -constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result) -> detail::enable_if_t<E, Result> { - using D = std::decay_t<E>; - - if (const auto v = enum_cast<D>(value)) { - return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result)); - } - return std::forward<Result>(result); -} - -template <typename E, typename Lambda> -constexpr auto enum_for_each(Lambda&& lambda) { - using D = std::decay_t<E>; - static_assert(std::is_enum_v<D>, "magic_enum::enum_for_each requires enum type."); - - return detail::for_each<D>(std::forward<Lambda>(lambda), std::make_index_sequence<detail::count_v<D>>{}); -} - -namespace ostream_operators { - -template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0> -std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) { - using D = std::decay_t<E>; - using U = underlying_type_t<D>; - - if constexpr (detail::supported<D>::value) { - if (const auto name = enum_flags_name<D>(value); !name.empty()) { - for (const auto c : name) { - os.put(c); - } - return os; - } - } - return (os << static_cast<U>(value)); -} - -template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0> -std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) { - return value ? (os << *value) : os; -} - -} // namespace magic_enum::ostream_operators - -namespace istream_operators { - -template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0> -std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) { - using D = std::decay_t<E>; - - std::basic_string<Char, Traits> s; - is >> s; - if (const auto v = enum_cast<D>(s)) { - value = *v; - } else { - is.setstate(std::basic_ios<Char>::failbit); - } - return is; -} - -} // namespace magic_enum::istream_operators - -namespace iostream_operators { - -using namespace ostream_operators; -using namespace istream_operators; - -} // namespace magic_enum::iostream_operators - -namespace bitwise_operators { - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E operator~(E rhs) noexcept { - return static_cast<E>(~static_cast<underlying_type_t<E>>(rhs)); -} - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E operator|(E lhs, E rhs) noexcept { - return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) | static_cast<underlying_type_t<E>>(rhs)); -} - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E operator&(E lhs, E rhs) noexcept { - return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) & static_cast<underlying_type_t<E>>(rhs)); -} - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E operator^(E lhs, E rhs) noexcept { - return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) ^ static_cast<underlying_type_t<E>>(rhs)); -} - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E& operator|=(E& lhs, E rhs) noexcept { - return lhs = (lhs | rhs); -} - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E& operator&=(E& lhs, E rhs) noexcept { - return lhs = (lhs & rhs); -} - -template <typename E, detail::enable_if_t<E, int> = 0> -constexpr E& operator^=(E& lhs, E rhs) noexcept { - return lhs = (lhs ^ rhs); -} - -} // namespace magic_enum::bitwise_operators - -} // namespace magic_enum - -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#elif defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // NEARGYE_MAGIC_ENUM_HPP diff --git a/src/libs/magic_enum/include/magic_enum_format.hpp b/src/libs/magic_enum/include/magic_enum_format.hpp deleted file mode 100644 index a90288a85..000000000 --- a/src/libs/magic_enum/include/magic_enum_format.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// __ __ _ ______ _____ -// | \/ | (_) | ____| / ____|_ _ -// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ -// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| -// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| -// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| -// __/ | https://github.com/Neargye/magic_enum -// |___/ version 0.8.0 -// -// Licensed under the MIT License <http://opensource.org/licenses/MIT>. -// SPDX-License-Identifier: MIT -// Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef NEARGYE_MAGIC_ENUM_FORMAT_HPP -#define NEARGYE_MAGIC_ENUM_FORMAT_HPP - -#if !defined(__cpp_lib_format) -# error "Format is not supported" -#endif - -#include "magic_enum.hpp" - -#if !defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT) -# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT true -# define MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE -#endif // MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT - -namespace magic_enum::customize { - // customize enum to enable/disable automatic std::format - template <typename E> - constexpr bool enum_format_enabled() noexcept { - return MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT; - } -} // magic_enum::customize - -#include <format> - -template <typename E> -struct std::formatter<E, std::enable_if_t<std::is_enum_v<E> && magic_enum::customize::enum_format_enabled<E>(), char>> : std::formatter<std::string_view, char> { - auto format(E e, format_context& ctx) { - using D = std::decay_t<E>; - if constexpr (magic_enum::detail::is_flags_v<D>) { - if (auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) { - return this->std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx); - } - } else { - if (auto name = magic_enum::enum_name<D>(e); !name.empty()) { - return this->std::formatter<std::string_view, char>::format(std::string_view{name.data(), name.size()}, ctx); - } - } - constexpr auto type_name = magic_enum::enum_type_name<E>(); - throw std::format_error("Type of " + std::string{type_name.data(), type_name.size()} + " enum value: " + std::to_string(magic_enum::enum_integer<D>(e)) + " is not exists."); - } -}; - -#if defined(MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE) -# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT -# undef MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE -#endif // MAGIC_ENUM_DEFAULT_ENABLE_ENUM_FORMAT_AUTO_DEFINE - -#endif // NEARGYE_MAGIC_ENUM_FORMAT_HPP diff --git a/src/libs/magic_enum/include/magic_enum_fuse.hpp b/src/libs/magic_enum/include/magic_enum_fuse.hpp deleted file mode 100644 index 16f4834c8..000000000 --- a/src/libs/magic_enum/include/magic_enum_fuse.hpp +++ /dev/null @@ -1,89 +0,0 @@ -// __ __ _ ______ _____ -// | \/ | (_) | ____| / ____|_ _ -// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ -// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| -// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| -// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| -// __/ | https://github.com/Neargye/magic_enum -// |___/ version 0.8.0 -// -// Licensed under the MIT License <http://opensource.org/licenses/MIT>. -// SPDX-License-Identifier: MIT -// Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef NEARGYE_MAGIC_ENUM_FUSE_HPP -#define NEARGYE_MAGIC_ENUM_FUSE_HPP - -#include "magic_enum.hpp" - -namespace magic_enum { - -namespace detail { - -template <typename E> -constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept { - if (hash) { - if (const auto index = enum_index(value)) { - return (*hash << log2(enum_count<E>() + 1)) | *index; - } - } - return {}; -} - -template <typename E> -constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept { - return fuse_one_enum(0, value); -} - -template <typename E, typename... Es> -constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept { - return fuse_one_enum(fuse_enum(tail...), head); -} - -template <typename... Es> -constexpr auto typesafe_fuse_enum(Es... values) noexcept { - enum class enum_fuse_t : std::uintmax_t; - const auto fuse = fuse_enum(values...); - if (fuse) { - return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)}; - } - return optional<enum_fuse_t>{}; -} - -} // namespace magic_enum::detail - -// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements. -template <typename... Es> -[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept { - static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type."); - static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values."); - static_assert((detail::log2(enum_count<std::decay_t<Es>>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums"); -#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE) - const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...); -#else - const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...); -#endif - return assert(fuse), fuse; -} - -} // namespace magic_enum - -#endif // NEARGYE_MAGIC_ENUM_FUSE_HPP |