Commit 77ba000da4128c2ad0e10a2def9f32bcc7c2adfc

Authored by Peter M. Groen
1 parent 63c4d338

Implmenting ModbusBase class

include/modbus.h 0 → 100644
src/3rdparty/MagicEnum.hpp 0 → 100644
  1 +// __ __ _ ______ _____
  2 +// | \/ | (_) | ____| / ____|_ _
  3 +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
  4 +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
  5 +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
  6 +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
  7 +// __/ | https://github.com/Neargye/magic_enum
  8 +// |___/ version 0.7.3
  9 +//
  10 +// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
  11 +// SPDX-License-Identifier: MIT
  12 +// Copyright (c) 2019 - 2022 Daniil Goncharov <neargye@gmail.com>.
  13 +//
  14 +// Permission is hereby granted, free of charge, to any person obtaining a copy
  15 +// of this software and associated documentation files (the "Software"), to deal
  16 +// in the Software without restriction, including without limitation the rights
  17 +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  18 +// copies of the Software, and to permit persons to whom the Software is
  19 +// furnished to do so, subject to the following conditions:
  20 +//
  21 +// The above copyright notice and this permission notice shall be included in all
  22 +// copies or substantial portions of the Software.
  23 +//
  24 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  25 +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  26 +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  27 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  28 +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  29 +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  30 +// SOFTWARE.
  31 +
  32 +#ifndef NEARGYE_MAGIC_ENUM_HPP
  33 +#define NEARGYE_MAGIC_ENUM_HPP
  34 +
  35 +#define MAGIC_ENUM_VERSION_MAJOR 0
  36 +#define MAGIC_ENUM_VERSION_MINOR 7
  37 +#define MAGIC_ENUM_VERSION_PATCH 3
  38 +
  39 +#include <array>
  40 +#include <cassert>
  41 +#include <cstdint>
  42 +#include <cstddef>
  43 +#include <iosfwd>
  44 +#include <limits>
  45 +#include <type_traits>
  46 +#include <utility>
  47 +#include <variant>
  48 +
  49 +#if defined(MAGIC_ENUM_CONFIG_FILE)
  50 +#include MAGIC_ENUM_CONFIG_FILE
  51 +#endif
  52 +
  53 +#if !defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL)
  54 +#include <optional>
  55 +#endif
  56 +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING)
  57 +#include <string>
  58 +#endif
  59 +#if !defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW)
  60 +#include <string_view>
  61 +#endif
  62 +
  63 +#if defined(__clang__)
  64 +# pragma clang diagnostic push
  65 +#elif defined(__GNUC__)
  66 +# pragma GCC diagnostic push
  67 +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // May be used uninitialized 'return {};'.
  68 +#elif defined(_MSC_VER)
  69 +# pragma warning(push)
  70 +# pragma warning(disable : 26495) // Variable 'static_string<N>::chars_' is uninitialized.
  71 +# pragma warning(disable : 28020) // Arithmetic overflow: Using operator '-' on a 4 byte value and then casting the result to a 8 byte value.
  72 +# pragma warning(disable : 26451) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call.
  73 +#endif
  74 +
  75 +// Checks magic_enum compiler compatibility.
  76 +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1910
  77 +# undef MAGIC_ENUM_SUPPORTED
  78 +# define MAGIC_ENUM_SUPPORTED 1
  79 +#endif
  80 +
  81 +// Checks magic_enum compiler aliases compatibility.
  82 +#if defined(__clang__) && __clang_major__ >= 5 || defined(__GNUC__) && __GNUC__ >= 9 || defined(_MSC_VER) && _MSC_VER >= 1920
  83 +# undef MAGIC_ENUM_SUPPORTED_ALIASES
  84 +# define MAGIC_ENUM_SUPPORTED_ALIASES 1
  85 +#endif
  86 +
  87 +// Enum value must be greater or equals than MAGIC_ENUM_RANGE_MIN. By default MAGIC_ENUM_RANGE_MIN = -128.
  88 +// If need another min range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN.
  89 +#if !defined(MAGIC_ENUM_RANGE_MIN)
  90 +# define MAGIC_ENUM_RANGE_MIN -128
  91 +#endif
  92 +
  93 +// Enum value must be less or equals than MAGIC_ENUM_RANGE_MAX. By default MAGIC_ENUM_RANGE_MAX = 128.
  94 +// If need another max range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MAX.
  95 +#if !defined(MAGIC_ENUM_RANGE_MAX)
  96 +# define MAGIC_ENUM_RANGE_MAX 128
  97 +#endif
  98 +
  99 +namespace magic_enum {
  100 +
  101 +// If need another optional type, define the macro MAGIC_ENUM_USING_ALIAS_OPTIONAL.
  102 +#if defined(MAGIC_ENUM_USING_ALIAS_OPTIONAL)
  103 +MAGIC_ENUM_USING_ALIAS_OPTIONAL
  104 +#else
  105 +using std::optional;
  106 +#endif
  107 +
  108 +// If need another string_view type, define the macro MAGIC_ENUM_USING_ALIAS_STRING_VIEW.
  109 +#if defined(MAGIC_ENUM_USING_ALIAS_STRING_VIEW)
  110 +MAGIC_ENUM_USING_ALIAS_STRING_VIEW
  111 +#else
  112 +using std::string_view;
  113 +#endif
  114 +
  115 +// If need another string type, define the macro MAGIC_ENUM_USING_ALIAS_STRING.
  116 +#if defined(MAGIC_ENUM_USING_ALIAS_STRING)
  117 +MAGIC_ENUM_USING_ALIAS_STRING
  118 +#else
  119 +using std::string;
  120 +#endif
  121 +
  122 +namespace customize {
  123 +
  124 +// 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.
  125 +// If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX.
  126 +// If need another range for specific enum type, add specialization enum_range for necessary enum type.
  127 +template <typename E>
  128 +struct enum_range {
  129 + static_assert(std::is_enum_v<E>, "magic_enum::customize::enum_range requires enum type.");
  130 + static constexpr int min = MAGIC_ENUM_RANGE_MIN;
  131 + static constexpr int max = MAGIC_ENUM_RANGE_MAX;
  132 + static_assert(max > min, "magic_enum::customize::enum_range requires max > min.");
  133 +};
  134 +
  135 +static_assert(MAGIC_ENUM_RANGE_MAX > MAGIC_ENUM_RANGE_MIN, "MAGIC_ENUM_RANGE_MAX must be greater than MAGIC_ENUM_RANGE_MIN.");
  136 +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.");
  137 +
  138 +namespace detail {
  139 +enum class default_customize_tag {};
  140 +enum class invalid_customize_tag {};
  141 +} // namespace magic_enum::customize::detail
  142 +
  143 +using customize_t = std::variant<string_view, detail::default_customize_tag, detail::invalid_customize_tag>;
  144 +
  145 +// Default customize.
  146 +inline constexpr auto default_tag = detail::default_customize_tag{};
  147 +// Invalid customize.
  148 +inline constexpr auto invalid_tag = detail::invalid_customize_tag{};
  149 +
  150 +// If need custom names for enum, add specialization enum_name for necessary enum type.
  151 +template <typename E>
  152 +constexpr customize_t enum_name(E) noexcept {
  153 + return default_tag;
  154 +}
  155 +
  156 +// If need custom type name for enum, add specialization enum_type_name for necessary enum type.
  157 +template <typename E>
  158 +constexpr customize_t enum_type_name() noexcept {
  159 + return default_tag;
  160 +}
  161 +
  162 +} // namespace magic_enum::customize
  163 +
  164 +namespace detail {
  165 +
  166 +template <auto V, typename = std::enable_if_t<std::is_enum_v<std::decay_t<decltype(V)>>>>
  167 +using enum_constant = std::integral_constant<std::decay_t<decltype(V)>, V>;
  168 +
  169 +template <typename... T>
  170 +inline constexpr bool always_false_v = false;
  171 +
  172 +template <typename T>
  173 +struct supported
  174 +#if defined(MAGIC_ENUM_SUPPORTED) && MAGIC_ENUM_SUPPORTED || defined(MAGIC_ENUM_NO_CHECK_SUPPORT)
  175 + : std::true_type {};
  176 +#else
  177 + : std::false_type {};
  178 +#endif
  179 +
  180 +template <typename T, typename = void>
  181 +struct has_is_flags : std::false_type {};
  182 +
  183 +template <typename T>
  184 +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)>>> {};
  185 +
  186 +template <typename T, typename = void>
  187 +struct range_min : std::integral_constant<int, MAGIC_ENUM_RANGE_MIN> {};
  188 +
  189 +template <typename T>
  190 +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> {};
  191 +
  192 +template <typename T, typename = void>
  193 +struct range_max : std::integral_constant<int, MAGIC_ENUM_RANGE_MAX> {};
  194 +
  195 +template <typename T>
  196 +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> {};
  197 +
  198 +template <std::size_t N>
  199 +class static_string {
  200 + public:
  201 + constexpr explicit static_string(string_view str) noexcept : static_string{str, std::make_index_sequence<N>{}} {
  202 + assert(str.size() == N);
  203 + }
  204 +
  205 + constexpr const char* data() const noexcept { return chars_; }
  206 +
  207 + constexpr std::size_t size() const noexcept { return N; }
  208 +
  209 + constexpr operator string_view() const noexcept { return {data(), size()}; }
  210 +
  211 + private:
  212 + template <std::size_t... I>
  213 + constexpr static_string(string_view str, std::index_sequence<I...>) noexcept : chars_{str[I]..., '\0'} {}
  214 +
  215 + char chars_[N + 1];
  216 +};
  217 +
  218 +template <>
  219 +class static_string<0> {
  220 + public:
  221 + constexpr explicit static_string() = default;
  222 +
  223 + constexpr explicit static_string(string_view) noexcept {}
  224 +
  225 + constexpr const char* data() const noexcept { return nullptr; }
  226 +
  227 + constexpr std::size_t size() const noexcept { return 0; }
  228 +
  229 + constexpr operator string_view() const noexcept { return {}; }
  230 +};
  231 +
  232 +constexpr string_view pretty_name(string_view name) noexcept {
  233 + for (std::size_t i = name.size(); i > 0; --i) {
  234 + if (!((name[i - 1] >= '0' && name[i - 1] <= '9') ||
  235 + (name[i - 1] >= 'a' && name[i - 1] <= 'z') ||
  236 + (name[i - 1] >= 'A' && name[i - 1] <= 'Z') ||
  237 +#if defined(MAGIC_ENUM_ENABLE_NONASCII)
  238 + (name[i - 1] & 0x80) ||
  239 +#endif
  240 + (name[i - 1] == '_'))) {
  241 + name.remove_prefix(i);
  242 + break;
  243 + }
  244 + }
  245 +
  246 + if (name.size() > 0 && ((name.front() >= 'a' && name.front() <= 'z') ||
  247 + (name.front() >= 'A' && name.front() <= 'Z') ||
  248 +#if defined(MAGIC_ENUM_ENABLE_NONASCII)
  249 + (name.front() & 0x80) ||
  250 +#endif
  251 + (name.front() == '_'))) {
  252 + return name;
  253 + }
  254 +
  255 + return {}; // Invalid name.
  256 +}
  257 +
  258 +class case_insensitive {
  259 + static constexpr char to_lower(char c) noexcept {
  260 + return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + ('a' - 'A')) : c;
  261 + }
  262 +
  263 + public:
  264 + template <typename L, typename R>
  265 + 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> {
  266 +#if defined(MAGIC_ENUM_ENABLE_NONASCII)
  267 + static_assert(always_false_v<L, R>, "magic_enum::case_insensitive not supported Non-ASCII feature.");
  268 + return false;
  269 +#else
  270 + return to_lower(lhs) == to_lower(rhs);
  271 +#endif
  272 + }
  273 +};
  274 +
  275 +constexpr std::size_t find(string_view str, char c) noexcept {
  276 +#if defined(__clang__) && __clang_major__ < 9 && defined(__GLIBCXX__) || defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__)
  277 +// https://stackoverflow.com/questions/56484834/constexpr-stdstring-viewfind-last-of-doesnt-work-on-clang-8-with-libstdc
  278 +// https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html
  279 + constexpr bool workaround = true;
  280 +#else
  281 + constexpr bool workaround = false;
  282 +#endif
  283 +
  284 + if constexpr (workaround) {
  285 + for (std::size_t i = 0; i < str.size(); ++i) {
  286 + if (str[i] == c) {
  287 + return i;
  288 + }
  289 + }
  290 +
  291 + return string_view::npos;
  292 + } else {
  293 + return str.find_first_of(c);
  294 + }
  295 +}
  296 +
  297 +template <typename T, std::size_t N, std::size_t... I>
  298 +constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N], std::index_sequence<I...>) noexcept {
  299 + return {{a[I]...}};
  300 +}
  301 +
  302 +template <typename BinaryPredicate>
  303 +constexpr bool is_default_predicate() noexcept {
  304 + return std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<string_view::value_type>> ||
  305 + std::is_same_v<std::decay_t<BinaryPredicate>, std::equal_to<>>;
  306 +}
  307 +
  308 +template <typename BinaryPredicate>
  309 +constexpr bool is_nothrow_invocable() {
  310 + return is_default_predicate<BinaryPredicate>() ||
  311 + std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>;
  312 +}
  313 +
  314 +template <typename BinaryPredicate>
  315 +constexpr bool cmp_equal(string_view lhs, string_view rhs, [[maybe_unused]] BinaryPredicate&& p) noexcept(is_nothrow_invocable<BinaryPredicate>()) {
  316 +#if defined(_MSC_VER) && _MSC_VER < 1920 && !defined(__clang__)
  317 + // https://developercommunity.visualstudio.com/content/problem/360432/vs20178-regression-c-failed-in-test.html
  318 + // https://developercommunity.visualstudio.com/content/problem/232218/c-constexpr-string-view.html
  319 + constexpr bool workaround = true;
  320 +#else
  321 + constexpr bool workaround = false;
  322 +#endif
  323 +
  324 + if constexpr (!is_default_predicate<BinaryPredicate>() || workaround) {
  325 + if (lhs.size() != rhs.size()) {
  326 + return false;
  327 + }
  328 +
  329 + const auto size = lhs.size();
  330 + for (std::size_t i = 0; i < size; ++i) {
  331 + if (!p(lhs[i], rhs[i])) {
  332 + return false;
  333 + }
  334 + }
  335 +
  336 + return true;
  337 + } else {
  338 + return lhs == rhs;
  339 + }
  340 +}
  341 +
  342 +template <typename L, typename R>
  343 +constexpr bool cmp_less(L lhs, R rhs) noexcept {
  344 + static_assert(std::is_integral_v<L> && std::is_integral_v<R>, "magic_enum::detail::cmp_less requires integral type.");
  345 +
  346 + if constexpr (std::is_signed_v<L> == std::is_signed_v<R>) {
  347 + // If same signedness (both signed or both unsigned).
  348 + return lhs < rhs;
  349 + } else if constexpr (std::is_same_v<L, bool>) { // bool special case
  350 + return static_cast<R>(lhs) < rhs;
  351 + } else if constexpr (std::is_same_v<R, bool>) { // bool special case
  352 + return lhs < static_cast<L>(rhs);
  353 + } else if constexpr (std::is_signed_v<R>) {
  354 + // If 'right' is negative, then result is 'false', otherwise cast & compare.
  355 + return rhs > 0 && lhs < static_cast<std::make_unsigned_t<R>>(rhs);
  356 + } else {
  357 + // If 'left' is negative, then result is 'true', otherwise cast & compare.
  358 + return lhs < 0 || static_cast<std::make_unsigned_t<L>>(lhs) < rhs;
  359 + }
  360 +}
  361 +
  362 +template <typename I>
  363 +constexpr I log2(I value) noexcept {
  364 + static_assert(std::is_integral_v<I>, "magic_enum::detail::log2 requires integral type.");
  365 +
  366 + if constexpr (std::is_same_v<I, bool>) { // bool special case
  367 + return assert(false), value;
  368 + } else {
  369 + auto ret = I{0};
  370 + for (; value > I{1}; value >>= I{1}, ++ret) {}
  371 +
  372 + return ret;
  373 + }
  374 +}
  375 +
  376 +template <typename T>
  377 +inline constexpr bool is_enum_v = std::is_enum_v<T> && std::is_same_v<T, std::decay_t<T>>;
  378 +
  379 +template <typename E>
  380 +constexpr auto n() noexcept {
  381 + static_assert(is_enum_v<E>, "magic_enum::detail::n requires enum type.");
  382 +
  383 + [[maybe_unused]] constexpr auto custom = customize::enum_type_name<E>();
  384 + static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type.");
  385 + if constexpr (custom.index() == 0) {
  386 + constexpr auto name = std::get<string_view>(custom);
  387 + static_assert(!name.empty(), "magic_enum::customize requires not empty string.");
  388 + return static_string<name.size()>{name};
  389 + } else if constexpr (custom.index() == 1 && supported<E>::value) {
  390 +#if defined(__clang__) || defined(__GNUC__)
  391 + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2});
  392 +#elif defined(_MSC_VER)
  393 + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17});
  394 +#else
  395 + constexpr auto name = string_view{};
  396 +#endif
  397 + return static_string<name.size()>{name};
  398 + } else {
  399 + return static_string<0>{}; // Unsupported compiler or Invalid customize.
  400 + }
  401 +}
  402 +
  403 +template <typename E>
  404 +inline constexpr auto type_name_v = n<E>();
  405 +
  406 +template <typename E, E V>
  407 +constexpr auto n() noexcept {
  408 + static_assert(is_enum_v<E>, "magic_enum::detail::n requires enum type.");
  409 +
  410 + [[maybe_unused]] constexpr auto custom = customize::enum_name<E>(V);
  411 + static_assert(std::is_same_v<std::decay_t<decltype(custom)>, customize::customize_t>, "magic_enum::customize requires customize_t type.");
  412 + if constexpr (custom.index() == 0) {
  413 + constexpr auto name = std::get<string_view>(custom);
  414 + static_assert(!name.empty(), "magic_enum::customize requires not empty string.");
  415 + return static_string<name.size()>{name};
  416 + } else if constexpr (custom.index() == 1 && supported<E>::value) {
  417 +#if defined(__clang__) || defined(__GNUC__)
  418 + constexpr auto name = pretty_name({__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2});
  419 +#elif defined(_MSC_VER)
  420 + constexpr auto name = pretty_name({__FUNCSIG__, sizeof(__FUNCSIG__) - 17});
  421 +#else
  422 + constexpr auto name = string_view{};
  423 +#endif
  424 + return static_string<name.size()>{name};
  425 + } else {
  426 + return static_string<0>{}; // Unsupported compiler or Invalid customize.
  427 + }
  428 +}
  429 +
  430 +template <typename E, E V>
  431 +inline constexpr auto enum_name_v = n<E, V>();
  432 +
  433 +template <typename E, auto V>
  434 +constexpr bool is_valid() noexcept {
  435 + static_assert(is_enum_v<E>, "magic_enum::detail::is_valid requires enum type.");
  436 +
  437 + return n<E, static_cast<E>(V)>().size() != 0;
  438 +}
  439 +
  440 +template <typename E, int O, bool IsFlags, typename U = std::underlying_type_t<E>>
  441 +constexpr E value(std::size_t i) noexcept {
  442 + static_assert(is_enum_v<E>, "magic_enum::detail::value requires enum type.");
  443 +
  444 + if constexpr (std::is_same_v<U, bool>) { // bool special case
  445 + static_assert(O == 0, "magic_enum::detail::value requires valid offset.");
  446 +
  447 + return static_cast<E>(i);
  448 + } else if constexpr (IsFlags) {
  449 + return static_cast<E>(U{1} << static_cast<U>(static_cast<int>(i) + O));
  450 + } else {
  451 + return static_cast<E>(static_cast<int>(i) + O);
  452 + }
  453 +}
  454 +
  455 +template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
  456 +constexpr int reflected_min() noexcept {
  457 + static_assert(is_enum_v<E>, "magic_enum::detail::reflected_min requires enum type.");
  458 +
  459 + if constexpr (IsFlags) {
  460 + return 0;
  461 + } else {
  462 + constexpr auto lhs = range_min<E>::value;
  463 + constexpr auto rhs = (std::numeric_limits<U>::min)();
  464 +
  465 + if constexpr (cmp_less(rhs, lhs)) {
  466 + return lhs;
  467 + } else {
  468 + return rhs;
  469 + }
  470 + }
  471 +}
  472 +
  473 +template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
  474 +constexpr int reflected_max() noexcept {
  475 + static_assert(is_enum_v<E>, "magic_enum::detail::reflected_max requires enum type.");
  476 +
  477 + if constexpr (IsFlags) {
  478 + return std::numeric_limits<U>::digits - 1;
  479 + } else {
  480 + constexpr auto lhs = range_max<E>::value;
  481 + constexpr auto rhs = (std::numeric_limits<U>::max)();
  482 +
  483 + if constexpr (cmp_less(lhs, rhs)) {
  484 + return lhs;
  485 + } else {
  486 + return rhs;
  487 + }
  488 + }
  489 +}
  490 +
  491 +template <typename E, bool IsFlags>
  492 +inline constexpr auto reflected_min_v = reflected_min<E, IsFlags>();
  493 +
  494 +template <typename E, bool IsFlags>
  495 +inline constexpr auto reflected_max_v = reflected_max<E, IsFlags>();
  496 +
  497 +template <std::size_t N>
  498 +constexpr std::size_t values_count(const bool (&valid)[N]) noexcept {
  499 + auto count = std::size_t{0};
  500 + for (std::size_t i = 0; i < N; ++i) {
  501 + if (valid[i]) {
  502 + ++count;
  503 + }
  504 + }
  505 +
  506 + return count;
  507 +}
  508 +
  509 +template <typename E, bool IsFlags, int Min, std::size_t... I>
  510 +constexpr auto values(std::index_sequence<I...>) noexcept {
  511 + static_assert(is_enum_v<E>, "magic_enum::detail::values requires enum type.");
  512 + constexpr bool valid[sizeof...(I)] = {is_valid<E, value<E, Min, IsFlags>(I)>()...};
  513 + constexpr std::size_t count = values_count(valid);
  514 +
  515 + if constexpr (count > 0) {
  516 + E values[count] = {};
  517 + for (std::size_t i = 0, v = 0; v < count; ++i) {
  518 + if (valid[i]) {
  519 + values[v++] = value<E, Min, IsFlags>(i);
  520 + }
  521 + }
  522 +
  523 + return to_array(values, std::make_index_sequence<count>{});
  524 + } else {
  525 + return std::array<E, 0>{};
  526 + }
  527 +}
  528 +
  529 +template <typename E, bool IsFlags, typename U = std::underlying_type_t<E>>
  530 +constexpr auto values() noexcept {
  531 + static_assert(is_enum_v<E>, "magic_enum::detail::values requires enum type.");
  532 + constexpr auto min = reflected_min_v<E, IsFlags>;
  533 + constexpr auto max = reflected_max_v<E, IsFlags>;
  534 + constexpr auto range_size = max - min + 1;
  535 + static_assert(range_size > 0, "magic_enum::enum_range requires valid size.");
  536 + static_assert(range_size < (std::numeric_limits<std::uint16_t>::max)(), "magic_enum::enum_range requires valid size.");
  537 +
  538 + return values<E, IsFlags, reflected_min_v<E, IsFlags>>(std::make_index_sequence<range_size>{});
  539 +}
  540 +
  541 +template <typename E, typename U = std::underlying_type_t<E>>
  542 +constexpr bool is_flags_enum() noexcept {
  543 + static_assert(is_enum_v<E>, "magic_enum::detail::is_flags_enum requires enum type.");
  544 +
  545 + if constexpr (has_is_flags<E>::value) {
  546 + return customize::enum_range<E>::is_flags;
  547 + } else if constexpr (std::is_same_v<U, bool>) { // bool special case
  548 + return false;
  549 + } else {
  550 +#if defined(MAGIC_ENUM_NO_CHECK_FLAGS)
  551 + return false;
  552 +#else
  553 + constexpr auto flags_values = values<E, true>();
  554 + constexpr auto default_values = values<E, false>();
  555 + if (flags_values.size() == 0 || default_values.size() > flags_values.size()) {
  556 + return false;
  557 + }
  558 + for (std::size_t i = 0; i < default_values.size(); ++i) {
  559 + const auto v = static_cast<U>(default_values[i]);
  560 + if (v != 0 && (v & (v - 1)) != 0) {
  561 + return false;
  562 + }
  563 + }
  564 + return flags_values.size() > 0;
  565 +#endif
  566 + }
  567 +}
  568 +
  569 +template <typename E>
  570 +inline constexpr bool is_flags_v = is_flags_enum<E>();
  571 +
  572 +template <typename E>
  573 +inline constexpr std::array values_v = values<E, is_flags_v<E>>();
  574 +
  575 +template <typename E, typename D = std::decay_t<E>>
  576 +using values_t = decltype((values_v<D>));
  577 +
  578 +template <typename E>
  579 +inline constexpr auto count_v = values_v<E>.size();
  580 +
  581 +template <typename E, typename U = std::underlying_type_t<E>>
  582 +inline constexpr auto min_v = (count_v<E> > 0) ? static_cast<U>(values_v<E>.front()) : U{0};
  583 +
  584 +template <typename E, typename U = std::underlying_type_t<E>>
  585 +inline constexpr auto max_v = (count_v<E> > 0) ? static_cast<U>(values_v<E>.back()) : U{0};
  586 +
  587 +template <typename E, std::size_t... I>
  588 +constexpr auto names(std::index_sequence<I...>) noexcept {
  589 + static_assert(is_enum_v<E>, "magic_enum::detail::names requires enum type.");
  590 +
  591 + return std::array<string_view, sizeof...(I)>{{enum_name_v<E, values_v<E>[I]>...}};
  592 +}
  593 +
  594 +template <typename E>
  595 +inline constexpr std::array names_v = names<E>(std::make_index_sequence<count_v<E>>{});
  596 +
  597 +template <typename E, typename D = std::decay_t<E>>
  598 +using names_t = decltype((names_v<D>));
  599 +
  600 +template <typename E, std::size_t... I>
  601 +constexpr auto entries(std::index_sequence<I...>) noexcept {
  602 + static_assert(is_enum_v<E>, "magic_enum::detail::entries requires enum type.");
  603 +
  604 + return std::array<std::pair<E, string_view>, sizeof...(I)>{{{values_v<E>[I], enum_name_v<E, values_v<E>[I]>}...}};
  605 +}
  606 +
  607 +template <typename E>
  608 +inline constexpr std::array entries_v = entries<E>(std::make_index_sequence<count_v<E>>{});
  609 +
  610 +template <typename E, typename D = std::decay_t<E>>
  611 +using entries_t = decltype((entries_v<D>));
  612 +
  613 +template <typename E, typename U = std::underlying_type_t<E>>
  614 +constexpr bool is_sparse() noexcept {
  615 + static_assert(is_enum_v<E>, "magic_enum::detail::is_sparse requires enum type.");
  616 +
  617 + if constexpr (count_v<E> == 0) {
  618 + return false;
  619 + } else if constexpr (std::is_same_v<U, bool>) { // bool special case
  620 + return false;
  621 + } else {
  622 + constexpr auto max = is_flags_v<E> ? log2(max_v<E>) : max_v<E>;
  623 + constexpr auto min = is_flags_v<E> ? log2(min_v<E>) : min_v<E>;
  624 + constexpr auto range_size = max - min + 1;
  625 +
  626 + return range_size != count_v<E>;
  627 + }
  628 +}
  629 +
  630 +template <typename E>
  631 +inline constexpr bool is_sparse_v = is_sparse<E>();
  632 +
  633 +template <typename E, typename U = std::underlying_type_t<E>>
  634 +constexpr U values_ors() noexcept {
  635 + static_assert(is_enum_v<E>, "magic_enum::detail::values_ors requires enum type.");
  636 +
  637 + auto ors = U{0};
  638 + for (std::size_t i = 0; i < count_v<E>; ++i) {
  639 + ors |= static_cast<U>(values_v<E>[i]);
  640 + }
  641 +
  642 + return ors;
  643 +}
  644 +
  645 +template <bool, typename R>
  646 +struct enable_if_enum {};
  647 +
  648 +template <typename R>
  649 +struct enable_if_enum<true, R> {
  650 + using type = R;
  651 + static_assert(supported<R>::value, "magic_enum unsupported compiler (https://github.com/Neargye/magic_enum#compiler-compatibility).");
  652 +};
  653 +
  654 +template <typename T, typename R, typename BinaryPredicate = std::equal_to<>>
  655 +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;
  656 +
  657 +template <typename T, typename Enable = std::enable_if_t<std::is_enum_v<std::decay_t<T>>>>
  658 +using enum_concept = T;
  659 +
  660 +template <typename T, bool = std::is_enum_v<T>>
  661 +struct is_scoped_enum : std::false_type {};
  662 +
  663 +template <typename T>
  664 +struct is_scoped_enum<T, true> : std::bool_constant<!std::is_convertible_v<T, std::underlying_type_t<T>>> {};
  665 +
  666 +template <typename T, bool = std::is_enum_v<T>>
  667 +struct is_unscoped_enum : std::false_type {};
  668 +
  669 +template <typename T>
  670 +struct is_unscoped_enum<T, true> : std::bool_constant<std::is_convertible_v<T, std::underlying_type_t<T>>> {};
  671 +
  672 +template <typename T, bool = std::is_enum_v<std::decay_t<T>>>
  673 +struct underlying_type {};
  674 +
  675 +template <typename T>
  676 +struct underlying_type<T, true> : std::underlying_type<std::decay_t<T>> {};
  677 +
  678 +template <typename Value, typename = void>
  679 +struct constexpr_hash_t;
  680 +
  681 +template <typename Value>
  682 +struct constexpr_hash_t<Value, std::enable_if_t<is_enum_v<Value>>> {
  683 + constexpr auto operator()(Value value) const noexcept {
  684 + using U = typename underlying_type<Value>::type;
  685 + if constexpr (std::is_same_v<U, bool>) { // bool special case
  686 + return static_cast<std::size_t>(value);
  687 + } else {
  688 + return static_cast<U>(value);
  689 + }
  690 + }
  691 + using secondary_hash = constexpr_hash_t;
  692 +};
  693 +
  694 +template <typename Value>
  695 +struct constexpr_hash_t<Value, std::enable_if_t<std::is_same_v<Value, string_view>>> {
  696 + static constexpr std::uint32_t crc_table[256] {
  697 + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,
  698 + 0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,
  699 + 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,
  700 + 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L,
  701 + 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
  702 + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L,
  703 + 0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,
  704 + 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,
  705 + 0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,
  706 + 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
  707 + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L,
  708 + 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,
  709 + 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,
  710 + 0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,
  711 + 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
  712 + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,
  713 + 0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,
  714 + 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,
  715 + 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,
  716 + 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
  717 + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,
  718 + 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L,
  719 + 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,
  720 + 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,
  721 + 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
  722 + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,
  723 + 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,
  724 + 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L,
  725 + 0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,
  726 + 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
  727 + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,
  728 + 0xb3667a2eL, 0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 0x2d02ef8dL
  729 + };
  730 + constexpr std::uint32_t operator()(string_view value) const noexcept {
  731 + auto crc = static_cast<std::uint32_t>(0xffffffffL);
  732 + for (const auto c : value) {
  733 + crc = (crc >> 8) ^ crc_table[(crc ^ static_cast<std::uint32_t>(c)) & 0xff];
  734 + }
  735 + return crc ^ 0xffffffffL;
  736 + }
  737 +
  738 + struct secondary_hash {
  739 + constexpr std::uint32_t operator()(string_view value) const noexcept {
  740 + auto acc = static_cast<std::uint64_t>(2166136261ULL);
  741 + for (const auto c : value) {
  742 + acc = ((acc ^ static_cast<std::uint64_t>(c)) * static_cast<std::uint64_t>(16777619ULL)) & std::numeric_limits<std::uint32_t>::max();
  743 + }
  744 + return static_cast<std::uint32_t>(acc);
  745 + }
  746 + };
  747 +};
  748 +
  749 +template <typename Hash>
  750 +constexpr static Hash hash_v{};
  751 +
  752 +template <auto* GlobValues, typename Hash>
  753 +constexpr auto calculate_cases(std::size_t Page) noexcept {
  754 + constexpr std::array values = *GlobValues;
  755 + constexpr std::size_t size = values.size();
  756 +
  757 + using switch_t = std::invoke_result_t<Hash, typename decltype(values)::value_type>;
  758 + static_assert(std::is_integral_v<switch_t> && !std::is_same_v<switch_t, bool>);
  759 + const std::size_t values_to = (std::min)(static_cast<std::size_t>(256), size - Page);
  760 +
  761 + std::array<switch_t, 256> result{};
  762 + auto fill = result.begin();
  763 + for (auto first = values.begin() + Page, last = values.begin() + Page + values_to; first != last; ) {
  764 + *fill++ = hash_v<Hash>(*first++);
  765 + }
  766 +
  767 + // dead cases, try to avoid case collisions
  768 + for (switch_t last_value = result[values_to - 1]; fill != result.end() && last_value != (std::numeric_limits<switch_t>::max)(); *fill++ = ++last_value) {
  769 + }
  770 +
  771 + auto it = result.begin();
  772 + for (auto last_value = (std::numeric_limits<switch_t>::min)(); fill != result.end(); *fill++ = last_value++) {
  773 + while (last_value == *it) {
  774 + ++last_value, ++it;
  775 + }
  776 + }
  777 +
  778 + return result;
  779 +}
  780 +
  781 +template <typename R, typename F, typename... Args>
  782 +constexpr R invoke_r(F&& f, Args&&... args) noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>) {
  783 + if constexpr (std::is_void_v<R>) {
  784 + std::forward<F>(f)(std::forward<Args>(args)...);
  785 + } else {
  786 + return static_cast<R>(std::forward<F>(f)(std::forward<Args>(args)...));
  787 + }
  788 +}
  789 +
  790 +enum class case_call_t {
  791 + index, value
  792 +};
  793 +
  794 +template <typename T = void>
  795 +inline constexpr auto default_result_type_lambda = []() noexcept(std::is_nothrow_default_constructible_v<T>) { return T{}; };
  796 +
  797 +template <>
  798 +inline constexpr auto default_result_type_lambda<void> = []() noexcept {};
  799 +
  800 +template <auto* Arr, typename Hash>
  801 +constexpr bool no_duplicate() noexcept {
  802 + using value_t = std::decay_t<decltype((*Arr)[0])>;
  803 + using hash_value_t = std::invoke_result_t<Hash, value_t>;
  804 + std::array<hash_value_t, Arr->size()> hashes{};
  805 + std::size_t size = 0;
  806 + for (auto elem : *Arr) {
  807 + hashes[size] = hash_v<Hash>(elem);
  808 + for (auto i = size++; i > 0; --i) {
  809 + if (hashes[i] < hashes[i - 1]) {
  810 + auto tmp = hashes[i];
  811 + hashes[i] = hashes[i - 1];
  812 + hashes[i - 1] = tmp;
  813 + } else if (hashes[i] == hashes[i - 1]) {
  814 + return false;
  815 + } else {
  816 + break;
  817 + }
  818 + }
  819 + }
  820 + return true;
  821 +}
  822 +
  823 +#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) \
  824 + 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) \
  825 + 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) \
  826 + 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) \
  827 + 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) \
  828 + 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) \
  829 + 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) \
  830 + 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)
  831 +
  832 +#define MAGIC_ENUM_CASE(val) \
  833 + case cases[val]: \
  834 + if constexpr ((val) + Page < size) { \
  835 + if (!pred(values[val + Page], searched)) { \
  836 + break; \
  837 + } \
  838 + if constexpr (CallValue == case_call_t::index) { \
  839 + if constexpr (std::is_invocable_r_v<result_t, Lambda, std::integral_constant<std::size_t, val + Page>>) { \
  840 + return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), std::integral_constant<std::size_t, val + Page>{}); \
  841 + } else if constexpr (std::is_invocable_v<Lambda, std::integral_constant<std::size_t, val + Page>>) { \
  842 + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \
  843 + } \
  844 + } else if constexpr (CallValue == case_call_t::value) { \
  845 + if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \
  846 + return detail::invoke_r<result_t>(std::forward<Lambda>(lambda), enum_constant<values[val + Page]>{}); \
  847 + } else if constexpr (std::is_invocable_r_v<result_t, Lambda, enum_constant<values[val + Page]>>) { \
  848 + assert(false && "magic_enum::detail::constexpr_switch wrong result type."); \
  849 + } \
  850 + } \
  851 + break; \
  852 + } else [[fallthrough]];
  853 +
  854 +template <auto* GlobValues,
  855 + case_call_t CallValue,
  856 + std::size_t Page = 0,
  857 + typename Hash = constexpr_hash_t<typename std::decay_t<decltype(*GlobValues)>::value_type>,
  858 + typename Lambda, typename ResultGetterType = decltype(default_result_type_lambda<>),
  859 + typename BinaryPredicate = std::equal_to<>>
  860 +constexpr std::invoke_result_t<ResultGetterType> constexpr_switch(
  861 + Lambda&& lambda,
  862 + typename std::decay_t<decltype(*GlobValues)>::value_type searched,
  863 + ResultGetterType&& def = default_result_type_lambda<>,
  864 + BinaryPredicate&& pred = {}) {
  865 + using result_t = std::invoke_result_t<ResultGetterType>;
  866 + using hash_t = std::conditional_t<no_duplicate<GlobValues, Hash>(), Hash, typename Hash::secondary_hash>;
  867 + constexpr std::array values = *GlobValues;
  868 + constexpr std::size_t size = values.size();
  869 + constexpr std::array cases = calculate_cases<GlobValues, hash_t>(Page);
  870 +
  871 + switch (hash_v<hash_t>(searched)) {
  872 + MAGIC_ENUM_FOR_EACH_256(MAGIC_ENUM_CASE)
  873 + default:
  874 + if constexpr (size > 256 + Page) {
  875 + return constexpr_switch<GlobValues, CallValue, Page + 256, Hash>(std::forward<Lambda>(lambda), searched, std::forward<ResultGetterType>(def));
  876 + }
  877 + break;
  878 + }
  879 + return def();
  880 +}
  881 +
  882 +#undef MAGIC_ENUM_FOR_EACH_256
  883 +#undef MAGIC_ENUM_CASE
  884 +
  885 +template <typename E, typename Lambda, std::size_t... I>
  886 +constexpr auto for_each(Lambda&& lambda, std::index_sequence<I...>) {
  887 + static_assert(is_enum_v<E>, "magic_enum::detail::for_each requires enum type.");
  888 + constexpr bool has_void_return = (std::is_void_v<std::invoke_result_t<Lambda, enum_constant<values_v<E>[I]>>> || ...);
  889 + 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]>>> && ...);
  890 +
  891 + if constexpr (has_void_return) {
  892 + (lambda(enum_constant<values_v<E>[I]>{}), ...);
  893 + } else if constexpr (all_same_return) {
  894 + return std::array{lambda(enum_constant<values_v<E>[I]>{})...};
  895 + } else {
  896 + return std::tuple{lambda(enum_constant<values_v<E>[I]>{})...};
  897 + }
  898 +}
  899 +
  900 +template <typename E, typename Lambda, typename D = std::decay_t<E>>
  901 +using for_each_t = decltype(for_each<D>(std::declval<Lambda>(), std::make_index_sequence<count_v<D>>{}));
  902 +
  903 +} // namespace magic_enum::detail
  904 +
  905 +// Checks is magic_enum supported compiler.
  906 +inline constexpr bool is_magic_enum_supported = detail::supported<void>::value;
  907 +
  908 +template <typename T>
  909 +using Enum = detail::enum_concept<T>;
  910 +
  911 +// Checks whether T is an Unscoped enumeration type.
  912 +// 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.
  913 +template <typename T>
  914 +struct is_unscoped_enum : detail::is_unscoped_enum<T> {};
  915 +
  916 +template <typename T>
  917 +inline constexpr bool is_unscoped_enum_v = is_unscoped_enum<T>::value;
  918 +
  919 +// Checks whether T is an Scoped enumeration type.
  920 +// 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.
  921 +template <typename T>
  922 +struct is_scoped_enum : detail::is_scoped_enum<T> {};
  923 +
  924 +template <typename T>
  925 +inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value;
  926 +
  927 +// If T is a complete enumeration type, provides a member typedef type that names the underlying type of T.
  928 +// 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.
  929 +template <typename T>
  930 +struct underlying_type : detail::underlying_type<T> {};
  931 +
  932 +template <typename T>
  933 +using underlying_type_t = typename underlying_type<T>::type;
  934 +
  935 +template <auto V>
  936 +using enum_constant = detail::enum_constant<V>;
  937 +
  938 +// Returns type name of enum.
  939 +template <typename E>
  940 +[[nodiscard]] constexpr auto enum_type_name() noexcept -> detail::enable_if_t<E, string_view> {
  941 + constexpr string_view name = detail::type_name_v<std::decay_t<E>>;
  942 + static_assert(!name.empty(), "magic_enum::enum_type_name enum type does not have a name.");
  943 +
  944 + return name;
  945 +}
  946 +
  947 +// Returns number of enum values.
  948 +template <typename E>
  949 +[[nodiscard]] constexpr auto enum_count() noexcept -> detail::enable_if_t<E, std::size_t> {
  950 + return detail::count_v<std::decay_t<E>>;
  951 +}
  952 +
  953 +// Returns enum value at specified index.
  954 +// No bounds checking is performed: the behavior is undefined if index >= number of enum values.
  955 +template <typename E>
  956 +[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
  957 + using D = std::decay_t<E>;
  958 +
  959 + if constexpr (detail::is_sparse_v<D>) {
  960 + return assert((index < detail::count_v<D>)), detail::values_v<D>[index];
  961 + } else {
  962 + constexpr bool is_flag = detail::is_flags_v<D>;
  963 + constexpr auto min = is_flag ? detail::log2(detail::min_v<D>) : detail::min_v<D>;
  964 +
  965 + return assert((index < detail::count_v<D>)), detail::value<D, min, is_flag>(index);
  966 + }
  967 +}
  968 +
  969 +// Returns enum value at specified index.
  970 +template <typename E, std::size_t I>
  971 +[[nodiscard]] constexpr auto enum_value() noexcept -> detail::enable_if_t<E, std::decay_t<E>> {
  972 + using D = std::decay_t<E>;
  973 + static_assert(I < detail::count_v<D>, "magic_enum::enum_value out of range.");
  974 +
  975 + return enum_value<D>(I);
  976 +}
  977 +
  978 +// Returns std::array with enum values, sorted by enum value.
  979 +template <typename E>
  980 +[[nodiscard]] constexpr auto enum_values() noexcept -> detail::enable_if_t<E, detail::values_t<E>> {
  981 + return detail::values_v<std::decay_t<E>>;
  982 +}
  983 +
  984 +// Returns integer value from enum value.
  985 +template <typename E>
  986 +[[nodiscard]] constexpr auto enum_integer(E value) noexcept -> detail::enable_if_t<E, underlying_type_t<E>> {
  987 + return static_cast<underlying_type_t<E>>(value);
  988 +}
  989 +
  990 +
  991 +// Returns underlying value from enum value.
  992 +template <typename E>
  993 +[[nodiscard]] constexpr auto enum_underlying(E value) noexcept -> detail::enable_if_t<E, underlying_type_t<E>> {
  994 + return static_cast<underlying_type_t<E>>(value);
  995 +}
  996 +
  997 +// Obtains index in enum values from enum value.
  998 +// Returns optional with index.
  999 +template <typename E>
  1000 +[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t<E, optional<std::size_t>> {
  1001 + using D = std::decay_t<E>;
  1002 + using U = underlying_type_t<D>;
  1003 +
  1004 + if constexpr (detail::count_v<D> == 0) {
  1005 + return {}; // Empty enum.
  1006 + } else if constexpr (detail::is_sparse_v<D> || detail::is_flags_v<D>) {
  1007 + return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::index>(
  1008 + [](std::size_t i) { return optional<std::size_t>{i}; },
  1009 + value,
  1010 + detail::default_result_type_lambda<optional<std::size_t>>);
  1011 + } else {
  1012 + const auto v = static_cast<U>(value);
  1013 + if (v >= detail::min_v<D> && v <= detail::max_v<D>) {
  1014 + return static_cast<std::size_t>(v - detail::min_v<D>);
  1015 + }
  1016 + return {}; // Invalid value or out of range.
  1017 + }
  1018 +}
  1019 +
  1020 +// Returns name from static storage enum variable.
  1021 +// This version is much lighter on the compile times and is not restricted to the enum_range limitation.
  1022 +template <auto V>
  1023 +[[nodiscard]] constexpr auto enum_name() noexcept -> detail::enable_if_t<decltype(V), string_view> {
  1024 + constexpr string_view name = detail::enum_name_v<std::decay_t<decltype(V)>, V>;
  1025 + static_assert(!name.empty(), "magic_enum::enum_name enum value does not have a name.");
  1026 +
  1027 + return name;
  1028 +}
  1029 +
  1030 +// Returns name from enum value.
  1031 +// If enum value does not have name or value out of range, returns empty string.
  1032 +template <typename E>
  1033 +[[nodiscard]] constexpr auto enum_name(E value) noexcept -> detail::enable_if_t<E, string_view> {
  1034 + using D = std::decay_t<E>;
  1035 +
  1036 + if (const auto i = enum_index<D>(value)) {
  1037 + return detail::names_v<D>[*i];
  1038 + }
  1039 + return {};
  1040 +}
  1041 +
  1042 +// Returns name from enum-flags value.
  1043 +// If enum-flags value does not have name or value out of range, returns empty string.
  1044 +template <typename E>
  1045 +[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t<E, string> {
  1046 + using D = std::decay_t<E>;
  1047 + using U = underlying_type_t<D>;
  1048 +
  1049 + if constexpr (detail::is_flags_v<D>) {
  1050 + string name;
  1051 + auto check_value = U{0};
  1052 + for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
  1053 + if (const auto v = static_cast<U>(enum_value<D>(i)); (static_cast<U>(value) & v) != 0) {
  1054 + check_value |= v;
  1055 + const auto n = detail::names_v<D>[i];
  1056 + if (!name.empty()) {
  1057 + name.append(1, '|');
  1058 + }
  1059 + name.append(n.data(), n.size());
  1060 + }
  1061 + }
  1062 +
  1063 + if (check_value != 0 && check_value == static_cast<U>(value)) {
  1064 + return name;
  1065 + }
  1066 +
  1067 + return {}; // Invalid value or out of range.
  1068 + } else {
  1069 + return string{enum_name(value)};
  1070 + }
  1071 +}
  1072 +
  1073 +// Returns std::array with names, sorted by enum value.
  1074 +template <typename E>
  1075 +[[nodiscard]] constexpr auto enum_names() noexcept -> detail::enable_if_t<E, detail::names_t<E>> {
  1076 + return detail::names_v<std::decay_t<E>>;
  1077 +}
  1078 +
  1079 +// Returns std::array with pairs (value, name), sorted by enum value.
  1080 +template <typename E>
  1081 +[[nodiscard]] constexpr auto enum_entries() noexcept -> detail::enable_if_t<E, detail::entries_t<E>> {
  1082 + return detail::entries_v<std::decay_t<E>>;
  1083 +}
  1084 +
  1085 +// Obtains enum value from integer value.
  1086 +// Returns optional with enum value.
  1087 +template <typename E>
  1088 +[[nodiscard]] constexpr auto enum_cast(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, optional<std::decay_t<E>>> {
  1089 + using D = std::decay_t<E>;
  1090 + using U = underlying_type_t<D>;
  1091 +
  1092 + if constexpr (detail::count_v<D> == 0) {
  1093 + return {}; // Empty enum.
  1094 + } else if constexpr (detail::is_sparse_v<D>) {
  1095 + if constexpr (detail::is_flags_v<D>) {
  1096 + constexpr auto count = detail::count_v<D>;
  1097 + auto check_value = U{0};
  1098 + for (std::size_t i = 0; i < count; ++i) {
  1099 + if (const auto v = static_cast<U>(enum_value<D>(i)); (value & v) != 0) {
  1100 + check_value |= v;
  1101 + }
  1102 + }
  1103 +
  1104 + if (check_value != 0 && check_value == value) {
  1105 + return static_cast<D>(value);
  1106 + }
  1107 + return {}; // Invalid value or out of range.
  1108 + } else {
  1109 + return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
  1110 + [](D v) { return optional<D>{v}; },
  1111 + static_cast<D>(value),
  1112 + detail::default_result_type_lambda<optional<D>>);
  1113 + }
  1114 + } else {
  1115 + constexpr auto min = detail::min_v<D>;
  1116 + constexpr auto max = detail::is_flags_v<D> ? detail::values_ors<D>() : detail::max_v<D>;
  1117 +
  1118 + if (value >= min && value <= max) {
  1119 + return static_cast<D>(value);
  1120 + }
  1121 + return {}; // Invalid value or out of range.
  1122 + }
  1123 +}
  1124 +
  1125 +// Allows you to write magic_enum::enum_cast<foo>("bar", magic_enum::case_insensitive);
  1126 +inline constexpr auto case_insensitive = detail::case_insensitive{};
  1127 +
  1128 +// Obtains enum value from name.
  1129 +// Returns optional with enum value.
  1130 +template <typename E, typename BinaryPredicate = std::equal_to<>>
  1131 +[[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> {
  1132 + static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_cast requires bool(char, char) invocable predicate.");
  1133 + using D = std::decay_t<E>;
  1134 + using U = underlying_type_t<D>;
  1135 +
  1136 + if constexpr (detail::count_v<D> == 0) {
  1137 + return {}; // Empty enum.
  1138 + } else if constexpr (detail::is_flags_v<D>) {
  1139 + auto result = U{0};
  1140 + while (!value.empty()) {
  1141 + const auto d = detail::find(value, '|');
  1142 + const auto s = (d == string_view::npos) ? value : value.substr(0, d);
  1143 + auto f = U{0};
  1144 + for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
  1145 + if (detail::cmp_equal(s, detail::names_v<D>[i], p)) {
  1146 + f = static_cast<U>(enum_value<D>(i));
  1147 + result |= f;
  1148 + break;
  1149 + }
  1150 + }
  1151 + if (f == U{0}) {
  1152 + return {}; // Invalid value or out of range.
  1153 + }
  1154 + value.remove_prefix((d == string_view::npos) ? value.size() : d + 1);
  1155 + }
  1156 +
  1157 + if (result != U{0}) {
  1158 + return static_cast<D>(result);
  1159 + }
  1160 + return {}; // Invalid value or out of range.
  1161 + } else if constexpr (detail::count_v<D> > 0) {
  1162 + if constexpr (detail::is_default_predicate<BinaryPredicate>()) {
  1163 + return detail::constexpr_switch<&detail::names_v<D>, detail::case_call_t::index>(
  1164 + [](std::size_t i) { return optional<D>{detail::values_v<D>[i]}; },
  1165 + value,
  1166 + detail::default_result_type_lambda<optional<D>>,
  1167 + [&p](string_view lhs, string_view rhs) { return detail::cmp_equal(lhs, rhs, p); });
  1168 + } else {
  1169 + for (std::size_t i = 0; i < detail::count_v<D>; ++i) {
  1170 + if (detail::cmp_equal(value, detail::names_v<D>[i], p)) {
  1171 + return enum_value<D>(i);
  1172 + }
  1173 + }
  1174 + return {}; // Invalid value or out of range.
  1175 + }
  1176 + }
  1177 +}
  1178 +
  1179 +// Obtains index in enum values from static storage enum variable.
  1180 +template <auto V>
  1181 +[[nodiscard]] constexpr auto enum_index() noexcept -> detail::enable_if_t<decltype(V), std::size_t> {
  1182 + constexpr auto index = enum_index<std::decay_t<decltype(V)>>(V);
  1183 + static_assert(index, "magic_enum::enum_index enum value does not have a index.");
  1184 +
  1185 + return *index;
  1186 +}
  1187 +
  1188 +// Checks whether enum contains enumerator with such enum value.
  1189 +template <typename E>
  1190 +[[nodiscard]] constexpr auto enum_contains(E value) noexcept -> detail::enable_if_t<E, bool> {
  1191 + using D = std::decay_t<E>;
  1192 + using U = underlying_type_t<D>;
  1193 +
  1194 + return static_cast<bool>(enum_cast<D>(static_cast<U>(value)));
  1195 +}
  1196 +
  1197 +// Checks whether enum contains enumerator with such integer value.
  1198 +template <typename E>
  1199 +[[nodiscard]] constexpr auto enum_contains(underlying_type_t<E> value) noexcept -> detail::enable_if_t<E, bool> {
  1200 + using D = std::decay_t<E>;
  1201 +
  1202 + return static_cast<bool>(enum_cast<D>(value));
  1203 +}
  1204 +
  1205 +// Checks whether enum contains enumerator with such name.
  1206 +template <typename E, typename BinaryPredicate = std::equal_to<>>
  1207 +[[nodiscard]] constexpr auto enum_contains(string_view value, BinaryPredicate&& p = {}) noexcept(detail::is_nothrow_invocable<BinaryPredicate>()) -> detail::enable_if_t<E, bool, BinaryPredicate> {
  1208 + static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_contains requires bool(char, char) invocable predicate.");
  1209 + using D = std::decay_t<E>;
  1210 +
  1211 + return static_cast<bool>(enum_cast<D>(value, std::forward<BinaryPredicate>(p)));
  1212 +}
  1213 +
  1214 +template <typename Result = void, typename E, typename Lambda>
  1215 +constexpr auto enum_switch(Lambda&& lambda, E value) -> detail::enable_if_t<E, Result> {
  1216 + using D = std::decay_t<E>;
  1217 +
  1218 + return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
  1219 + std::forward<Lambda>(lambda),
  1220 + value,
  1221 + detail::default_result_type_lambda<Result>);
  1222 +}
  1223 +
  1224 +template <typename Result, typename E, typename Lambda>
  1225 +constexpr auto enum_switch(Lambda&& lambda, E value, Result&& result) -> detail::enable_if_t<E, Result> {
  1226 + using D = std::decay_t<E>;
  1227 +
  1228 + return detail::constexpr_switch<&detail::values_v<D>, detail::case_call_t::value>(
  1229 + std::forward<Lambda>(lambda),
  1230 + value,
  1231 + [&result] { return std::forward<Result>(result); });
  1232 +}
  1233 +
  1234 +template <typename E, typename Result = void, typename BinaryPredicate = std::equal_to<>, typename Lambda>
  1235 +constexpr auto enum_switch(Lambda&& lambda, string_view name, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
  1236 + static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
  1237 + using D = std::decay_t<E>;
  1238 +
  1239 + if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) {
  1240 + return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
  1241 + }
  1242 + return detail::default_result_type_lambda<Result>();
  1243 +}
  1244 +
  1245 +template <typename E, typename Result, typename BinaryPredicate = std::equal_to<>, typename Lambda>
  1246 +constexpr auto enum_switch(Lambda&& lambda, string_view name, Result&& result, BinaryPredicate&& p = {}) -> detail::enable_if_t<E, Result, BinaryPredicate> {
  1247 + static_assert(std::is_invocable_r_v<bool, BinaryPredicate, char, char>, "magic_enum::enum_switch requires bool(char, char) invocable predicate.");
  1248 + using D = std::decay_t<E>;
  1249 +
  1250 + if (const auto v = enum_cast<D>(name, std::forward<BinaryPredicate>(p))) {
  1251 + return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result));
  1252 + }
  1253 + return std::forward<Result>(result);
  1254 +}
  1255 +
  1256 +template <typename E, typename Result = void, typename Lambda>
  1257 +constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value) -> detail::enable_if_t<E, Result> {
  1258 + using D = std::decay_t<E>;
  1259 +
  1260 + if (const auto v = enum_cast<D>(value)) {
  1261 + return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v);
  1262 + }
  1263 + return detail::default_result_type_lambda<Result>();
  1264 +}
  1265 +
  1266 +template <typename E, typename Result, typename Lambda>
  1267 +constexpr auto enum_switch(Lambda&& lambda, underlying_type_t<E> value, Result&& result) -> detail::enable_if_t<E, Result> {
  1268 + using D = std::decay_t<E>;
  1269 +
  1270 + if (const auto v = enum_cast<D>(value)) {
  1271 + return enum_switch<Result, D>(std::forward<Lambda>(lambda), *v, std::forward<Result>(result));
  1272 + }
  1273 + return std::forward<Result>(result);
  1274 +}
  1275 +
  1276 +template <typename E, typename Lambda>
  1277 +constexpr auto enum_for_each(Lambda&& lambda) -> detail::enable_if_t<E, detail::for_each_t<E, Lambda>> {
  1278 + using D = std::decay_t<E>;
  1279 +
  1280 + return detail::for_each<D>(std::forward<Lambda>(lambda), std::make_index_sequence<detail::count_v<D>>{});
  1281 +}
  1282 +
  1283 +namespace detail {
  1284 +
  1285 +template <typename E>
  1286 +constexpr optional<std::uintmax_t> fuse_one_enum(optional<std::uintmax_t> hash, E value) noexcept {
  1287 + if (hash) {
  1288 + if (const auto index = enum_index(value)) {
  1289 + return (*hash << log2(enum_count<E>() + 1)) | *index;
  1290 + }
  1291 + }
  1292 + return {};
  1293 +}
  1294 +
  1295 +template <typename E>
  1296 +constexpr optional<std::uintmax_t> fuse_enum(E value) noexcept {
  1297 + return fuse_one_enum(0, value);
  1298 +}
  1299 +
  1300 +template <typename E, typename... Es>
  1301 +constexpr optional<std::uintmax_t> fuse_enum(E head, Es... tail) noexcept {
  1302 + return fuse_one_enum(fuse_enum(tail...), head);
  1303 +}
  1304 +
  1305 +template <typename... Es>
  1306 +constexpr auto typesafe_fuse_enum(Es... values) noexcept {
  1307 + enum class enum_fuse_t : std::uintmax_t;
  1308 + const auto fuse = fuse_enum(values...);
  1309 + if (fuse) {
  1310 + return optional<enum_fuse_t>{static_cast<enum_fuse_t>(*fuse)};
  1311 + }
  1312 + return optional<enum_fuse_t>{};
  1313 +}
  1314 +
  1315 +} // namespace magic_enum::detail
  1316 +
  1317 +// Returns a bijective mix of several enum values. This can be used to emulate 2D switch/case statements.
  1318 +template <typename... Es>
  1319 +[[nodiscard]] constexpr auto enum_fuse(Es... values) noexcept {
  1320 + static_assert((std::is_enum_v<std::decay_t<Es>> && ...), "magic_enum::enum_fuse requires enum type.");
  1321 + static_assert(sizeof...(Es) >= 2, "magic_enum::enum_fuse requires at least 2 values.");
  1322 + static_assert((detail::log2(enum_count<Es>() + 1) + ...) <= (sizeof(std::uintmax_t) * 8), "magic_enum::enum_fuse does not work for large enums");
  1323 +#if defined(MAGIC_ENUM_NO_TYPESAFE_ENUM_FUSE)
  1324 + const auto fuse = detail::fuse_enum<std::decay_t<Es>...>(values...);
  1325 +#else
  1326 + const auto fuse = detail::typesafe_fuse_enum<std::decay_t<Es>...>(values...);
  1327 +#endif
  1328 + return assert(fuse), fuse;
  1329 +}
  1330 +
  1331 +namespace ostream_operators {
  1332 +
  1333 +template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
  1334 +std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, E value) {
  1335 + using D = std::decay_t<E>;
  1336 + using U = underlying_type_t<D>;
  1337 +
  1338 + if constexpr (detail::supported<D>::value) {
  1339 + if (const auto name = enum_flags_name<D>(value); !name.empty()) {
  1340 + for (const auto c : name) {
  1341 + os.put(c);
  1342 + }
  1343 + return os;
  1344 + }
  1345 + }
  1346 + return (os << static_cast<U>(value));
  1347 +}
  1348 +
  1349 +template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
  1350 +std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& os, optional<E> value) {
  1351 + return value ? (os << *value) : os;
  1352 +}
  1353 +
  1354 +} // namespace magic_enum::ostream_operators
  1355 +
  1356 +namespace istream_operators {
  1357 +
  1358 +template <typename Char, typename Traits, typename E, detail::enable_if_t<E, int> = 0>
  1359 +std::basic_istream<Char, Traits>& operator>>(std::basic_istream<Char, Traits>& is, E& value) {
  1360 + using D = std::decay_t<E>;
  1361 +
  1362 + std::basic_string<Char, Traits> s;
  1363 + is >> s;
  1364 + if (const auto v = enum_cast<D>(s)) {
  1365 + value = *v;
  1366 + } else {
  1367 + is.setstate(std::basic_ios<Char>::failbit);
  1368 + }
  1369 + return is;
  1370 +}
  1371 +
  1372 +} // namespace magic_enum::istream_operators
  1373 +
  1374 +namespace iostream_operators {
  1375 +
  1376 +using namespace ostream_operators;
  1377 +using namespace istream_operators;
  1378 +
  1379 +} // namespace magic_enum::iostream_operators
  1380 +
  1381 +namespace bitwise_operators {
  1382 +
  1383 +template <typename E, detail::enable_if_t<E, int> = 0>
  1384 +constexpr E operator~(E rhs) noexcept {
  1385 + return static_cast<E>(~static_cast<underlying_type_t<E>>(rhs));
  1386 +}
  1387 +
  1388 +template <typename E, detail::enable_if_t<E, int> = 0>
  1389 +constexpr E operator|(E lhs, E rhs) noexcept {
  1390 + return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) | static_cast<underlying_type_t<E>>(rhs));
  1391 +}
  1392 +
  1393 +template <typename E, detail::enable_if_t<E, int> = 0>
  1394 +constexpr E operator&(E lhs, E rhs) noexcept {
  1395 + return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) & static_cast<underlying_type_t<E>>(rhs));
  1396 +}
  1397 +
  1398 +template <typename E, detail::enable_if_t<E, int> = 0>
  1399 +constexpr E operator^(E lhs, E rhs) noexcept {
  1400 + return static_cast<E>(static_cast<underlying_type_t<E>>(lhs) ^ static_cast<underlying_type_t<E>>(rhs));
  1401 +}
  1402 +
  1403 +template <typename E, detail::enable_if_t<E, int> = 0>
  1404 +constexpr E& operator|=(E& lhs, E rhs) noexcept {
  1405 + return lhs = (lhs | rhs);
  1406 +}
  1407 +
  1408 +template <typename E, detail::enable_if_t<E, int> = 0>
  1409 +constexpr E& operator&=(E& lhs, E rhs) noexcept {
  1410 + return lhs = (lhs & rhs);
  1411 +}
  1412 +
  1413 +template <typename E, detail::enable_if_t<E, int> = 0>
  1414 +constexpr E& operator^=(E& lhs, E rhs) noexcept {
  1415 + return lhs = (lhs ^ rhs);
  1416 +}
  1417 +
  1418 +} // namespace magic_enum::bitwise_operators
  1419 +
  1420 +} // namespace magic_enum
  1421 +
  1422 +#if defined(__clang__)
  1423 +# pragma clang diagnostic pop
  1424 +#elif defined(__GNUC__)
  1425 +# pragma GCC diagnostic pop
  1426 +#elif defined(_MSC_VER)
  1427 +# pragma warning(pop)
  1428 +#endif
  1429 +
  1430 +#endif // NEARGYE_MAGIC_ENUM_HPP
