From 08e5eda53bb6baa2e8962fce261f54999aa64e19 Mon Sep 17 00:00:00 2001 From: David Dight Date: Sat, 30 Mar 2024 21:00:21 +1100 Subject: [PATCH] pre-v1.0-update2 --- CMakeLists.txt | 42 ++--- examples/example.cpp | 208 ++++++++++++----------- examples/unittests.cpp | 95 ++++++----- include/fix8/conjure_enum.hpp | 312 +++++++++++++++++++++------------- 4 files changed, 368 insertions(+), 289 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d2408218..6ab65b2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,39 +1,33 @@ # ----------------------------------------------------------------------------------------- +# SPDX-License-Identifier: MIT # conjure_enum (header only) # Copyright (C) 2024 Fix8 Market Technologies Pty Ltd # by David L. Dight # see https://github.com/fix8mt/conjure_enum # +# Lightweight header-only C++20 enum reflection +# # Based on magic_enum -# Licensed under the MIT License . -# SPDX-License-Identifier: MIT # Copyright (c) 2019 - 2024 Daniil Goncharov . # -# Lightweight header-only C++20 enum reflection -# -# Distributed under the Boost Software License, Version 1.0 August 17th, 2003 +# Licensed under the MIT License . # -# Permission is hereby granted, free of charge, to any person or organization -# obtaining a copy of the software and accompanying documentation covered by -# this license (the "Software") to use, reproduce, display, distribute, -# execute, and transmit the Software, and to prepare derivative works of the -# Software, and to permit third-parties to whom the Software is furnished to -# do so, all subject to the following: +# 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 copyright notices in the Software and this entire statement, including -# the above license grant, this restriction and the following disclaimer, -# must be included in all copies of the Software, in whole or in part, and -# all derivative works of the Software, unless such copies or derivative -# works are solely in the form of machine-executable object code generated by -# a source language processor. +# The above copyright notice and this permission notice (including the next paragraph) +# 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -# SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -# FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. +# 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. # ---------------------------------------------------------------------------------------- cmake_minimum_required (VERSION 3.20) project (conjure_enum diff --git a/examples/example.cpp b/examples/example.cpp index 214e995e..2f3e47fd 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -1,128 +1,78 @@ //----------------------------------------------------------------------------------------- +// SPDX-License-Identifier: MIT // conjure_enum (header only) // Copyright (C) 2024 Fix8 Market Technologies Pty Ltd // by David L. Dight // see https://github.com/fix8mt/conjure_enum // +// Lightweight header-only C++20 enum reflection +// // Based on magic_enum -// Licensed under the MIT License . -// SPDX-License-Identifier: MIT // Copyright (c) 2019 - 2024 Daniil Goncharov . // -// Lightweight header-only C++20 enum reflection -// -// Distributed under the Boost Software License, Version 1.0 August 17th, 2003 +// Licensed under the MIT License . // -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: +// 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 copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. +// The above copyright notice and this permission notice (including the next paragraph) +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //---------------------------------------------------------------------------------------- #include #include +#include +#include +#if defined(__clang__) || defined(__GNUC__) +#include +#endif #include +//----------------------------------------------------------------------------------------- using namespace std::literals::string_view_literals; using namespace std::literals::string_literals; -//----------------------------------------------------------------------------------------- using namespace FIX8; +//----------------------------------------------------------------------------------------- enum class component : int { scheme, authority, userinfo, user, password, host, port, path=12, test=path, query, fragment }; enum component1 : int { scheme, authority, userinfo, user, password, host, port, path=12, query, fragment }; enum class numbers : int { zero, one, two, three, four, five, six, seven, eight, nine }; enum class numbers1 : int { zero=4, one=3, two=2, three, four, five, six, seven, eight, nine }; -int main(int argc, char *argv[]) +//----------------------------------------------------------------------------------------- +template +const std::string demangle() noexcept { - /* - std::cout << get_name(epeek()) << '\n'; - */ - if (conjure_enum::is_valid()) - std::cout << "valid\n"; - else - std::cout << "not valid\n"; - if (conjure_enum::is_valid(16)>()) - std::cout << "valid\n"; - else - std::cout << "not valid\n"; - - /* - for(const auto ev : conjure_enum::enum_values) - std::cout << static_cast(ev) << '\n'; - for(const auto [a, b] : conjure_enum::enum_entries) - std::cout << '*' << static_cast(a) << ' ' << conjure_enum::remove_scope(b) << '\n'; - for(const auto ev : conjure_enum::enum_values) - std::cout << static_cast(ev) << '\n'; - for(const auto ev : conjure_enum::enum_names) - std::cout << ev << '\n'; - std::cout << conjure_enum::enum_names[5] << '\n'; - enum_bitset n1(numbers::zero,numbers::one,numbers::three,numbers::seven,numbers::nine); - n1.for_each([](numbers val) noexcept +#if defined(__clang__) || defined(__GNUC__) + int status; + std::unique_ptr eptr(abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status), &free); + switch (status) { - std::cout << conjure_enum::enum_to_string(val) << '(' << static_cast(val) << ')' << '\n'; - }); - */ - /* - for (auto itr{conjure_enum::cbegin()}; itr != conjure_enum::cend(); ++itr) - { - const auto [a, b] {*itr}; - std::cout << static_cast(a) << ' ' << conjure_enum::remove_scope(b) << '\n'; - }*/ - - /* - std::cout << "1: " << std::string_view(conjure_enum::enum_name_v) << '\n'; - std::cout << "2: " << conjure_enum::enum_name().get() << '\n'; - std::cout << "3: " << conjure_enum::enum_to_string(component::path, true) << '\n'; - std::cout << "4: " << conjure_enum::enum_to_string(component::test) << '\n'; - std::cout << "16: " << '\"' << conjure_enum::enum_to_string(static_cast(100)) << '\"' << '\n'; - std::cout << "5: " << conjure_enum::enum_to_string(path) << '\n'; - std::cout << "6: " << static_cast(conjure_enum::string_to_enum("component::path").value()) << '\n'; - std::cout << "7: " << static_cast(conjure_enum::string_to_enum("path").value()) << '\n'; - std::cout << "8: " << static_cast(conjure_enum::string_to_enum("wrong").value_or(component(100))) << '\n'; - std::cout << "9: " << conjure_enum::get_name() << '\n'; - std::cout << "10: " << conjure_enum::get_name() << '\n'; - std::cout << "16: " << conjure_enum::get_type() << '\n'; - std::cout << "16: " << conjure_enum::get_type() << '\n'; - */ - - /* - //using enum component; - //std::cout << std::boolalpha << is_scoped() << '\n'; - std::cout << "11: " << std::boolalpha << conjure_enum::is_scoped() << '\n'; - std::cout << "12: " << std::boolalpha << conjure_enum::is_scoped() << '\n'; - std::string_view res1 { conjure_enum::enum_name_v }; - std::cout << "13: " << res1 << '\n'; - std::cout << "14: " << conjure_enum::enum_name_v << '\n'; - std::cout << "15: " << conjure_enum::count() << '\n'; - if (auto result {conjure_enum::int_to_enum(100)}; result) - std::cout << static_cast(*result) << '\n'; - else - std::cout << "invalid int to enum\n"; - if (auto result {conjure_enum::int_to_enum(12)}; result) - std::cout << static_cast(*result) << '\n'; - else - std::cout << "invalid int to enum\n"; + case 0: return eptr.get(); + case -1: return "memory allocation failiure"s; + case -2: return "invalid mangled name"s; + default: break; // includes -3 invalid argument + } + return "invalid argument"s; +#endif + return typeid(T).name(); +} +int main(int argc, char *argv[]) +{ int total{}; - auto myfunc { conjure_enum::for_each([](component val, int other, int& tot) + auto myfunc { conjure_enum::for_each([](component val, int other, int& tot) { std::cout << static_cast(val) << ' ' << other << '\n'; tot += static_cast(val); @@ -130,15 +80,14 @@ int main(int argc, char *argv[]) myfunc(component::fragment); std::cout << total << '\n'; - // enum_bitset - enum class numbers : int { zero, one, two, three, four, five, six, seven, eight, nine }; - //enum_bitset ec; enum_bitset eb; eb.set_all(); std::cout << eb << '\n'; - std::cout << eb.test_all() << '\n'; - eb.clear_all(); - std::cout << eb.test_all() << '\n'; + std::cout << std::boolalpha << eb.test_all() << '\n'; + eb.clear_all(); + std::cout << std::boolalpha << eb.test_all(0, 2, 5, 9) << '\n'; + std::cout << std::boolalpha << eb.test_any(0, 2, 5, 9) << '\n'; + std::cout << std::boolalpha << eb.test_all(numbers::zero,numbers::nine) << '\n'; std::cout << eb << '\n'; eb.clear(numbers::nine); std::cout << eb << '\n'; @@ -155,16 +104,71 @@ int main(int argc, char *argv[]) ec.flip(); std::cout << ec << '\n'; std::cout << std::boolalpha << conjure_enum::enum_contains("component::path"sv) << '\n'; - std::cout << std::boolalpha << conjure_enum::enum_contains(argv[1]) << '\n'; + //std::cout << std::boolalpha << conjure_enum::enum_contains(argv[1]) << '\n'; for(const auto [a, b] : conjure_enum::enum_entries_sorted) std::cout << conjure_enum::remove_scope(b) << ' ' << static_cast(a) << '\n'; for(const auto [a, b] : conjure_enum::enum_entries) std::cout << b << ' ' << static_cast(a) << '\n'; - */ std::cout << conjure_enum::add_scope("path"sv) << '\n'; std::cout << conjure_enum::add_scope("component::path"sv) << '\n'; std::cout << conjure_enum::add_scope("path"sv) << '\n'; + std::cout << conjure_enum::enum_to_string() << '\n'; + std::cout << conjure_enum::enum_to_string() << '\n'; + using cje = conjure_enum; + std::cout << std::boolalpha << cje::has_scope("component::scheme") << '\n'; + std::cout << std::boolalpha << cje::has_scope("scheme") << '\n'; + std::cout << std::boolalpha << cje::has_scope("scheme") << '\n'; std::cout << conjure_enum::epeek() << '\n'; std::cout << conjure_enum::tpeek() << '\n'; + std::cout << demangle::enum_scoped_entries)>() << '\n'; + for(const auto [a, b] : conjure_enum::enum_scoped_entries) + std::cout << a << ' ' << b << '\n'; + std::cout << std::format(R"("{}")", conjure_enum::enum_to_string(static_cast(100))) << '\n'; + std::cout << std::format(R"("{}")", conjure_enum::enum_to_string(static_cast(100))) << '\n'; + enum_bitset a(0, 1, 2, 3); + std::cout << a << '\n'; + enum_bitset b(numbers::zero, numbers::one, numbers::two, numbers::three); + std::cout << b << '\n'; + enum_bitset c(15); + std::cout << c << '\n'; + for(const auto [a, b] : conjure_enum::enum_scoped_entries) + std::cout << std::format("{:9} {}\n", a, b); + for(const auto [value, str] : conjure_enum::enum_entries) // scoped + std::cout << std::format("{:<2} {}\n", static_cast(value), str); + std::cout << std::format("{}\n", conjure_enum::enum_contains("component::path"sv)); + std::cout << std::format("\"{}\"\n", conjure_enum::enum_to_string()); + auto printer([](numbers val) + { + std::cout << conjure_enum::enum_to_string(val) << '\n'; + }); + enum_bitset ec1("numbers::zero |numbers::two |numbers::five| numbers::nine", false); + std::cout << ec1 << '\n'; + ec1.for_each(printer); + enum_bitset ed("zero|two|five|\tnine", true); + std::cout << ed << '\n'; + ed.for_each(printer); + try + { + enum_bitset ee("twenty,two,rubbish,nine", true, ',', false); + std::cout << ee << '\n'; + ee.for_each(printer); + } + catch(const std::invalid_argument& e) + { + std::cerr << "exception: " << e.what() << '\n'; + } + enum_bitset ef("one|three|four|eight"sv, true); + std::cout << ef << '\n'; + ef.for_each(printer); + int a1{1}, b1{2}, c1{3}, d1{}; + enum_bitset eg(d1, a1, b1, c1); + std::cout << eg << '\n'; + enum_bitset ek(0, 1, 2, 3); + std::cout << ek << '\n'; + std::cout << (ek & 0b111) << '\n'; + ek ^= numbers::two; + std::cout << ek << '\n'; + enum_bitset eh("one three four eight "sv, true, ' '); + std::cout << eh << '\n'; return 0; } diff --git a/examples/unittests.cpp b/examples/unittests.cpp index 753cd611..52d4f136 100644 --- a/examples/unittests.cpp +++ b/examples/unittests.cpp @@ -1,39 +1,33 @@ //----------------------------------------------------------------------------------------- +// SPDX-License-Identifier: MIT // conjure_enum (header only) // Copyright (C) 2024 Fix8 Market Technologies Pty Ltd // by David L. Dight // see https://github.com/fix8mt/conjure_enum // +// Lightweight header-only C++20 enum reflection +// // Based on magic_enum -// Licensed under the MIT License . -// SPDX-License-Identifier: MIT // Copyright (c) 2019 - 2024 Daniil Goncharov . // -// Lightweight header-only C++20 enum reflection -// -// Distributed under the Boost Software License, Version 1.0 August 17th, 2003 +// Licensed under the MIT License . // -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: +// 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 copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. +// The above copyright notice and this permission notice (including the next paragraph) +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //---------------------------------------------------------------------------------------- #include #include @@ -75,13 +69,6 @@ TEST_CASE("count") REQUIRE(conjure_enum::count() == 10); } -//----------------------------------------------------------------------------------------- -TEST_CASE("enum_name") -{ - REQUIRE(conjure_enum::enum_name() == "component::fragment"sv); - REQUIRE(conjure_enum::enum_name() == "fragment"sv); -} - //----------------------------------------------------------------------------------------- TEST_CASE("enum_names") { @@ -177,15 +164,25 @@ TEST_CASE("enum_to_string") REQUIRE(conjure_enum::enum_to_string(component::test) == "component::path"); // alias REQUIRE(conjure_enum::enum_to_string(path) == "path"); REQUIRE(conjure_enum::enum_to_string(static_cast(100)).empty()); + REQUIRE(conjure_enum::enum_to_string() == "component::fragment"); + REQUIRE(conjure_enum::enum_to_string() == "fragment"); } //----------------------------------------------------------------------------------------- TEST_CASE("remove_scope") { using comp0 = conjure_enum; - REQUIRE(comp0::remove_scope(comp0::enum_name()) == "fragment"sv); + REQUIRE(comp0::remove_scope(comp0::enum_to_string()) == "fragment"sv); using comp1 = conjure_enum; - REQUIRE(comp1::remove_scope(comp1::enum_name()) == "fragment"sv); + REQUIRE(comp1::remove_scope(comp1::enum_to_string()) == "fragment"sv); +} + +//----------------------------------------------------------------------------------------- +TEST_CASE("add_scope") +{ + REQUIRE(conjure_enum::add_scope("path"sv) == "component::path"sv); + REQUIRE(conjure_enum::add_scope("component::path"sv) == "component::path"sv); + REQUIRE(conjure_enum::add_scope("path"sv) == "path"sv); } //----------------------------------------------------------------------------------------- @@ -199,6 +196,10 @@ TEST_CASE("iterators") for (; itr != conjure_enum::cend(); ++itr) ++cnt; REQUIRE(cnt == conjure_enum::count()); + REQUIRE(std::get(conjure_enum::back()) == component::fragment); + REQUIRE(std::get(conjure_enum::front()) == component::scheme); + REQUIRE(std::get(conjure_enum::front()) == component::scheme); + REQUIRE(std::get(conjure_enum::back()) == std::get(*conjure_enum::crbegin())); } //----------------------------------------------------------------------------------------- @@ -221,15 +222,15 @@ TEST_CASE("int_to_enum") //----------------------------------------------------------------------------------------- TEST_CASE("get_name") { - REQUIRE(conjure_enum::get_name() == "component::scheme"); - REQUIRE(conjure_enum::get_name() == "scheme"); + REQUIRE(conjure_enum::enum_to_string() == "component::scheme"); + REQUIRE(conjure_enum::enum_to_string() == "scheme"); } //----------------------------------------------------------------------------------------- -TEST_CASE("get_type") +TEST_CASE("enum_type") { - REQUIRE(conjure_enum::get_type() == "component"); - REQUIRE(conjure_enum::get_type() == "component1"); + REQUIRE(conjure_enum::enum_type() == "component"); + REQUIRE(conjure_enum::enum_type() == "component1"); } //----------------------------------------------------------------------------------------- @@ -281,15 +282,15 @@ TEST_CASE("enum_bitset") enum_bitset ed(numbers::two,numbers::three,numbers::four,numbers::seven); REQUIRE(ed.test_all()); REQUIRE(ed.test_any()); - REQUIRE((ed << 1) == 0b0100111000); + REQUIRE((ed << 1).to_ulong() == 0b0100111000); ed <<= 1; REQUIRE(ed.to_ulong() == 0b0100111000); - REQUIRE((ed >> 1) == 0b0010011100); + REQUIRE((ed >> 1).to_ulong() == 0b0010011100); ed >>= 1; REQUIRE(ed.to_ulong() == 0b0010011100); - REQUIRE((ed | numbers::one) == 0b0010011110); - REQUIRE((ed & numbers::two) == 0b100); + REQUIRE((ed | numbers::one).to_ulong() == 0b0010011110); + REQUIRE((ed & numbers::two).to_ulong() == 0b100); ed |= numbers::one; REQUIRE(ed.to_ulong() == 0b0010011110); ed &= numbers::one; @@ -298,7 +299,7 @@ TEST_CASE("enum_bitset") ed.clear(); REQUIRE(!ed); - REQUIRE((ed ^ numbers::one) == 0b010); + REQUIRE((ed ^ numbers::one).to_ulong() == 0b010); ed ^= numbers::one; REQUIRE(ed.to_ulong() == 0b010); @@ -314,5 +315,13 @@ numbers::three(3) numbers::five(5) numbers::seven(7) )"); + + int total{}; + enum_bitset enc(numbers::two,numbers::three,numbers::four,numbers::seven); + enc.for_each([](numbers val, int& tot) + { + tot += static_cast(val); + }, std::ref(total)); + REQUIRE(total == 16); } diff --git a/include/fix8/conjure_enum.hpp b/include/fix8/conjure_enum.hpp index a55ae0f2..09220a3f 100644 --- a/include/fix8/conjure_enum.hpp +++ b/include/fix8/conjure_enum.hpp @@ -1,39 +1,33 @@ //----------------------------------------------------------------------------------------- +// SPDX-License-Identifier: MIT // conjure_enum (header only) // Copyright (C) 2024 Fix8 Market Technologies Pty Ltd // by David L. Dight // see https://github.com/fix8mt/conjure_enum // +// Lightweight header-only C++20 enum reflection +// // Based on magic_enum -// Licensed under the MIT License . -// SPDX-License-Identifier: MIT // Copyright (c) 2019 - 2024 Daniil Goncharov . // -// Lightweight header-only C++20 enum reflection -// -// Distributed under the Boost Software License, Version 1.0 August 17th, 2003 +// Licensed under the MIT License . // -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: +// 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 copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. +// The above copyright notice and this permission notice (including the next paragraph) +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. +// 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 FIX8_CONJURE_ENUM_HPP_ #define FIX8_CONJURE_ENUM_HPP_ @@ -48,6 +42,7 @@ #include #include #include +#include #include #include @@ -76,28 +71,24 @@ class fixed_string final friend std::ostream& operator<<(std::ostream& os, const fixed_string& what) { return os << what.get(); } }; +//----------------------------------------------------------------------------------------- template requires std::is_enum_v class conjure_enum { static constexpr int enum_min_value{ENUM_MIN_VALUE}, enum_max_value{ENUM_MAX_VALUE}; - using enum_tuple = std::tuple; - using scoped_tuple = std::tuple; - - template - static constexpr auto entries(std::index_sequence) noexcept - { - return std::array{{{ enum_values[I], enum_name_v}...}}; - } - -public: template static constexpr auto enum_name() noexcept { constexpr auto result { get_name() }; return fixed_string(result); } + +public: + using enum_tuple = std::tuple; + using scoped_tuple = std::tuple; + template static constexpr auto enum_name_v { enum_name() }; @@ -106,6 +97,12 @@ class conjure_enum static consteval const char *epeek() noexcept { return std::source_location::current().function_name(); } private: + template + static constexpr auto entries(std::index_sequence) noexcept + { + return std::array{{{ enum_values[I], enum_name_v}...}}; + } + template static constexpr auto scoped_entries(std::index_sequence) noexcept { @@ -140,47 +137,27 @@ class conjure_enum return vals; } - static constexpr bool value_comp(const T& pl, const T& pr) noexcept - { - return static_cast(pl) < static_cast(pr); - } - static constexpr bool tuple_comp(const enum_tuple& pl, const enum_tuple& pr) noexcept - { - return static_cast(std::get(pl)) < static_cast(std::get(pr)); - } - static constexpr bool tuple_comp_rev(const enum_tuple& pl, const enum_tuple& pr) noexcept - { - return std::get(pl) < std::get(pr); - } - static constexpr bool scoped_comp(const scoped_tuple& pl, const scoped_tuple& pr) noexcept - { - return std::get<0>(pl) < std::get<0>(pr); - } - -public: - conjure_enum() = delete; - template static constexpr std::string_view get_name() noexcept { - /* clang - static const char *FIX8::conjure_enum::epeek() [T = component, e = component::path] - |<-- -->| - gcc - static consteval const char* FIX8::conjure_enum::epeek() [with T e = component::path; T = component] + /* clang + static const char *FIX8::conjure_enum::epeek() [T = component, e = component::path] |<-- -->| - */ + gcc + static consteval const char* FIX8::conjure_enum::epeek() [with T e = component::path; T = component] + |<-- -->| + */ constexpr std::string_view from{epeek()}; #if defined(__clang__) || defined(__GNUC__) if (constexpr auto ep { from.rfind("e = ") }; ep != std::string_view::npos && from[ep + 4] != '(') { - std::string_view result { from.substr(ep + 4) }; + constexpr std::string_view result { from.substr(ep + 4) }; #if defined(__clang__) # define ptrm (']') #else # define ptrm (';') #endif - if (auto lc { result.find_first_of(ptrm) }; lc != std::string_view::npos) + if (constexpr auto lc { result.find_first_of(ptrm) }; lc != std::string_view::npos) return result.substr(0, lc); #undef ptrm } @@ -192,21 +169,44 @@ class conjure_enum return {}; } - static constexpr std::string_view get_type() noexcept + static constexpr bool value_comp(const T& pl, const T& pr) noexcept { - /* clang - static const char *FIX8::conjure_enum::tpeek() [T = component] - |<-- -->| - gcc - static consteval const char* FIX8::conjure_enum::tpeek() [with T = component] - |<-- -->| - */ + return static_cast(pl) < static_cast(pr); + } + static constexpr bool tuple_comp(const enum_tuple& pl, const enum_tuple& pr) noexcept + { + return static_cast(std::get(pl)) < static_cast(std::get(pr)); + } + static constexpr bool tuple_comp_rev(const enum_tuple& pl, const enum_tuple& pr) noexcept + { + return std::get(pl) < std::get(pr); + } + static constexpr bool scoped_comp(const scoped_tuple& pl, const scoped_tuple& pr) noexcept + { + return std::get<0>(pl) < std::get<0>(pr); + } + +public: + conjure_enum() = delete; + + template + static constexpr std::string_view enum_to_string() noexcept { return get_name(); } + + static constexpr std::string_view enum_type() noexcept + { + /* clang + static const char *FIX8::conjure_enum::tpeek() [T = component] + |<-- -->| + gcc + static consteval const char* FIX8::conjure_enum::tpeek() [with T = component] + |<-- -->| + */ constexpr std::string_view from{tpeek()}; #if defined(__clang__) || defined(__GNUC__) if (constexpr auto ep { from.rfind("T = ") }; ep != std::string_view::npos) { - std::string_view result { from.substr(ep + 4) }; - if (auto lc { result.find_first_of(']') }; lc != std::string_view::npos) + constexpr std::string_view result { from.substr(ep + 4) }; + if (constexpr auto lc { result.find_first_of(']') }; lc != std::string_view::npos) return result.substr(0, lc); } #elif defined(_MSC_VER) @@ -235,7 +235,7 @@ class conjure_enum { if constexpr (is_scoped()) { - if (auto lc { what.find_last_of(':') }; lc != std::string_view::npos) + if (const auto lc { what.find_last_of(':') }; lc != std::string_view::npos) return what.substr(lc + 1); } return what; @@ -253,6 +253,14 @@ class conjure_enum return what; } + static constexpr bool has_scope(std::string_view what) noexcept + { + if constexpr (is_scoped()) + return enum_contains(what); + else + return false; + } + static constexpr auto cbegin() noexcept { return enum_entries.cbegin(); } static constexpr auto cend() noexcept { return enum_entries.cend(); } static constexpr auto crbegin() noexcept { return enum_entries.crbegin(); } @@ -277,11 +285,11 @@ class conjure_enum const auto result { std::equal_range(enum_entries_sorted.cbegin(), enum_entries_sorted.cend(), enum_tuple(T{}, str), tuple_comp_rev) }; return result.first != result.second; } - static constexpr std::string_view enum_to_string(T value) noexcept + static constexpr std::string_view enum_to_string(T value, bool noscope=false) noexcept { if (const auto result { std::equal_range(enum_entries.cbegin(), enum_entries.cend(), enum_tuple(value, std::string_view()), tuple_comp) }; result.first != result.second) - return std::get(*result.first); + return noscope ? remove_scope(std::get(*result.first)) : std::get(*result.first); return {}; } static constexpr std::optional string_to_enum(std::string_view str) noexcept @@ -310,7 +318,7 @@ class conjure_enum //----------------------------------------------------------------------------------------- // bitset based on supplied enum -// Note: the enum sequence must be contiguous with the last enum value < count of enumerations +// Note: the enum sequence must be continuous with the last enum value < count of enumerations //----------------------------------------------------------------------------------------- template::count()> requires (std::is_enum_v && countof > 0 && static_cast(conjure_enum::enum_values.back()) < countof) @@ -318,102 +326,120 @@ class enum_bitset { using U = std::underlying_type_t; - static constexpr bool scoped { !std::is_same_v }; template - static constexpr U as_U() noexcept { return static_cast(val); } - - static constexpr U as_U(T val) noexcept { return static_cast(val); } - static constexpr U all_bits {(1 << countof) - 1}; + static constexpr U to_underlying() noexcept { return static_cast(val); } // C++23: upgrade to std::to_underlying + static constexpr U to_underlying(T val) noexcept { return static_cast(val); } + static constexpr U all_bits { (1 << countof) - 1 }; U _present{}; public: constexpr enum_bitset(U bits) noexcept : _present(bits) {} + constexpr enum_bitset(std::string_view from, bool anyscope=false, char sep='|', bool ignore_errors=true) + : _present(factory(from, anyscope, sep, ignore_errors)) {} + template - constexpr enum_bitset(E... comp) noexcept : _present((... | (1 << as_U(comp)))) {} + requires(std::is_enum_v && ...) + constexpr enum_bitset(E... comp) noexcept : _present((0u | ... | (1 << to_underlying(comp)))) {} + + template + requires(std::is_integral_v && ...) + constexpr enum_bitset(I... comp) noexcept : _present((0u | ... | (1 << comp))) {} + constexpr enum_bitset() = default; constexpr ~enum_bitset() = default; - constexpr std::size_t count() const noexcept { return std::popcount(static_cast>(_present)); } // upgrade to std::bitset when constexpr in c++23 + constexpr std::size_t count() const noexcept + { return std::popcount(static_cast>(_present)); } // C++23: upgrade to std::bitset when count is constexpr constexpr std::size_t size() const noexcept { return countof; } constexpr U to_ulong() const noexcept { return _present; } + constexpr unsigned long long to_ullong() const noexcept { return _present; } constexpr bool operator[](std::size_t pos) const noexcept { return test(pos); } constexpr bool operator[](T what) const noexcept { return test(what); } - template - static constexpr U bitsum(E... comp) noexcept { return (... | (1 << comp)); } - - template - static constexpr U bitsum() noexcept { return (... | (1 << comp)); } - constexpr void set(U pos) noexcept { _present |= (1 << pos); } - constexpr void set(T what) noexcept { set(as_U(what)); } + constexpr void set(T what) noexcept { set(to_underlying(what)); } constexpr void set() noexcept { _present = all_bits; } template constexpr void set() noexcept { - if constexpr (constexpr auto uu{as_U()}; uu < countof) + if constexpr (constexpr auto uu{to_underlying()}; uu < countof) _present |= (1 << uu); } template constexpr void set_all() noexcept { (set(),...); } template - constexpr void set(E... comp) noexcept { return (... | (set(comp))); } + constexpr void set_all(E... comp) noexcept { return (... | (set(comp))); } template constexpr void flip() noexcept { - if constexpr (constexpr auto uu{as_U()}; uu < countof) + if constexpr (constexpr auto uu{to_underlying()}; uu < countof) _present ^= (1 << uu); } constexpr void flip() noexcept { _present = ~_present & all_bits; } constexpr void flip(U pos) noexcept { _present ^= (1 << pos); } - constexpr void flip(T what) noexcept { flip(as_U(what)); } + constexpr void flip(T what) noexcept { flip(to_underlying(what)); } template constexpr void clear() noexcept { - if constexpr (constexpr auto uu{as_U()}; uu < countof) + if constexpr (constexpr auto uu{to_underlying()}; uu < countof) _present &= ~(1 << uu); } constexpr void clear() noexcept { _present = 0; } constexpr void clear(U pos) noexcept { _present &= ~(1 << pos); } - constexpr void clear(T what) noexcept { clear(as_U(what)); } + constexpr void clear(T what) noexcept { clear(to_underlying(what)); } template constexpr void clear_all() noexcept { (clear(),...); } + template + requires(std::is_integral_v && ...) + constexpr void clear_all(I...comp) noexcept { (clear(comp),...); }; constexpr bool test(U pos) const noexcept { return _present & (1 << pos); } - constexpr bool test(T what) const noexcept { return test(as_U(what)); } + constexpr bool test(T what) const noexcept { return test(to_underlying(what)); } constexpr bool test() const noexcept { return _present; } template constexpr bool test() const noexcept { - if constexpr (constexpr auto uu{as_U()}; uu < countof) + if constexpr (constexpr auto uu{to_underlying()}; uu < countof) return test(uu); } template - constexpr U test_any() const noexcept { return (... || test()); } + constexpr bool test_any() const noexcept { return (... || test()); } + template + requires(std::is_integral_v && ...) + constexpr bool test_any(I...comp) const noexcept { return (... || test(comp)); } + template + requires(std::is_enum_v && ...) + constexpr bool test_any(E...comp) const noexcept { return (... || test(comp)); } template - constexpr U test_all() const noexcept { return (... && test()); } - - constexpr U& operator<<=(std::size_t pos) noexcept { return _present <<= pos; } - constexpr U& operator>>=(std::size_t pos) noexcept { return _present >>= pos; } - constexpr U& operator&=(T other) noexcept { return _present &= (1 << as_U(other)); } - constexpr U& operator|=(T other) noexcept { return _present |= (1 << as_U(other)); } - constexpr U& operator^=(T other) noexcept { return _present ^= (1 << as_U(other)); } - constexpr U& operator&=(U other) noexcept { return _present &= other; } - constexpr U& operator|=(U other) noexcept { return _present |= other; } - constexpr U& operator^=(U other) noexcept { return _present ^= other; } - - constexpr U operator<<(int pos) const noexcept { return _present << pos; } - constexpr U operator>>(int pos) const noexcept { return _present >> pos; } - constexpr U operator&(T other) const noexcept { return _present & (1 << as_U(other)); } - constexpr U operator|(T other) const noexcept { return _present | (1 << as_U(other)); } - constexpr U operator^(T other) const noexcept { return _present ^ (1 << as_U(other)); } - constexpr U operator&(U other) const noexcept { return _present & other; } - constexpr U operator|(U other) const noexcept { return _present | other; } - constexpr U operator^(U other) const noexcept { return _present ^ other; } - constexpr U operator~() const noexcept { return ~_present & all_bits; } + constexpr bool test_all() const noexcept { return (... && test()); } + template + requires(std::is_integral_v && ...) + constexpr bool test_all(I...comp) const noexcept { return (... && test(comp)); } + template + requires(std::is_enum_v && ...) + constexpr bool test_all(E...comp) const noexcept { return (... && test(comp)); } + + constexpr enum_bitset& operator<<=(std::size_t pos) noexcept { _present <<= pos; return *this; } + constexpr enum_bitset& operator>>=(std::size_t pos) noexcept { _present >>= pos; return *this; } + constexpr enum_bitset& operator&=(T other) noexcept { _present &= (1 << to_underlying(other)); return *this; } + constexpr enum_bitset& operator|=(T other) noexcept { _present |= (1 << to_underlying(other)); return *this; } + constexpr enum_bitset& operator^=(T other) noexcept { _present ^= (1 << to_underlying(other)); return *this; } + constexpr enum_bitset& operator&=(U other) noexcept { _present &= other; return *this; } + constexpr enum_bitset& operator|=(U other) noexcept { _present |= other; return *this; } + constexpr enum_bitset& operator^=(U other) noexcept { _present ^= other; return *this; } + + constexpr enum_bitset operator<<(int pos) const noexcept { return enum_bitset(_present << pos); } + constexpr enum_bitset operator>>(int pos) const noexcept { return enum_bitset(_present >> pos); } + constexpr enum_bitset operator&(T other) const noexcept { return enum_bitset(_present & (1 << to_underlying(other))); } + constexpr enum_bitset operator|(T other) const noexcept { return enum_bitset(_present | (1 << to_underlying(other))); } + constexpr enum_bitset operator^(T other) const noexcept { return enum_bitset(_present ^ (1 << to_underlying(other))); } + constexpr enum_bitset operator&(U other) const noexcept { return enum_bitset(_present & other); } + constexpr enum_bitset operator|(U other) const noexcept { return enum_bitset(_present | other); } + constexpr enum_bitset operator^(U other) const noexcept { return enum_bitset(_present ^ other); } + constexpr enum_bitset operator~() const noexcept { return enum_bitset(~_present & all_bits); } constexpr operator bool() const noexcept { return count(); } @@ -427,17 +453,63 @@ class enum_bitset return std::bind(std::forward(func), std::placeholders::_1, std::forward(args)...); } + /// create a bitset from enum separated enum string + constexpr static U factory(std::string_view src, bool anyscope, char sep, bool ignore_errors) + { + enum_bitset result; + auto trim([](std::string_view src) noexcept ->std::string_view + { + const auto bg(src.find_first_not_of(" \t")); + return bg == std::string_view::npos ? src : src.substr(bg, src.find_last_not_of(" \t") - bg + 1); + }); + auto process([anyscope,&result](std::string_view src) noexcept ->bool + { + if (anyscope && !conjure_enum::has_scope(src)) + src = conjure_enum::add_scope(src); + if (auto ev { conjure_enum::string_to_enum(src) }; ev) + { + result |= *ev; + return true; + } + return false; + }); + for (std::string_view::size_type pos{}, fnd{};; pos = fnd + 1) + { + if ((fnd = src.find_first_of(sep, pos)) != std::string_view::npos) + { + if (auto srcp { trim(src.substr(pos, fnd - pos)) }; !process(trim(srcp)) && !ignore_errors) + throw std::invalid_argument(std::string(srcp).c_str()); + continue; + } + if (pos < src.size()) + if (auto srcp { trim(src.substr(pos, src.size() - pos)) }; !process(trim(srcp)) && !ignore_errors) + throw std::invalid_argument(std::string(srcp).c_str()); + break; + } + return result._present; + } + constexpr std::string to_string(char zero='0', char one='1') const noexcept { return std::bitset(_present).to_string(zero, one); } - friend std::ostream& operator<<(std::ostream& os, const enum_bitset& what) noexcept + friend constexpr std::ostream& operator<<(std::ostream& os, const enum_bitset& what) noexcept { return os << what.to_string(); } }; +template +constexpr enum_bitset operator&(const enum_bitset& lh, const enum_bitset& rh) noexcept + { return lh.operator&(rh.to_ulong()); } +template +constexpr enum_bitset operator|(const enum_bitset& lh, const enum_bitset& rh) noexcept + { return lh.operator|(rh.to_ulong()); } +template +constexpr enum_bitset operator^(const enum_bitset& lh, const enum_bitset& rh) noexcept + { return lh.operator^(rh.to_ulong()); } + //----------------------------------------------------------------------------------------- } // FIX8