... ...
src/CMakeLists.txt
... ... @@ -9,6 +9,12 @@ include(compiler)
9 9 set(SRC_LIST
10 10 ${CMAKE_CURRENT_SOURCE_DIR}/modbusbase.h
11 11 ${CMAKE_CURRENT_SOURCE_DIR}/modbusbase.cpp
  12 + ${CMAKE_CURRENT_SOURCE_DIR}/modbustcp.h
  13 + ${CMAKE_CURRENT_SOURCE_DIR}/modbustcp.cpp
  14 + ${CMAKE_CURRENT_SOURCE_DIR}/modbusrtu.h
  15 + ${CMAKE_CURRENT_SOURCE_DIR}/modbusrtu.cpp
  16 + ${CMAKE_CURRENT_SOURCE_DIR}/connectionconfig.h
  17 + ${CMAKE_CURRENT_SOURCE_DIR}/connectionconfig.cpp
12 18 )
13 19  
14 20 include(library)
... ...
src/connectionconfig.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
... ...
src/connectionconfig.h 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +#pragma once
  8 +
  9 +namespace osdev {
  10 +namespace components {
  11 +namespace modbus {
  12 +
  13 +class ConnectionConfig
  14 +{
  15 +public:
  16 + ConnectionConfig
  17 +};
  18 +
  19 +} // End namespace modbus
  20 +} // End namespace components
  21 +} // End namespace osdev
... ...
src/modbusbase.cpp
... ... @@ -36,7 +36,7 @@ int ModbusBase::readCoils(uint16_t address, uint16_t amount, bool *buffer)
36 36  
37 37 for (auto i = 0; i < amount; i++)
38 38 {
39   - buffer[i] = (bool)((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);
  39 + buffer[i] = static_cast<bool>((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);
40 40 }
41 41 return 0;
42 42 }
... ... @@ -73,7 +73,7 @@ int ModbusBase::readInputBits(uint16_t address, uint16_t amount, bool *buffer)
73 73  
74 74 for (auto i = 0; i < amount; i++)
75 75 {
76   - buffer[i] = (bool)((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);
  76 + buffer[i] = static_cast<bool>((to_rec[9u + i / 8u] >> (i % 8u)) & 1u);
77 77 }
78 78 modbusErrorHandle(to_rec, READ_INPUT_BITS);
79 79 return 0;
... ... @@ -84,7 +84,7 @@ int ModbusBase::readInputBits(uint16_t address, uint16_t amount, bool *buffer)
84 84 }
85 85 }
86 86  
87   -int ModbusBase::readHoldingRegisters(uint16_t address, uitn16_t amount, uint16_t *buffer)
  87 +int ModbusBase::readHoldingRegisters(uint16_t address, uint16_t amount, uint16_t *buffer)
88 88 {
89 89 if (m_connected)
90 90 {
... ... @@ -103,8 +103,8 @@ int ModbusBase::readHoldingRegisters(uint16_t address, uitn16_t amount, uint16_t
103 103 }
104 104 for (auto i = 0; i < amount; i++)
105 105 {
106   - buffer[i] = ((uint16_t)to_rec[9u + 2u * i]) << 8u;
107   - buffer[i] += (uint16_t)to_rec[10u + 2u * i];
  106 + buffer[i] = (static_cast<uint16_t>(to_rec[9u + 2u * i])) << 8u;
  107 + buffer[i] += static_cast<uint16_t>(to_rec[10u + 2u * i]);
108 108 }
109 109 return 0;
110 110 }
... ... @@ -121,7 +121,7 @@ int ModbusBase::readInputRegisters(uint16_t address, uint16_t amount, uint16_t *
121 121 {
122 122 modbusRead(address, amount, READ_INPUT_REGS);
123 123 uint8_t to_rec[MAX_MSG_LENGTH];
124   - size_t result = modbusReceive(to_rec);
  124 + ssize_t result = modbusReceive(to_rec);
125 125 if (result == -1)
126 126 {
127 127 setBadConnection();
... ... @@ -136,8 +136,8 @@ int ModbusBase::readInputRegisters(uint16_t address, uint16_t amount, uint16_t *
136 136  
137 137 for (auto i = 0; i < amount; i++)
138 138 {
139   - buffer[i] = ((uint16_t)to_rec[9u + 2u * i]) << 8u;
140   - buffer[i] = (uint16_t)to_rec[10u + 2u * i];
  139 + buffer[i] = (static_cast<uint16_t>(to_rec[9u + 2u * i])) << 8u;
  140 + buffer[i] = static_cast<uint16_t>(to_rec[10u + 2u * i]);
141 141 }
142 142 return 0;
143 143 }
... ... @@ -153,7 +153,7 @@ int ModbusBase::writeCoil(uint16_t address, const bool &amp;to_write)
153 153 if (m_connected)
154 154 {
155 155 int value = to_write * 0xFF00;
156   - modbusWrite(address, 1, WRITE_COIL, (uint16_t *)&value);
  156 + modbusWrite(address, 1, WRITE_COIL, reinterpret_cast<uint16_t *>(&value));
157 157 uint8_t to_rec[MAX_MSG_LENGTH];
158 158 ssize_t result = modbusReceive(to_rec);
159 159 if (result == -1)
... ... @@ -182,7 +182,7 @@ int ModbusBase::writeRegister(uint16_t address, const uint16_t &amp;value)
182 182 {
183 183 modbusWrite(address, 1, WRITE_REG, &value);
184 184 uint8_t to_rec[MAX_MSG_LENGTH];
185   - ssize_t result = modebusReceive(to_rec);
  185 + ssize_t result = modbusReceive(to_rec);
186 186 if (result == -1)
187 187 {
188 188 setBadConnection();
... ... @@ -210,7 +210,7 @@ int ModbusBase::writeCoils(uint16_t address, uint16_t amount, const bool *value)
210 210 uint16_t *temp = new uint16_t[amount];
211 211 for (int i = 0; i < amount; i++)
212 212 {
213   - temp[i] = (uint16_t)value[i];
  213 + temp[i] = static_cast<uint16_t>(value[i]);
214 214 }
215 215  
216 216 modbusWrite(address, amount, WRITE_COILS, temp);
... ... @@ -267,15 +267,15 @@ int ModbusBase::writeRegisters(uint16_t address, uint16_t amount, const uint16_t
267 267  
268 268 void ModbusBase::buildRequest(uint8_t *to_send, uint16_t address, int function_code) const
269 269 {
270   - to_send[0] = (uint8_t)(m_msg_id >> 8u);
271   - to_send[1] = (uint8_t)(m_msg_id & 0x00FFu);
  270 + to_send[0] = static_cast<uint8_t>(m_msg_id >> 8u);
  271 + to_send[1] = static_cast<uint8_t>(m_msg_id & 0x00FFu);
272 272 to_send[2] = 0;
273 273 to_send[3] = 0;
274 274 to_send[4] = 0;
275   - to_send[6] = (uint8_t)m_slaveId;
276   - to_send[7] = (uint8_t)function_code;
277   - to_send[8] = (uint8_t)(address >> 8u);
278   - to_send[9] = (uint8_t)(address & 0x00FFu);
  275 + to_send[6] = static_cast<uint8_t>(m_slaveId);
  276 + to_send[7] = static_cast<uint8_t>(function_code);
  277 + to_send[8] = static_cast<uint8_t>(address >> 8u);
  278 + to_send[9] = static_cast<uint8_t>(address & 0x00FFu);
279 279 }
280 280  
281 281 int ModbusBase::modbusRead(uint16_t address, uint16_t amount, int function_code)
... ... @@ -286,8 +286,8 @@ int ModbusBase::modbusRead(uint16_t address, uint16_t amount, int function_code)
286 286 uint8_t to_send[12];
287 287 buildRequest(to_send, address, function_code);
288 288 to_send[5] = 6;
289   - to_send[10] = (uint8_t)(amount >> 8u);
290   - to_send[11] = (uint8_t)(amount & 0x00FFu);
  289 + to_send[10] = static_cast<uint8_t>(amount >> 8u);
  290 + to_send[11] = static_cast<uint8_t>(amount & 0x00FFu);
291 291 return modbusSend(to_send, 12);
292 292 }
293 293  
... ... @@ -308,8 +308,8 @@ int ModbusBase::modbusWrite(uint16_t address, uint16_t amount, int function_code
308 308 to_send = new uint8_t[12];
309 309 buildRequest(to_send, address, function_code);
310 310 to_send[5] = 6;
311   - to_send[10] = (uint8_t)(value[0] >> 8u);
312   - to_send[11] = (uint8_t)(value[0] & 0x00FFu);
  311 + to_send[10] = static_cast<uint8_t>(value[0] >> 8u);
  312 + to_send[11] = static_cast<uint8_t>(value[0] & 0x00FFu);
313 313 status = modbusSend(to_send, 12);
314 314 break;
315 315 }
... ... @@ -317,14 +317,14 @@ int ModbusBase::modbusWrite(uint16_t address, uint16_t amount, int function_code
317 317 {
318 318 to_send = new uint8_t[13 + 2 * amount];
319 319 buildRequest(to_send, address, function_code);
320   - to_send[5] = (uint8_t)(7 + 2 * amount);
321   - to_send[10] = (uint8_t)(amount >> 8u);
322   - to_send[11] = (uint8_t)(amount & 0x00FFu);
323   - to_send[12] = (uint8_t)(2 * amount);
  320 + to_send[5] = static_cast<uint8_t>(7 + 2 * amount);
  321 + to_send[10] = static_cast<uint8_t>(amount >> 8u);
  322 + to_send[11] = static_cast<uint8_t>(amount & 0x00FFu);
  323 + to_send[12] = static_cast<uint8_t>(2 * amount);
324 324 for (int i = 0; i < amount; i++)
325 325 {
326   - to_send[13 + 2 * i] = (uint8_t)(value[i] >> 8u);
327   - to_send[14 + 2 * i] = (uint8_t)(value[i] & 0x00FFu);
  326 + to_send[13 + 2 * i] = static_cast<uint8_t>(value[i] >> 8u);
  327 + to_send[14 + 2 * i] = static_cast<uint8_t>(value[i] & 0x00FFu);
328 328 }
329 329 status = modbusSend(to_send, 13 + 2 * amount);
330 330 break;
... ... @@ -333,17 +333,17 @@ int ModbusBase::modbusWrite(uint16_t address, uint16_t amount, int function_code
333 333 {
334 334 to_send = new uint8_t[14 + ( amount - 1 ) / 8];
335 335 buildRequest(to_send, address, function_code);
336   - to_send[5] = (uint8_t)(7 + ( amount + 7 ) / 8);
337   - to_send[10] = (uint8_t)(amount >> 8u);
338   - to_send[11] = (uint8_t)(amount & 0x00FFu);
339   - to_send[12] = (uint8_t)((amount + 7) / 8);
  336 + to_send[5] = static_cast<uint8_t>(7 + ( amount + 7 ) / 8);
  337 + to_send[10] = static_cast<uint8_t>(amount >> 8u);
  338 + to_send[11] = static_cast<uint8_t>(amount & 0x00FFu);
  339 + to_send[12] = static_cast<uint8_t>((amount + 7) / 8);
340 340 for (int i = 0; i < (amount + 7) / 8; i++)
341 341 {
342   - to_send[13 + i] = 0 // Init needed before summing.
  342 + to_send[13 + i] = 0; // Init needed before summing.
343 343 }
344 344 for (int i = 0; i < amount; i++)
345 345 {
346   - to_send[13 + i / 8] += (uint8_t)(value[i] << (i % 8u));
  346 + to_send[13 + i / 8] += static_cast<uint8_t>(value[i] << (i % 8u));
347 347 }
348 348 status = modbusSend(to_send, 13 + (amount - 1) / 8);
349 349 }
... ... @@ -354,17 +354,23 @@ int ModbusBase::modbusWrite(uint16_t address, uint16_t amount, int function_code
354 354  
355 355 ssize_t ModbusBase::modbusSend(uint8_t *to_send, size_t length)
356 356 {
  357 + (void)to_send;
  358 + (void)length;
357 359  
  360 + return 0;
358 361 }
359 362  
360 363 ssize_t ModbusBase::modbusReceive(uint8_t *buffer) const
361 364 {
  365 + (void)buffer;
362 366  
  367 + return 0;
363 368 }
364 369  
365 370 void ModbusBase::modbusErrorHandle(const uint8_t *msg, int function_code)
366 371 {
367   -
  372 + (void)msg;
  373 + (void)function_code;
368 374 }
369 375  
370 376 void ModbusBase::setBadConnection()
... ...
src/modbusbase.h
... ... @@ -4,9 +4,10 @@
4 4 * This source code is licensed under the MIT license found in the
5 5 * LICENSE file in the root directory of this source tree.
6 6 */
7   -
8 7 #pragma once
9 8  
  9 +#include <string>
  10 +
10 11 // Create a simple logger for console output during debugging.
11 12 // TODO: Replace with a custom logger by using std::function
12 13 // to keep it consistent with TrueMQTT.
... ... @@ -17,7 +18,8 @@
17 18 #define LOG(...) (void)0
18 19 #endif
19 20  
20   -#define MAX_MSG_LENGTH 260
  21 +#define MAX_MSG_LENGTH_TCP 260
  22 +#define MAX_MSG_LENGTH_RTU 252
21 23  
22 24 // Function Codes
23 25 #define READ_COILS 0x01
... ... @@ -68,7 +70,7 @@ public:
68 70 // Pure virtuals. Override when inherited.
69 71 virtual bool Connect() const = 0;
70 72 virtual void Close() = 0;
71   - virtual bool isConnected() const - 0;
  73 + virtual bool isConnected() const = 0;
72 74  
73 75 // Modbus implementation(s)
74 76 /*!
... ... @@ -96,7 +98,7 @@ public:
96 98 * \param amount - Amount of Registers to Read
97 99 * \param buffer - Buffer to Store Data Read from Registers
98 100 */
99   - int readHoldingRegisters(uint16_t address, uitn16_t amount, uint16_t *buffer);
  101 + int readHoldingRegisters(uint16_t address, uint16_t amount, uint16_t *buffer);
100 102  
101 103 /*!
102 104 * Read Input Registers
... ... @@ -159,18 +161,40 @@ private: // Methods
159 161 * \param function_code - Modbus Functional Code
160 162 * \param value - Data to be written
161 163 *
162   - * \return int
  164 + * \return int -
163 165 */
164 166 int modbusWrite(uint16_t address, uint16_t amount, int function_code, const uint16_t *value);
165 167  
  168 + /*!
  169 + * \brief modbusSend
  170 + * \param to_send
  171 + * \param length
  172 + * \return
  173 + */
166 174 virtual ssize_t modbusSend(uint8_t *to_send, size_t length) = 0;
167 175  
  176 + /*!
  177 + * \brief modbusReceive
  178 + * \param buffer
  179 + * \return
  180 + */
168 181 virtual ssize_t modbusReceive(uint8_t *buffer) const = 0;
169 182  
  183 + /*!
  184 + * \brief modbusErrorHandle
  185 + * \param msg
  186 + * \param function_code
  187 + */
170 188 void modbusErrorHandle(const uint8_t *msg, int function_code);
171 189  
  190 + /*!
  191 + * \brief setBadConnection
  192 + */
172 193 void setBadConnection();
173 194  
  195 + /*!
  196 + * \brief setBadInput
  197 + */
174 198 void setBadInput();
175 199  
176 200 private: // Members (Giggity!)
... ...
src/modbusrtu.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +#include "modbusrtu.h"
  8 +
  9 +using namespace osdev::components::modbus;
  10 +
  11 +ModbusRTU::ModbusRTU()
  12 +{
  13 +
  14 +}
... ...
src/modbusrtu.h 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +#pragma once
  8 +
  9 +#include "modbusbase.h"
  10 +
  11 +namespace osdev {
  12 +namespace components {
  13 +namespace modbus {
  14 +
  15 +class ModbusRTU : public ModbusBase
  16 +{
  17 +public:
  18 + ModbusRTU();
  19 +};
  20 +
  21 +} // End namespace modbus
  22 +} // End namespace components
  23 +} // End namespace osdev
... ...
src/modbustcp.cpp 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +#include "modbustcp.h"
  8 +
  9 +using namespace osdev::components::modbus;
  10 +
  11 +ModbusTCP::ModbusTCP()
  12 +{
  13 +
  14 +}
... ...
src/modbustcp.h 0 → 100644
  1 +/*
  2 + * Copyright (c) 2022 Peter M. Groen
  3 + *
  4 + * This source code is licensed under the MIT license found in the
  5 + * LICENSE file in the root directory of this source tree.
  6 + */
  7 +#pragma once
  8 +
  9 +#include "modbusbase.h"
  10 +
  11 +namespace osdev {
  12 +namespace components {
  13 +namespace modbus {
  14 +
  15 +class ModbusTCP : public ModbusBase
  16 +{
  17 +public:
  18 + ModbusTCP();
  19 +};
  20 +
  21 +} // End namespace modbus
  22 +} // End namespace components
  23 +} // End namespace osdev
... ...