From 533c9509ef77d0fedd8be47d16dc6310877e24cf Mon Sep 17 00:00:00 2001 From: Bela Schaum Date: Tue, 17 Jan 2023 15:59:37 +0100 Subject: [PATCH] add constexpr containers (#187) --- README.md | 37 + doc/reference.md | 366 +++++- example/CMakeLists.txt | 3 + example/example_containers_array.cpp | 61 + example/example_containers_bitset.cpp | 51 + example/example_containers_set.cpp | 55 + include/magic_enum.hpp | 11 +- include/magic_enum_containers.hpp | 1710 +++++++++++++++++++++++++ test/CMakeLists.txt | 4 + test/test_containers.cpp | 318 +++++ 10 files changed, 2609 insertions(+), 7 deletions(-) create mode 100644 example/example_containers_array.cpp create mode 100644 example/example_containers_bitset.cpp create mode 100644 example/example_containers_set.cpp create mode 100644 include/magic_enum_containers.hpp create mode 100644 test/test_containers.cpp diff --git a/README.md b/README.md index aca0c5e..d75c3ba 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ Header-only C++17 library provides static reflection for enums, work with any en * `underlying_type` improved UB-free "SFINAE-friendly" [underlying_type](https://en.cppreference.com/w/cpp/types/underlying_type). * `ostream_operators` ostream operators for enums. * `bitwise_operators` bitwise operators for enums. +* `containers::array` array container for enums. +* `containers::bitset` bitset container for enums. +* `containers::set` set container for enums. ## Documentation @@ -222,6 +225,40 @@ Header-only C++17 library provides static reflection for enums, work with any en // color_name -> "BLUE" ``` +* `containers::array` array container for enums. + + ```cpp + magic_enum::containers::array color_rgb_array {}; + color_rgb_array[Color::RED] = {255, 0, 0}; + color_rgb_array[Color::GREEN] = {0, 255, 0}; + color_rgb_array[Color::BLUE] = {0, 0, 255}; + std::get(color_rgb_array) // -> RGB{0, 0, 255} + ``` + +* `containers::bitset` bitset container for enums. + + ```cpp + constexpr magic_enum::containers::bitset color_bitset_red_green {Color::RED|Color::GREEN}; + bool all = color_bitset_red_green.all(); + // all -> false + // Color::BLUE is missing + bool test = color_bitset_red_green.test(Color::RED); + // test -> true + ``` + +* `containers::set` set container for enums. + + ```cpp + auto color_set = magic_enum::containers::set(); + bool empty = color_set.empty(); + // empty -> true + color_set.insert(Color::GREEN); + color_set.insert(Color::BLUE); + color_set.insert(Color::RED); + std::size_t size = color_set.size(); + // size -> 3 + ``` + ## Remarks * `magic_enum` does not pretend to be a silver bullet for reflection for enums, it was originally designed for small enum. diff --git a/doc/reference.md b/doc/reference.md index e90bd61..fada24f 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -20,6 +20,9 @@ * [`underlying_type` improved UB-free "SFINAE-friendly" underlying_type.](#underlying_type) * [`ostream_operators` ostream operators for enums.](#ostream_operators) * [`bitwise_operators` bitwise operators for enums.](#bitwise_operators) +* [`containers::array` array container for enums.](#containersarray) +* [`containers::bitset` bitset container for enums.](#containersbitset) +* [`containers::set` set container for enums.](#containersset) ## Synopsis @@ -325,7 +328,7 @@ constexpr string_view enum_type_name() noexcept; ```cpp Color color = Color::RED; auto type_name = magic_enum::enum_type_name(); - // color_name -> "Color" + // type_name -> "Color" ``` ## `enum_fuse` @@ -427,7 +430,7 @@ constexpr optional enum_flags_contains(string_view value, BinaryPredicate p) ```cpp auto directions_name = magic_enum::enum_flags_name(Directions::Up | Directions::Right); - // directions_name -> "Directions::Up | Directions::Right" + // directions_name -> "Directions::Up|Directions::Right" ``` @@ -562,3 +565,362 @@ constexpr E& operator^=(E& lhs, E rhs) noexcept; // Support operators: ~, |, &, ^, |=, &=, ^=. Flags flags = Flags::A | Flags::B & ~Flags::C; ``` + +## `containers::array` + +```cpp +template> +struct array { + + constexpr reference at(E pos); + + constexpr const_reference at(E pos) const; + + constexpr reference operator[](E pos) noexcept; + + constexpr const_reference operator[](E pos) const noexcept; + + constexpr reference front() noexcept; + + constexpr const_reference front() const noexcept; + + constexpr reference back() noexcept; + + constexpr const_reference back() const noexcept; + + constexpr pointer data() noexcept; + + constexpr const_pointer data() const noexcept; + + constexpr iterator begin() noexcept; + + constexpr const_iterator begin() const noexcept; + + constexpr const_iterator cbegin() const noexcept; + + constexpr iterator end() noexcept; + + constexpr const_iterator end() const noexcept; + + constexpr const_iterator cend() const noexcept; + + constexpr iterator rbegin() noexcept; + + constexpr const_iterator rbegin() const noexcept; + + constexpr const_iterator crbegin() const noexcept; + + constexpr iterator rend() noexcept; + + constexpr const_iterator rend() const noexcept; + + constexpr const_iterator crend() const noexcept; + + constexpr bool empty() const noexcept; + + constexpr size_type size() const noexcept; + + constexpr size_type max_size() const noexcept; + + constexpr void fill( const V& value ); + + constexpr void swap(array& other) noexcept(std::is_nothrow_swappable_v); + + friend constexpr bool operator==(const array& a1, const array& a2); + + friend constexpr bool operator!=(const array& a1, const array& a2); + + friend constexpr bool operator<(const array& a1, const array& a2); + + friend constexpr bool operator<=(const array& a1, const array& a2); + + friend constexpr bool operator>(const array& a1, const array& a2); + + friend constexpr bool operator>=(const array& a1, const array& a2); +} +``` + +* STL like array for all enums. + +* Examples + + ```cpp + constexpr magic_enum::containers::array color_rgb_array {{{{255, 0, 0}, {0, 255, 0}, {0, 0, 255}}}}; + ``` + + ```cpp + magic_enum::containers::array color_rgb_array {}; + color_rgb_array[Color::RED] = {255, 0, 0}; + color_rgb_array[Color::GREEN] = {0, 255, 0}; + color_rgb_array[Color::BLUE] = {0, 0, 255}; + std::get(color_rgb_array) // -> RGB{0, 0, 255} + ``` + +## `containers::bitset` + +```cpp +template> +class bitset { + + constexpr explicit bitset(detail::raw_access_t = raw_access) noexcept; + + constexpr explicit bitset(detail::raw_access_t, unsigned long long val); + + constexpr explicit bitset(detail::raw_access_t, + string_view sv, + string_view::size_type pos = 0, + string_view::size_type n = string_view::npos, + char zero = '0', + char one = '1'); + + constexpr explicit bitset(detail::raw_access_t, + const char* str, + std::size_t n = ~std::size_t{}, + char zero = '0', + char one = '1'); + + constexpr bitset(std::initializer_list starters); + + template + constexpr explicit bitset(std::enable_if_t, E> starter); + + template> + constexpr explicit bitset(string_view sv, + Cmp&& cmp = {}, + char sep = '|'); + + friend constexpr bool operator==( const bitset& lhs, const bitset& rhs ) noexcept; + + friend constexpr bool operator!=( const bitset& lhs, const bitset& rhs ) noexcept; + + constexpr bool operator[](E pos) const noexcept; + + constexpr reference operator[](E pos) noexcept; + + constexpr bool test(E pos) const; + + constexpr bool all() const noexcept; + + constexpr bool any() const noexcept; + + constexpr bool none() const noexcept; + + constexpr std::size_t count() const noexcept; + + constexpr std::size_t size() const noexcept; + + constexpr std::size_t max_size() const noexcept; + + constexpr bitset& operator&= (const bitset& other) noexcept; + + constexpr bitset& operator|= (const bitset& other) noexcept; + + constexpr bitset& operator^= (const bitset& other) noexcept; + + constexpr bitset operator~() const noexcept; + + constexpr bitset& set() noexcept; + + constexpr bitset& set(E pos, bool value = true); + + constexpr bitset& reset() noexcept; + + constexpr bitset& reset(E pos); + + constexpr bitset& flip() noexcept; + + friend constexpr bitset operator&(const bitset& lhs, const bitset& rhs) noexcept; + + friend constexpr bitset operator|(const bitset& lhs, const bitset& rhs) noexcept; + + friend constexpr bitset operator^(const bitset& lhs, const bitset& rhs) noexcept; + + template + constexpr explicit operator std::enable_if_t, E>() const; + + string to_string(char sep = '|') const; + + string to_string(detail::raw_access_t, + char zero = '0', + char one = '1') const; + + constexpr unsigned long long to_ullong(detail::raw_access_t raw) const; + + constexpr unsigned long long to_ulong(detail::raw_access_t raw) const; + + friend std::ostream& operator<<(std::ostream& o, const bitset& bs); + + friend std::istream& operator>>(std::istream& i, bitset& bs); +} +``` + +* STL like bitset for all enums. + +* Examples + + ```cpp + constexpr magic_enum::containers::bitset color_bitset_red_green {Color::RED|Color::GREEN}; + bool all = color_bitset_red_green.all(); + // all -> false + // Color::BLUE is missing + bool test = color_bitset_red_green.test(Color::RED); + // test -> true + ``` + + ```cpp + auto color_bitset = magic_enum::containers::bitset(); + color_bitset.set(Color::GREEN); + color_bitset.set(Color::BLUE); + std::string to_string = color_bitset.to_string(); + // to_string -> "GREEN|BLUE" + ``` + +## `containers::set` + +```cpp +template> +class set { + + constexpr set() noexcept = default; + + template + constexpr set(InputIt first, InputIt last); + + constexpr set(std::initializer_list ilist); + + template + constexpr explicit set(std::enable_if_t, E> starter); + + constexpr set(const set&) noexcept = default; + + constexpr set(set&&) noexcept = default; + + constexpr set& operator=(const set&) noexcept = default; + + constexpr set& operator=(set&&) noexcept = default; + + constexpr set& operator=(std::initializer_list ilist); + + constexpr const_iterator begin() const noexcept; + + constexpr const_iterator end() const noexcept; + + constexpr const_iterator cbegin() const noexcept; + + constexpr const_iterator cend() const noexcept; + + constexpr const_reverse_iterator rbegin() const noexcept; + + constexpr const_reverse_iterator rend() const noexcept; + + constexpr const_reverse_iterator crbegin() const noexcept; + + constexpr const_reverse_iterator crend() const noexcept; + + constexpr bool empty() const noexcept; + + constexpr size_type size() const noexcept; + + constexpr size_type max_size() const noexcept; + + constexpr void clear() noexcept; + + constexpr std::pair insert(const value_type& value) noexcept; + + constexpr std::pair insert(value_type&& value) noexcept; + + constexpr iterator insert(const_iterator, const value_type& value) noexcept; + + constexpr iterator insert(const_iterator hint, value_type&& value) noexcept; + + template< class InputIt > + constexpr void insert(InputIt first, InputIt last) noexcept; + + constexpr void insert(std::initializer_list ilist) noexcept; + + template + constexpr std::pair emplace(Args&&... args) noexcept; + + template + constexpr iterator emplace_hint(const_iterator, Args&&... args) noexcept; + + constexpr iterator erase(const_iterator pos) noexcept; + + constexpr iterator erase(const_iterator first, const_iterator last) noexcept; + + constexpr size_type erase(const key_type& key) noexcept; + + template + constexpr std::enable_if_t, size_type> erase(K&& x) noexcept; + + void swap(set& other) noexcept; + + constexpr size_type count(const key_type& key) const noexcept; + + template + constexpr std::enable_if_t, size_type> count(const K& x) const; + + constexpr const_iterator find(const key_type & key) const noexcept; + + template + constexpr std::enable_if_t, const_iterator> find(const K& x) const; + + constexpr bool contains(const key_type& key) const noexcept; + + template + constexpr std::enable_if_t, bool> contains(const K& x) const noexcept; + + constexpr std::pair equal_range(const key_type& key) const noexcept; + + template + constexpr std::enable_if_t, std::pair> equal_range(const K& x) const noexcept; + + constexpr const_iterator lower_bound(const key_type& key) const noexcept; + + template + constexpr std::enable_if_t, const_iterator> lower_bound(const K& x) const noexcept; + + constexpr const_iterator upper_bound(const key_type& key) const noexcept; + + template + constexpr std::enable_if_t, const_iterator> upper_bound(const K& x) const noexcept; + + constexpr key_compare key_comp() const; + + constexpr value_compare value_comp() const; + + constexpr friend bool operator==(const set& lhs, const set& rhs) noexcept; + + constexpr friend bool operator!=(const set& lhs, const set& rhs) noexcept; + + constexpr friend bool operator<(const set& lhs, const set& rhs) noexcept; + + constexpr friend bool operator<=(const set& lhs, const set& rhs) noexcept; + + constexpr friend bool operator>(const set& lhs, const set& rhs) noexcept; + + constexpr friend bool operator>=(const set& lhs, const set& rhs) noexcept; + + template + size_type erase_if(Pred pred); +} +``` + +* STL like set for all enums. + +* Examples + + ```cpp + constexpr magic_enum::containers::set color_set_filled = {Color::RED, Color::GREEN, Color::BLUE}; + ``` + + ```cpp + auto color_set = magic_enum::containers::set(); + bool empty = color_set.empty(); + // empty -> true + color_set.insert(Color::GREEN); + color_set.insert(Color::BLUE); + color_set.insert(Color::RED); + std::size_t size = color_set.size(); + // size -> 3 + ``` diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index e0d4979..03c880d 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -19,6 +19,9 @@ endfunction() make_example(example) make_example(enum_flag_example) +make_example(example_containers_array) +make_example(example_containers_bitset) +make_example(example_containers_set) make_example(example_custom_name) make_example(example_switch) if(MAGIC_ENUM_OPT_ENABLE_NONASCII) diff --git a/example/example_containers_array.cpp b/example/example_containers_array.cpp new file mode 100644 index 0000000..c9c3c69 --- /dev/null +++ b/example/example_containers_array.cpp @@ -0,0 +1,61 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +#include + +enum class Color { RED = 1, GREEN = 2, BLUE = 4 }; + +struct RGB { + + std::uint8_t r {}; + std::uint8_t g {}; + std::uint8_t b {}; + + [[nodiscard]] constexpr bool empty() { return std::equal_to{}(r, g) && std::equal_to{}(g, b) && std::equal_to{}(b, 0); } + + [[nodiscard]] constexpr bool operator==(RGB rgb) const noexcept { return std::equal_to{}(r, rgb.r) && std::equal_to{}(g, rgb.g) && std::equal_to{}(b, rgb.b); } + + friend std::ostream& operator<<(std::ostream& ostream, RGB rgb) { + + ostream << "R=" << static_cast(rgb.r) << " G=" << static_cast(rgb.g) << " B=" << static_cast(rgb.b); + return ostream; + } +}; + +constexpr std::uint8_t color_max = std::numeric_limits::max(); + +int main() { + + constexpr magic_enum::containers::array color_rgb_initializer {{{{color_max, 0, 0}, {0, color_max, 0}, {0, 0, color_max}}}}; + + std::cout << std::get<0>(color_rgb_initializer) << std::endl; // R=255 G=0 B=0 + std::cout << std::get<1>(color_rgb_initializer) << std::endl; // R=0 G=255 B=0 + std::cout << std::get<2>(color_rgb_initializer) << std::endl; // R=0 G=0 B=255 + + std::cout << std::get(color_rgb_initializer) << std::endl; // R=255 G=0 B=0 + std::cout << std::get(color_rgb_initializer) << std::endl; // R=0 G=255 B=0 + std::cout << std::get(color_rgb_initializer) << std::endl; // R=0 G=0 B=255 + + return 0; +} diff --git a/example/example_containers_bitset.cpp b/example/example_containers_bitset.cpp new file mode 100644 index 0000000..83ee71e --- /dev/null +++ b/example/example_containers_bitset.cpp @@ -0,0 +1,51 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4244) // warning C4244: 'argument': conversion from 'const T' to 'unsigned int', possible loss of data. +#endif + +#include + +#include + +enum class Color { RED = 1, GREEN = 2, BLUE = 4 }; + +int main() { + + auto color_bitset = magic_enum::containers::bitset(); + color_bitset.set(Color::GREEN); + color_bitset.set(Color::BLUE); + + std::cout << std::boolalpha; + std::cout << color_bitset.size() << std::endl; // 3 == magic_enum::enum_count() + std::cout << color_bitset.all() << std::endl; // false + std::cout << color_bitset.any() << std::endl; // true + std::cout << color_bitset.none() << std::endl; // false + std::cout << color_bitset.count() << std::endl; // 2 + std::cout << color_bitset.test(Color::RED) << std::endl; // false + std::cout << color_bitset.test(Color::GREEN) << std::endl; // true + std::cout << color_bitset.test(Color::BLUE) << std::endl; // true + + return 0; +} diff --git a/example/example_containers_set.cpp b/example/example_containers_set.cpp new file mode 100644 index 0000000..e80291b --- /dev/null +++ b/example/example_containers_set.cpp @@ -0,0 +1,55 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4244) // warning C4244: 'argument': conversion from 'const T' to 'unsigned int', possible loss of data. +#endif + +#ifdef _WIN32 +#define _ITERATOR_DEBUG_LEVEL 0 +#endif + +#include + +#include + +enum class Color { RED = 1, GREEN = 2, BLUE = 4 }; + +int main() { + + std::cout << std::boolalpha; + magic_enum::containers::set color_set {Color::RED, Color::GREEN, Color::BLUE}; + std::cout << color_set.empty() << std::endl; // false + std::cout << color_set.size() << std::endl; // 3 + + color_set.clear(); + std::cout << color_set.empty() << std::endl; // true + std::cout << color_set.size() << std::endl; // 0 + + color_set.insert(Color::GREEN); + color_set.insert(Color::BLUE); + std::cout << color_set.empty() << std::endl; // false + std::cout << color_set.size() << std::endl; // 2 + + return 0; +} diff --git a/include/magic_enum.hpp b/include/magic_enum.hpp index c41f29b..6e223de 100644 --- a/include/magic_enum.hpp +++ b/include/magic_enum.hpp @@ -38,8 +38,8 @@ #include #include -#include #include +#include #include #include #include @@ -281,6 +281,7 @@ constexpr string_view pretty_name(string_view name) noexcept { return {}; // Invalid name. } +template> class case_insensitive { static constexpr char to_lower(char c) noexcept { return (c >= 'A' && c <= 'Z') ? static_cast(c + ('a' - 'A')) : c; @@ -293,7 +294,7 @@ class case_insensitive { static_assert(always_false_v, "magic_enum::case_insensitive not supported Non-ASCII feature."); return false; #else - return to_lower(lhs) == to_lower(rhs); + return Op{}(to_lower(lhs), to_lower(rhs)); #endif } }; @@ -1107,7 +1108,7 @@ template // Obtains index in enum values from enum value. // Returns optional with index. template -[[nodiscard]] constexpr auto enum_index(E value) noexcept -> detail::enable_if_t> { +[[nodiscard]] constexpr auto enum_index([[maybe_unused]] E value) noexcept -> detail::enable_if_t> { using D = std::decay_t; using U = underlying_type_t; @@ -1216,7 +1217,7 @@ template // Returns name from enum-flags value. // If enum-flags value does not have name or value out of range, returns empty string. template -[[nodiscard]] auto enum_flags_name(E value) -> detail::enable_if_t { +[[nodiscard]] auto enum_flags_name(E value, [[maybe_unused]] char sep = '|') -> detail::enable_if_t { using D = std::decay_t; static_assert(detail::is_flags_v, "magic_enum::enum_flags_name requires enum-flags type."); @@ -1236,7 +1237,7 @@ template } // Allows you to write magic_enum::enum_cast("bar", magic_enum::case_insensitive); -inline constexpr auto case_insensitive = detail::case_insensitive{}; +inline constexpr auto case_insensitive = detail::case_insensitive<>{}; // Obtains enum value from integer value. // Returns optional with enum value. diff --git a/include/magic_enum_containers.hpp b/include/magic_enum_containers.hpp new file mode 100644 index 0000000..75715f8 --- /dev/null +++ b/include/magic_enum_containers.hpp @@ -0,0 +1,1710 @@ +// __ __ _ ______ _____ +// | \/ | (_) | ____| / ____|_ _ +// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ +// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| +// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| +// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| +// __/ | https://github.com/Neargye/magic_enum +// |___/ version 0.8.2 +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef NEARGYE_MAGIC_ENUM_CONTAINERS_HPP +#define NEARGYE_MAGIC_ENUM_CONTAINERS_HPP + +#include "magic_enum.hpp" + +#include + +namespace magic_enum::containers { + +namespace detail { + template + [[maybe_unused]] constexpr static bool is_transparent_v {}; + + template + constexpr static bool is_transparent_v> {true}; + + template, typename T1, typename T2> + constexpr bool equal(T1&& t1, T2&& t2, Eq&& eq = {}) { + auto first1 = t1.begin(); + auto last1 = t1.end(); + auto first2 = t2.begin(); + auto last2 = t2.end(); + + for (; first1 != last1; ++first1, ++first2) { + if (first2 == last2 || !eq(*first1, *first2)) { + return false; + } + } + return first2 == last2; + } + + template, typename T1, typename T2> + constexpr bool lexicographical_compare(T1&& t1, T2&& t2, Cmp&& cmp = {}) noexcept { + auto first1 = t1.begin(); + auto last1 = t1.end(); + auto first2 = t2.begin(); + auto last2 = t2.end(); + + // copied from std::lexicographical_compare + for ( ; (first1 != last1) && (first2 != last2); ++first1, (void) ++first2 ) { + if (cmp(*first1, *first2)) { return true; } + if (cmp(*first2, *first1)) { return false; } + } + return (first1 == last1) && (first2 != last2); + } + + template< class T > + constexpr std::size_t popcount( T x ) noexcept { + std::size_t c = 0; + while (x > 0) { + c += x & 1; + x >>= 1; + } + return c; + } + + template, typename ForwardIt, typename E> + constexpr ForwardIt lower_bound(ForwardIt first, ForwardIt last, E&& e, Cmp&& comp = {}) { + auto count = std::distance(first, last); + + for (auto it = first; count > 0;) { + auto step = count / 2; + std::advance(it, step); + if (comp(*it, e)) { + first = ++it; + count -= step + 1; + } + else { + count = step; + } + } + return first; + } + + template, typename BidirIt, typename E> + constexpr auto equal_range(BidirIt begin, BidirIt end, E&& e, Cmp&& comp = {}) { + const auto first = lower_bound(begin, end, e, comp); + return std::pair{first, lower_bound(std::make_reverse_iterator(end), std::make_reverse_iterator(first), e, [&comp] (auto&& lhs, auto&& rhs) { + return comp(rhs, lhs); + }).base()}; + } + + template, typename = void> + struct indexing { + [[nodiscard]] constexpr static auto get_indices() noexcept { + // reverse result index mapping + std::array()> rev_res{}; + + // std::iota + for (std::size_t i = 0; i < enum_count(); ++i) { + rev_res[i] = i; + } + + constexpr auto orig_values = enum_values(); + constexpr Cmp cmp{}; + + // ~std::sort + for (std::size_t i = 0; i < enum_count(); ++i) { + for (std::size_t j = i+1; j < enum_count(); ++j) { + if (cmp(orig_values[rev_res[j]], orig_values[rev_res[i]])) { + auto tmp = rev_res[i]; + rev_res[i] = rev_res[j]; + rev_res[j] = tmp; + } + } + } + + std::array()> sorted_values{}; + // reverse the sorted indices + std::array()> res{}; + for (std::size_t i = 0; i < enum_count(); ++i) { + res[rev_res[i]] = i; + sorted_values[i] = orig_values[rev_res[i]]; + } + + return std::pair{sorted_values, res}; + } + + constexpr static inline std::array()> values = get_indices().first; + constexpr static inline const std::array()>* values_v = &values; + constexpr static inline std::array()> reindex = get_indices().second; + + [[nodiscard]] constexpr inline optional operator()(E val) const noexcept { + if (auto opt = enum_index(val)) { + return reindex[*opt]; + } + return {}; + } + }; + + template + struct indexing> && + (std::is_same_v> || std::is_same_v>)>> { + constexpr static inline const std::array()>* values_v = &enum_values(); + [[nodiscard]] constexpr inline optional operator()(E val) const noexcept { + return enum_index(val); + } + }; + + template + struct indexing { + using is_transparent = std::true_type; + template + [[nodiscard]] constexpr inline optional operator()(E val) const noexcept { + constexpr indexing ix{}; + return ix(val); + } + }; + + template, + typename = void> + struct name_sort_impl { + [[nodiscard]] constexpr inline bool operator()(E e1, E e2) const noexcept { + return OP{}(enum_name(e1), enum_name(e2)); + } + }; + + template + struct name_sort_impl { + using is_transparent = std::true_type; + template + struct FullCmp : S {}; + + template + struct FullCmp && + std::is_invocable_v>> { + [[nodiscard]] constexpr inline bool operator()(string_view s1, string_view s2) const noexcept { + return lexicographical_compare(s1, s2); + } + }; + + template + [[nodiscard]] constexpr inline std::enable_if_t< + // at least one of need to be an enum type + (std::is_enum_v> || std::is_enum_v>) && + // if both is enum, only accept if the same enum + (!std::is_enum_v> || !std::is_enum_v> || std::is_same_v) && + // is invocable with comparator + (std::is_invocable_r_v, + std::conditional_t>, string_view, E1>, + std::conditional_t>, string_view, E2>>), bool> operator()(E1 e1, E2 e2) const noexcept { + using D1 = std::decay_t; + using D2 = std::decay_t; + constexpr FullCmp<> cmp{}; + + if constexpr (std::is_enum_v && std::is_enum_v) { + return cmp(enum_name(e1), enum_name(e2)); + } else if constexpr (std::is_enum_v) { + return cmp(enum_name(e1), e2); + } else /* if constexpr (std::is_enum_v) */ { + return cmp(e1, enum_name(e2)); + } + } + }; + + struct raw_access_t {}; + + template + struct FilteredIterator { + Parent parent; + Iterator first; + Iterator last; + Iterator current; + Getter getter; + Predicate predicate; + + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::remove_reference_t>; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + constexpr FilteredIterator() noexcept = default; + constexpr FilteredIterator(const FilteredIterator&) = default; + constexpr FilteredIterator& operator=(const FilteredIterator&) = default; + constexpr FilteredIterator(FilteredIterator&&) noexcept = default; + constexpr FilteredIterator& operator=(FilteredIterator&&) noexcept = default; + + template && std::is_convertible_v>*> + constexpr explicit FilteredIterator(const FilteredIterator& other) + : parent(other.parent) + , first(other.first) + , last(other.last) + , current(other.current) + , getter(other.getter) + , predicate(other.predicate) + {} + + ~FilteredIterator() = default; + + constexpr FilteredIterator(Parent p, Iterator begin, Iterator end, Iterator curr, Getter getter = {}, Predicate pred = {}) + : parent(p) + , first(std::move(begin)) + , last(std::move(end)) + , current(std::move(curr)) + , getter{std::move(getter)} + , predicate{std::move(pred)} + { + if (current == first && !predicate(parent, current)) { + ++*this; + } + } + + [[nodiscard]] constexpr reference operator*() const { + return getter(parent, current); + } + + [[nodiscard]] constexpr pointer operator->() const { + return std::addressof(**this); + } + + constexpr FilteredIterator& operator++() { + do { + ++current; + } while(current != last && !predicate(parent, current)); + return *this; + } + + [[nodiscard]] constexpr FilteredIterator operator++(int) { + FilteredIterator cp = *this; + ++*this; + return cp; + } + + constexpr FilteredIterator& operator--() { + do { + --current; + } while(current != first && !predicate(parent, current)); + return *this; + } + + [[nodiscard]] constexpr FilteredIterator operator--(int) { + FilteredIterator cp = *this; + --*this; + return cp; + } + + [[nodiscard]] friend constexpr bool operator==(const FilteredIterator& lhs, const FilteredIterator& rhs) { + return lhs.current == rhs.current; + } + + [[nodiscard]] friend constexpr bool operator!=(const FilteredIterator& lhs, const FilteredIterator& rhs) { + return lhs.current != rhs.current; + } + }; +} // detail + +template +using name_less [[maybe_unused]] = detail::name_sort_impl; +template +using name_greater [[maybe_unused]] = detail::name_sort_impl>; + +using name_less_ci [[maybe_unused]] = detail::name_sort_impl>>; +using name_greater_ci [[maybe_unused]] = detail::name_sort_impl>>; + +template +using default_indexing = detail::indexing; + +template> +using comparator_indexing [[maybe_unused]] = detail::indexing; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ARRAY // +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template> +struct array { + static_assert(std::is_enum_v); + static_assert(std::is_trivially_constructible_v); + static_assert(enum_count() == 0 || Index{}(enum_values().front()) ); // check Index is constexpr + + using index_type = Index; + using container_type = std::array()>; + + using value_type = typename container_type::value_type; + using size_type = typename container_type::size_type; + using difference_type = typename container_type::difference_type; + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + using pointer = typename container_type::pointer; + using const_pointer = typename container_type::const_pointer; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using reverse_iterator = typename container_type::reverse_iterator; + using const_reverse_iterator = typename container_type::const_reverse_iterator; + + constexpr reference at(E pos) { + if (auto index = index_type{}(pos)) { + return a[*index]; + } + throw std::out_of_range("enum array::at: unrecognized position"); + } + + constexpr const_reference at(E pos) const { + if (auto index = index_type{}(pos)) { + return a[*index]; + } + throw std::out_of_range("enum array::at: unrecognized position"); + } + + [[nodiscard]] constexpr reference operator[](E pos) noexcept { + return a[*index_type{}(pos)]; + } + + [[nodiscard]] constexpr const_reference operator[](E pos) const noexcept { + return a[*index_type{}(pos)]; + } + + [[nodiscard]] constexpr reference front() noexcept { + return a.front(); + } + + [[nodiscard]] constexpr const_reference front() const noexcept { + return a.front(); + } + + [[nodiscard]] constexpr reference back() noexcept { + return a.back(); + } + + [[nodiscard]] constexpr const_reference back() const noexcept { + return a.back(); + } + + [[nodiscard]] constexpr pointer data() noexcept { + return a.data(); + } + + [[nodiscard]] constexpr const_pointer data() const noexcept { + return a.data(); + } + + [[nodiscard]] constexpr iterator begin() noexcept { + return a.begin(); + } + + [[nodiscard]] constexpr const_iterator begin() const noexcept { + return a.begin(); + } + + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { + return a.cbegin(); + } + + [[nodiscard]] constexpr iterator end() noexcept { + return a.end(); + } + + [[nodiscard]] constexpr const_iterator end() const noexcept { + return a.end(); + } + + [[nodiscard]] constexpr const_iterator cend() const noexcept { + return a.cend(); + } + + [[nodiscard]] constexpr iterator rbegin() noexcept { + return a.rbegin(); + } + + [[nodiscard]] constexpr const_iterator rbegin() const noexcept { + return a.rbegin(); + } + + [[nodiscard]] constexpr const_iterator crbegin() const noexcept { + return a.crbegin(); + } + + [[nodiscard]] constexpr iterator rend() noexcept { + return a.rend(); + } + + [[nodiscard]] constexpr const_iterator rend() const noexcept { + return a.rend(); + } + + [[nodiscard]] constexpr const_iterator crend() const noexcept { + return a.crend(); + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return a.empty(); + } + + [[nodiscard]] constexpr size_type size() const noexcept { + return a.size(); + } + + [[nodiscard]] constexpr size_type max_size() const noexcept { + return a.max_size(); + } + + constexpr void fill( const V& value ) { + for (auto& v : a) { + v = value; + } + } + + constexpr void swap(array& other) noexcept(std::is_nothrow_swappable_v) { + for (std::size_t i{}; i < a.size(); ++i) { + auto v = std::move(other.a[i]); + other.a[i] = std::move(a[i]); + a[i] = std::move(v); + } + } + + [[nodiscard]] friend constexpr bool operator==(const array& a1, const array& a2) { + return detail::equal(a1, a2); + } + + [[nodiscard]] friend constexpr bool operator!=(const array& a1, const array& a2) { + return !detail::equal(a1, a2); + } + + [[nodiscard]] friend constexpr bool operator<(const array& a1, const array& a2) { + return detail::lexicographical_compare(a1, a2); + } + + [[nodiscard]] friend constexpr bool operator<=(const array& a1, const array& a2) { + return !detail::lexicographical_compare(a2, a1); + } + + [[nodiscard]] friend constexpr bool operator>(const array& a1, const array& a2) { + return detail::lexicographical_compare(a2, a1); + } + + [[nodiscard]] friend constexpr bool operator>=(const array& a1, const array& a2) { + return !detail::lexicographical_compare(a1, a2); + } + + container_type a; +}; + +namespace detail { + + template + constexpr array> to_array_impl(T (&a)[N], std::index_sequence) { + return {{a[I]...}}; + } + + template + constexpr array> to_array_impl(T (&&a)[N], std::index_sequence) { + return {{std::move(a[I])...}}; + } +} + +template +constexpr std::enable_if_t<(enum_count() == N), array>> to_array(T (&a)[N]) { + return detail::to_array_impl(a, std::make_index_sequence{}); +} + +template +constexpr std::enable_if_t<(enum_count() == N), array>> to_array(T (&&a)[N]) { + return detail::to_array_impl(std::move(a), std::make_index_sequence{}); +} + +template +constexpr std::enable_if_t<(enum_count() == sizeof...(Ts)), array>>> make_array(Ts&& ... ts) { + return {{std::forward(ts)...}}; +} + +inline constexpr detail::raw_access_t raw_access {}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BITSET // +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template> +class bitset { + static_assert(std::is_enum_v); + static_assert(std::is_trivially_constructible_v); + static_assert(enum_count() == 0 || Index{}(enum_values().front()) ); // check Index is constexpr + + using base_type = std::conditional_t() <= 8, std::uint_least8_t, + std::conditional_t() <= 16, std::uint_least16_t, + std::conditional_t() <= 32, std::uint_least32_t, + std::uint_least64_t>>>; + constexpr static std::size_t bits_per_base = sizeof(base_type) * 8; + constexpr static std::size_t base_type_count = (enum_count() > 0 ? (enum_count() - 1) / bits_per_base + 1 : 0); + constexpr static std::size_t not_interested = base_type_count * bits_per_base - enum_count(); + constexpr static base_type last_value_max = (base_type{1} << (bits_per_base - not_interested)) - 1; + + template + class reference_impl { + friend class bitset; + + parent_t parent; + std::size_t num_index; + base_type bit_index; + + constexpr reference_impl(parent_t parent, std::size_t ix) noexcept + : reference_impl(parent, std::pair{ix / bits_per_base, base_type{1} << (ix % bits_per_base)}) + {} + + constexpr reference_impl(parent_t parent, std::pair ix) noexcept + : parent(parent) + , num_index(std::get<0>(ix)) + , bit_index(std::get<1>(ix)) + {} + + public: + constexpr reference_impl& operator=(bool v) noexcept { + if (v) { + parent->a[num_index] |= bit_index; + } else { + parent->a[num_index] &= ~bit_index; + } + return *this; + } + + constexpr reference_impl& operator=(const reference_impl& v) noexcept { + if (this == &v) { + return *this; + } + *this = static_cast(v); + return *this; + } + + [[nodiscard]] constexpr explicit operator bool() const noexcept { + return (parent->a[num_index] & bit_index) > 0; + } + + [[nodiscard]] constexpr bool operator~() const noexcept { + return !static_cast(*this); + } + + constexpr reference_impl& flip() noexcept { + *this = ~*this; + return *this; + } + }; + + template + [[nodiscard]] constexpr T to_(detail::raw_access_t) const { + T res{}; + T flag{1}; + for (std::size_t i{}; i < size(); ++i, flag <<= 1) { + if (const_reference{this, i}) { + if (i >= sizeof(T) * 8) { + throw std::overflow_error("cannot represent enum in this type"); + } + res |= flag; + } + } + return res; + } +public: + using index_type = Index; + using container_type = std::array; + using reference = reference_impl<>; + using const_reference = reference_impl; + + constexpr explicit bitset(detail::raw_access_t = raw_access) noexcept : a{{}} {} + + constexpr explicit bitset(detail::raw_access_t, unsigned long long val) : a{{}} { + unsigned long long bit{1}; + for (std::size_t i{}; i < (sizeof(val) * 8); ++i, bit <<= 1) { + if ((val & bit) > 0) { + if (i >= enum_count()) { + throw std::out_of_range("enum bitset::constructor: Upper bit set in raw number"); + } + + reference{this, i} = true; + } + } + } + + constexpr explicit bitset(detail::raw_access_t, + string_view sv, + string_view::size_type pos = 0, + string_view::size_type n = string_view::npos, + char zero = '0', + char one = '1') : a{{}} { + std::size_t i{}; + for (auto c : sv.substr(pos, n)) { + if (c == one) { + if (i >= enum_count()) { + throw std::out_of_range("enum bitset::constructor: Upper bit set in raw string"); + } + reference{this, i} = true; + } else if (c != zero) { + throw std::invalid_argument("enum bitset::constructor: unrecognized character in raw string"); + } + ++i; + } + } + + constexpr explicit bitset(detail::raw_access_t, + const char* str, + std::size_t n = ~std::size_t{}, + char zero = '0', + char one = '1') + : bitset(std::string_view{str, (std::min)(std::char_traits::length(str), n)}, + 0, n, zero, one) + {} + + constexpr bitset(std::initializer_list starters) : a{{}} { + if constexpr (magic_enum::detail::is_flags_v) { + for (auto& f : starters) { + *this |= bitset(f); + } + } else { + for (auto& f : starters) { + set(f); + } + } + } + + template + constexpr explicit bitset(std::enable_if_t, E> starter) : a{{}} { + auto u = enum_underlying(starter); + for (E v : enum_values()) { + if (auto ul = enum_underlying(v); (ul & u) != 0) { + u &= ~ul; + (*this)[v] = true; + } + } + if (u != 0) { + throw std::invalid_argument("enum bitset::constructor: unrecognized enum value in flag"); + } + } + + template> + constexpr explicit bitset(string_view sv, + Cmp&& cmp = {}, + char sep = '|') { + for (std::size_t to{}; (to = magic_enum::detail::find(sv, sep)) != string_view::npos; sv.remove_prefix(to+1)) { + if (auto v = magic_enum::enum_cast(sv.substr(0, to), cmp)) { + set(v); + } else { + throw std::invalid_argument("enum bitset::constructor: unrecognized enum value in string"); + } + } + if (!sv.empty()) { + if (auto v = magic_enum::enum_cast(sv, cmp)) { + set(v); + } else { + throw std::invalid_argument("enum bitset::constructor: unrecognized enum value in string"); + } + } + } + + [[nodiscard]] friend constexpr bool operator==( const bitset& lhs, const bitset& rhs ) noexcept { + return detail::equal(lhs.a, rhs.a); + } + + [[nodiscard]] friend constexpr bool operator!=( const bitset& lhs, const bitset& rhs ) noexcept { + return !detail::equal(lhs.a, rhs.a); + } + + [[nodiscard]] constexpr bool operator[](E pos) const noexcept { + return static_cast(const_reference(this, *index_type{}(pos))); + } + + [[nodiscard]] constexpr reference operator[](E pos) noexcept { + return reference{this, *index_type{}(pos)}; + } + + constexpr bool test(E pos) const { + if (auto ix = index_type{}(pos)) { + return static_cast(const_reference(this, *ix)); + } + throw std::out_of_range("enum bitset::test: unrecognized position"); + } + + [[nodiscard]] constexpr bool all() const noexcept { + if constexpr (base_type_count == 0) { + return true; + } + + for (std::size_t i{}; i < base_type_count - (not_interested > 0); ++i) { + auto check = ~a[i]; + if (check) { + return false; + } + } + + if constexpr (not_interested > 0) { + return a[base_type_count - 1] == last_value_max; + } + } + + [[nodiscard]] constexpr bool any() const noexcept { + for (auto& v : a) { + if (v > 0) { + return true; + } + } + return false; + } + + [[nodiscard]] constexpr bool none() const noexcept { + return !any(); + } + + [[nodiscard]] constexpr std::size_t count() const noexcept { + std::size_t c{}; + for (auto& v : a) { + c += detail::popcount(v); + } + return c; + } + + [[nodiscard]] constexpr std::size_t size() const noexcept { + return enum_count(); + } + + [[nodiscard]] constexpr std::size_t max_size() const noexcept { + return enum_count(); + } + + constexpr bitset& operator&= (const bitset& other) noexcept { + for (std::size_t i{}; i < base_type_count; ++i) { + a[i] &= other.a[i]; + } + return *this; + } + + constexpr bitset& operator|= (const bitset& other) noexcept { + for (std::size_t i{}; i < base_type_count; ++i) { + a[i] |= other.a[i]; + } + return *this; + } + + constexpr bitset& operator^= (const bitset& other) noexcept { + for (std::size_t i{}; i < base_type_count; ++i) { + a[i] ^= other.a[i]; + } + return *this; + } + + [[nodiscard]] constexpr bitset operator~() const noexcept { + bitset res; + for (std::size_t i{}; i < base_type_count - (not_interested > 0); ++i) { + res.a[i] = ~a[i]; + } + + if constexpr (not_interested > 0) { + res.a[base_type_count - 1] = ~a[base_type_count - 1] & last_value_max; + } + return res; + } + + constexpr bitset& set() noexcept { + for (std::size_t i{}; i < base_type_count - (not_interested > 0); ++i) { + a[i] = ~base_type{}; + } + + if constexpr (not_interested > 0) { + a[base_type_count - 1] = last_value_max; + } + return *this; + } + + constexpr bitset& set(E pos, bool value = true) { + if (auto ix = index_type{}(pos)) { + reference{this, *ix} = value; + return *this; + } + throw std::out_of_range("enum bitset::set: unrecognized position"); + } + + constexpr bitset& reset() noexcept { + return *this = bitset{}; + } + + constexpr bitset& reset(E pos) { + if (auto ix = index_type{}(pos)) { + reference{this, *ix} = false; + return *this; + } + throw std::out_of_range("enum bitset::reset: unrecognized position"); + } + + constexpr bitset& flip() noexcept { + return *this = ~*this; + } + + [[nodiscard]] friend constexpr bitset operator&(const bitset& lhs, const bitset& rhs) noexcept { + bitset cp = lhs; + cp &= rhs; + return cp; + } + + [[nodiscard]] friend constexpr bitset operator|(const bitset& lhs, const bitset& rhs) noexcept { + bitset cp = lhs; + cp |= rhs; + return cp; + } + + [[nodiscard]] friend constexpr bitset operator^(const bitset& lhs, const bitset& rhs) noexcept { + bitset cp = lhs; + cp ^= rhs; + return cp; + } + + template + [[nodiscard]] constexpr explicit operator std::enable_if_t, E>() const { + E res{}; + for (auto& e : enum_values()) { + if (test(e)) { + res |= e; + } + } + return res; + } + + [[nodiscard]] string to_string(char sep = '|') const { + // if constexpr (magic_enum::detail::is_flags_v) { + // return enum_flags_name(static_cast(*this), sep); + // } else { + string name; + + for (auto& e : enum_values()) { + if (test(e)) { + if (!name.empty()) { + name.append(1, sep); + } + auto n = enum_name(e); + name.append(n.data(), n.size()); + } + } + return name; + // } + } + + [[nodiscard]] string to_string(detail::raw_access_t, + char zero = '0', + char one = '1') const { + string name; + name.reserve(size()); + for (std::size_t i{}; i < size(); ++i) { + name.append(1, const_reference{this, i} ? one : zero); + } + return name; + } + + [[nodiscard]] constexpr unsigned long long to_ullong(detail::raw_access_t raw) const { + return to_(raw); + } + + [[nodiscard]] constexpr unsigned long long to_ulong(detail::raw_access_t raw) const { + return to_(raw); + } + + friend std::ostream& operator<<(std::ostream& o, const bitset& bs) { + return o << bs.to_string(); + } + + friend std::istream& operator>>(std::istream& i, bitset& bs) { + string s; + if (i >> s; !s.empty()) { + bs = bitset(string_view{s}); + } + return i; + } + +private: + container_type a; +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SET // +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template> +class set { + using index_type = detail::indexing; + struct Getter { + constexpr const E& operator()(const set*, const E* p) const noexcept { + return *p; + } + }; + struct Predicate { + constexpr bool operator()(const set* h, const E* e) const noexcept { + return h->a[*e]; + } + }; +public: + + using container_type = bitset; + using key_type = E; + using value_type = E; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using key_compare = CExprLess; + using value_compare = CExprLess; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = detail::FilteredIterator; + using const_iterator = detail::FilteredIterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr set() noexcept = default; + + template + constexpr set(InputIt first, InputIt last) { + while (first != last) { + insert(*first++); + } + } + + constexpr set(std::initializer_list ilist) { + for (auto e : ilist) { + insert(e); + } + } + + template + constexpr explicit set(std::enable_if_t, E> starter) { + auto u = enum_underlying(starter); + for (E v : enum_values()) { + if ((enum_underlying(v) & u) != 0) { + (*this)[v] = true; + } + } + } + + constexpr set(const set&) noexcept = default; + constexpr set(set&&) noexcept = default; + + constexpr set& operator=(const set&) noexcept = default; + constexpr set& operator=(set&&) noexcept = default; + constexpr set& operator=(std::initializer_list ilist) { + for (auto e : ilist) { + insert(e); + } + } + + constexpr const_iterator begin() const noexcept { + return const_iterator{this, &(*index_type::values_v->begin()), + &(*index_type::values_v->end()), + &(*index_type::values_v->begin())}; + } + + constexpr const_iterator end() const noexcept { + return const_iterator{this, &(*index_type::values_v->begin()), + &(*index_type::values_v->end()), + &(*index_type::values_v->end())}; + } + + constexpr const_iterator cbegin() const noexcept { + return begin(); + } + + constexpr const_iterator cend() const noexcept { + return end(); + } + + constexpr const_reverse_iterator rbegin() const noexcept { + return {end()}; + } + + constexpr const_reverse_iterator rend() const noexcept { + return {begin()}; + } + + constexpr const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + constexpr const_reverse_iterator crend() const noexcept { + return rend(); + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return s == 0; + } + + [[nodiscard]] constexpr size_type size() const noexcept { + return s; + } + + [[nodiscard]] constexpr size_type max_size() const noexcept { + return a.max_size(); + } + + constexpr void clear() noexcept { + a.reset(); + s = 0; + } + + constexpr std::pair insert(const value_type& value) noexcept { + if (auto i = index_type{}(value)) { + typename container_type::reference ref = a[value]; + bool r = !ref; + if (r) { + ref = true; + ++s; + } + + return {iterator{this, &(*index_type::values_v->begin()), + &(*index_type::values_v->end()), + &(*index_type::values_v)[*i]}, r}; + } + return {end(), false}; + } + + constexpr std::pair insert(value_type&& value) noexcept { + return insert(value); + } + + constexpr iterator insert(const_iterator, const value_type& value) noexcept { + return insert(value).first; + } + + constexpr iterator insert(const_iterator hint, value_type&& value) noexcept { + return insert(hint, value); + } + + template< class InputIt > + constexpr void insert(InputIt first, InputIt last) noexcept { + while (first != last) { + insert(*first++); + } + } + + constexpr void insert(std::initializer_list ilist) noexcept { + for (auto v : ilist) { + insert(v); + } + } + + template + constexpr std::pair emplace(Args&&... args) noexcept { + return insert({std::forward(args)...}); + } + + template + constexpr iterator emplace_hint(const_iterator, Args&&... args) noexcept { + return emplace(std::forward(args)...).first; + } + + constexpr iterator erase(const_iterator pos) noexcept { + erase(*pos++); + return pos; + } + + constexpr iterator erase(const_iterator first, const_iterator last) noexcept { + while((first = erase(first)) != last) { ; } + return first; + } + + constexpr size_type erase(const key_type& key) noexcept { + typename container_type::reference ref = a[key]; + bool res = ref; + if (res) { + --s; + } + ref = false; + return res; + } + + template + constexpr std::enable_if_t, size_type> erase(K&& x) noexcept { + size_type c{}; + for (auto [first, last] = detail::equal_range(index_type::values_v->begin(), index_type::values_v->end(), x, key_compare{}); + first != last; ) { + c += erase(*first++); + } + return c; + } + + void swap(set& other) noexcept { + set cp = *this; + *this = other; + other = cp; + } + + [[nodiscard]] constexpr size_type count(const key_type& key) const noexcept { + return index_type{}(key) && a[key]; + } + + template + [[nodiscard]] constexpr std::enable_if_t, size_type> count(const K& x) const { + size_type c{}; + for (auto [first, last] = detail::equal_range(index_type::values_v->begin(), index_type::values_v->end(), x, key_compare{}); first != last; ++first) { + c += count(*first); + } + return c; + } + + [[nodiscard]] constexpr const_iterator find(const key_type & key) const noexcept { + if (auto i = index_type{}(key); i && a.test(key)) + return const_iterator{this, index_type::values_v->begin(), + index_type::values_v->end(), + &(*index_type::values_v)[*i]}; + return end(); + } + + template + [[nodiscard]] constexpr std::enable_if_t, const_iterator> find(const K& x) const { + for (auto [first, last] = detail::equal_range(index_type::values_v->begin(), index_type::values_v->end(), x, key_compare{}); first != last; ++first) { + if (a.test(*first)) { + return find(*first); + } + } + return end(); + } + + [[nodiscard]] constexpr bool contains(const key_type& key) const noexcept { + return count(key); + } + + template + [[nodiscard]] constexpr std::enable_if_t, bool> contains(const K& x) const noexcept { + return count(x) > 0; + } + + [[nodiscard]] constexpr std::pair equal_range(const key_type& key) const noexcept { + return {lower_bound(key), upper_bound(key)}; + } + + template + [[nodiscard]] constexpr std::enable_if_t, std::pair> equal_range(const K& x) const noexcept { + return {lower_bound(x), upper_bound(x)}; + } + + [[nodiscard]] constexpr const_iterator lower_bound(const key_type& key) const noexcept { + if (auto i = index_type{}(key)) { + auto it = const_iterator{this, index_type::values_v->begin(), + index_type::values_v->end(), + &(*index_type::values_v)[*i]}; + return a.test(key) ? it : std::next(it); + } + return end(); + } + + template + [[nodiscard]] constexpr std::enable_if_t, const_iterator> lower_bound(const K& x) const noexcept { + auto [first, last] = detail::equal_range(index_type::values_v->begin(), index_type::values_v->end(), x, key_compare{}); + return first != last ? lower_bound(*first) : end(); + } + + [[nodiscard]] constexpr const_iterator upper_bound(const key_type& key) const noexcept { + if (auto i = index_type{}(key)) { + return std::next(const_iterator{this, index_type::values_v->begin(), + index_type::values_v->end(), + &(*index_type::values_v)[*i]}); + } + return end(); + } + + template + [[nodiscard]] constexpr std::enable_if_t, const_iterator> upper_bound(const K& x) const noexcept { + auto [first, last] = detail::equal_range(index_type::values_v->begin(), index_type::values_v->end(), x, key_compare{}); + return first != last ? upper_bound(*std::prev(last)) : end(); + } + + [[nodiscard]] constexpr key_compare key_comp() const { + return {}; + } + + [[nodiscard]] constexpr value_compare value_comp() const { + return {}; + } + + [[nodiscard]] constexpr friend bool operator==(const set& lhs, const set& rhs) noexcept { + return lhs.a == rhs.a; + } + + [[nodiscard]] constexpr friend bool operator!=(const set& lhs, const set& rhs) noexcept { + return lhs.a != rhs.a; + } + + [[nodiscard]] constexpr friend bool operator<(const set& lhs, const set& rhs) noexcept { + if (lhs.s < rhs.s) { return true; } + if (rhs.s < lhs.s) { return false; } + + for (auto& e : *index_type::values_v) { + if (auto c = rhs.contains(e); c != lhs.contains(e)) { + return c; + } + } + return false; + } + + [[nodiscard]] constexpr friend bool operator<=(const set& lhs, const set& rhs) noexcept { + return !(rhs < lhs); + } + + [[nodiscard]] constexpr friend bool operator>(const set& lhs, const set& rhs) noexcept { + return rhs < lhs; + } + + [[nodiscard]] constexpr friend bool operator>=(const set& lhs, const set& rhs) noexcept { + return !(lhs < rhs); + } + + template + size_type erase_if(Pred pred) { + auto old_size = size(); + for (auto i = begin(), last = end(); i != last; ) { + if (pred(*i)) { + i = erase(i); + } else { + ++i; + } + } + return old_size - size(); + } + //... + +private: + container_type a; + std::size_t s{}; +}; + +/* + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// FLATSET // +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +template> +class flat_set { + using index_type = detail::indexing; +public: + using container_type = std::array()>; + using key_type = E; + using value_type = E; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using key_compare = CExprLess; + using value_compare = CExprLess; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = const E*; + using const_iterator = const E*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr flat_set() noexcept : a{}, s{} {} + template + constexpr flat_set(InputIterator begin, InputIterator end) { + insert(begin, end); + } + constexpr flat_set(std::initializer_list il) { + insert(il); + } + + constexpr flat_set(const flat_set &) = default; + constexpr flat_set(flat_set&&) noexcept = default; + constexpr flat_set & operator=(const flat_set &) = default; + constexpr flat_set & operator=(flat_set &&) noexcept = default; + constexpr flat_set & operator=(std::initializer_list< value_type > il) { + return *this = flat_set{il}; + } + + [[nodiscard]] constexpr const_iterator begin() const noexcept { + return a.begin(); + } + + [[nodiscard]] constexpr const_iterator end() const noexcept { + return a.begin() + s; + } + + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { + return {end()}; + } + + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { + return {begin()}; + } + + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { + return begin(); + } + + [[nodiscard]] constexpr const_iterator cend() const noexcept { + return end(); + } + + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { + return rend(); + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return s == 0; + } + + [[nodiscard]] constexpr size_type size() const noexcept { + return s; + } + + [[nodiscard]] constexpr size_type max_size() const noexcept { + return a.max_size(); + } + + [[nodiscard]] constexpr size_type capacity() const noexcept { + return a.size(); + } + + template + constexpr std::pair< iterator, bool > emplace(Args &&... args) { + return insert(value_type{std::forward(args)...}); + } + + template + constexpr iterator emplace_hint(const_iterator, Args &&... args) { + return insert(value_type{std::forward(args)...}).first; + } + + constexpr std::pair< iterator, bool > insert(const value_type & v) { + auto it = lower_bound(v); + bool inserts = it == end() || *it != v; + if (inserts) { + auto nTh = it - begin(); + for (size_type cp = s; cp > static_cast(nTh); --cp) { + a[cp] = a[cp-1]; + } + a[nTh] = v; + ++s; + } + return {it, inserts}; + } + + constexpr std::pair< iterator, bool > insert(value_type&& v) { + return insert(v); + } + + template + constexpr void insert(InputIterator begin, InputIterator end) { + while(begin != end) { + insert(*begin++); + } + } + + constexpr void insert(std::initializer_list< value_type > il) { + for (auto e : il) { + insert(e); + } + } + + template + constexpr void merge(flat_set & other) { + for (auto e : other) { + insert(e); + } + } + + template + constexpr void merge(flat_set && other) { + merge(other); + } + + constexpr size_type erase(const key_type & key) { + auto it = lower_bound(key); + bool erases = it != end() && *it == key; + if (erases) { + erase(it); + } + return erases; + } + constexpr iterator erase(const_iterator it) { + if (it != end()) { + for (size_type from = it - begin(); from < s-1; ++from) { + a[from] = a[from+1]; + } + + --s; + } + return it; + } + + constexpr iterator erase(const_iterator first, const_iterator last) { + while ((first = erase(first)) != last) { ; } + return first; + } + + constexpr void swap(flat_set & fs) noexcept { + size_type until = (std::min)(s, fs.s); + for (size_type i{}; i < until; ++i) { + auto v = a[i]; + a[i] = fs.a[i]; + fs.a[i] = v; + } + for (size_type i = until; i < s; ++i) { + fs.a[i] = a[i]; + } + for (size_type i = until; i < fs.s; ++i) { + a[i] = fs.a[i]; + } + + until = s; + s = fs.s; + fs.s = until; + } + + constexpr void clear() noexcept { + s = 0; + } + + [[nodiscard]] constexpr key_compare key_comp() const { + return {}; + } + + [[nodiscard]] constexpr value_compare value_comp() const { + return {}; + } + + [[nodiscard]] constexpr const_iterator find(const key_type & k) const { + auto it = lower_bound(k); + if (it != end() && *it != k) { + it = end(); + } + return it; + } + + template + [[nodiscard]] constexpr std::enable_if_t, const_iterator> find(const K& x) const { + auto [first, last] = equal_range(x); + return first != last ? first : end(); + } + + [[nodiscard]] constexpr const_iterator nth(size_type n) const noexcept { + return a.begin() + n; + } + + [[nodiscard]] constexpr size_type index_of(const_iterator i) const noexcept { + return i - begin(); + } + + [[nodiscard]] constexpr size_type count(const key_type & k) const { + return find(k) != end(); + } + + template + [[nodiscard]] constexpr std::enable_if_t, size_type> count(const K& x) const { + auto [first, last] = equal_range(x); + return last - first; + } + + [[nodiscard]] constexpr bool contains(const key_type & key) const { + return count(key); + } + + template + [[nodiscard]] constexpr std::enable_if_t, bool> contains(const K& x) const { + auto [first, last] = equal_range(x); + return last - first > 0; + } + + [[nodiscard]] constexpr const_iterator lower_bound(const key_type & k) const { + return detail::lower_bound(begin(), end(), k, key_compare{}); + } + + template + [[nodiscard]] constexpr std::enable_if_t, const_iterator> lower_bound(const K& x) const { + return detail::lower_bound(begin(), end(), x, key_compare{}); + } + + [[nodiscard]] constexpr const_iterator upper_bound(const key_type & k) const { + return equal_range(k).second; + } + + template + [[nodiscard]] constexpr std::enable_if_t, const_iterator> upper_bound(const K& x) const { + return equal_range(x).second; + } + + [[nodiscard]] constexpr std::pair< const_iterator, const_iterator > equal_range(const key_type & k) const { + return detail::equal_range(begin(), end(), k, key_compare{}); + } + + template + [[nodiscard]] constexpr std::enable_if_t, std::pair< const_iterator, const_iterator >> equal_range(const K& x) const { + return detail::equal_range(begin(), end(), x, key_compare{}); + } + + [[nodiscard]] constexpr friend bool operator==(const flat_set& lhs, const flat_set& rhs) noexcept { + if (lhs.s != rhs.s) { return false; } + for (size_type i{}; i < lhs.s; ++i) { + if (lhs.a[i] != rhs.a[i]) { + return false; + } + } + return true; + } + + [[nodiscard]] constexpr friend bool operator!=(const flat_set& lhs, const flat_set& rhs) noexcept { + return lhs.a != rhs.a; + } + + [[nodiscard]] constexpr friend bool operator<(const flat_set& lhs, const flat_set& rhs) noexcept { + if (lhs.s < rhs.s) { return true; } + if (rhs.s < lhs.s) { return false; } + + for (auto& e : *index_type::values_v) { + if (auto c = rhs.contains(e); c != lhs.contains(e)) { + return c; + } + } + return false; + } + + [[nodiscard]] constexpr friend bool operator<=(const flat_set& lhs, const flat_set& rhs) noexcept { + return !(rhs < lhs); + } + + [[nodiscard]] constexpr friend bool operator>(const flat_set& lhs, const flat_set& rhs) noexcept { + return rhs < lhs; + } + + [[nodiscard]] constexpr friend bool operator>=(const flat_set& lhs, const flat_set& rhs) noexcept { + return !(lhs < rhs); + } + + constexpr friend void swap(flat_set & lhs, flat_set & rhs) noexcept { + lhs.swap(rhs); + } + + template + size_type erase_if(Pred pred) { + auto old_size = size(); + for (auto i = begin(), last = end(); i != last; ) { + if (pred(*i)) { + i = erase(i); + } else { + ++i; + } + } + return old_size - size(); + } +private: + container_type a; + std::size_t s; +}; + +*/ + +/* + +// multiset like API. (Probably delete can invalidate allocators?) +template> +class multiset { + using index_type = detail::indexing; +public: + + //... + +private: + array a; +}; + + +// map like API. +template> +class map { + using index_type = detail::indexing; +public: + + //... + +private: + array>, index_type> a; +}; + + +// flat_map (map) like API with contiguous iterator --> can be memcpy'd if V is trivially_copyable. +template> +class flat_map { +public: + + //... + +private: + union ValueType { + std::uint8_t uninitialized = {}; + std::pair value; + }; + array a; + std::size_t s; +}; +*/ + +}// namespace magic_enum::containers + +namespace std { + template< auto I, typename E, typename V, typename Index> + constexpr std::enable_if_t<(std::is_integral_v && + I < magic_enum::enum_count()), V&> get( magic_enum::containers::array& a ) noexcept { + return a.a[I]; + } + + template< auto I, typename E, typename V, typename Index> + constexpr std::enable_if_t<(std::is_integral_v && + I < magic_enum::enum_count()), V&&> get( magic_enum::containers::array&& a ) noexcept { + return std::move(a.a[I]); + } + + template< auto I, typename E, typename V, typename Index> + constexpr std::enable_if_t<(std::is_integral_v && + I < magic_enum::enum_count()), const V&> get( const magic_enum::containers::array& a ) noexcept { + return a.a[I]; + } + + template< auto I, typename E, typename V, typename Index> + constexpr std::enable_if_t<(std::is_integral_v && + I < magic_enum::enum_count()), const V&&> get( const magic_enum::containers::array&& a ) noexcept { + return std::move(a.a[I]); + } + + template< auto Enum, typename E, typename V, typename Index> + constexpr std::enable_if_t && + magic_enum::enum_contains(Enum), V&> get( magic_enum::containers::array& a ) noexcept { + return a[Enum]; + } + + template< auto Enum, typename E, typename V, typename Index> + constexpr std::enable_if_t && + magic_enum::enum_contains(Enum), V&&> get( magic_enum::containers::array&& a ) noexcept { + return std::move(a[Enum]); + } + + template< auto Enum, typename E, typename V, typename Index> + constexpr std::enable_if_t && + magic_enum::enum_contains(Enum), const V&> get( const magic_enum::containers::array& a ) noexcept { + return a[Enum]; + } + + template< auto Enum, typename E, typename V, typename Index> + constexpr std::enable_if_t && + magic_enum::enum_contains(Enum), const V&&> get( const magic_enum::containers::array&& a ) noexcept { + return std::move(a[Enum]); + } +} + +#endif// NEARGYE_MAGIC_ENUM_CONTAINERS_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cd3d177..c3fd695 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,21 +41,25 @@ endfunction() make_test(test.cpp test-cpp17 c++17) make_test(test_flags.cpp test_flags-cpp17 c++17) make_test(test_aliases.cpp test_aliases-cpp17 c++17) +make_test(test_containers.cpp test_containers-cpp17 c++17) if(HAS_CPP20_FLAG) make_test(test.cpp test-cpp20 c++20) make_test(test_flags.cpp test_flags-cpp20 c++20) make_test(test_aliases.cpp test_aliases-cpp20 c++20) + make_test(test_containers.cpp test_containers-cpp20 c++20) endif() if(HAS_CPP23_FLAG) make_test(test.cpp test-cpp23 c++23) make_test(test_flags.cpp test_flags-cpp23 c++23) make_test(test_aliases.cpp test_aliases-cpp23 c++23) + make_test(test_containers.cpp test_containers-cpp23 c++23) endif() if(HAS_CPPLATEST_FLAG) make_test(test.cpp test-cpplatest c++latest) make_test(test_flags.cpp test_flags-cpplatest c++latest) make_test(test_aliases.cpp test_aliases-cpplatest c++latest) + make_test(test_containers.cpp test_containers-cpplatest c++latest) endif() diff --git a/test/test_containers.cpp b/test/test_containers.cpp new file mode 100644 index 0000000..6a3c875 --- /dev/null +++ b/test/test_containers.cpp @@ -0,0 +1,318 @@ +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2019 - 2022 Daniil Goncharov . +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#if defined(__clang__) +# pragma clang diagnostic push +#elif defined(__GNUC__) +# pragma GCC diagnostic push +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4244) // warning C4244: 'argument': conversion from 'const T' to 'unsigned int', possible loss of data. +#endif + +#ifdef _WIN32 +#define _ITERATOR_DEBUG_LEVEL 0 +#endif + +#define CATCH_CONFIG_MAIN +#include + +#include + +#include + +enum class Color { RED = 1, GREEN = 2, BLUE = 4 }; + +enum class Empty {}; + +struct RGB { + + std::uint8_t r {}; + std::uint8_t g {}; + std::uint8_t b {}; + + [[nodiscard]] constexpr bool empty() { return std::equal_to{}(r, g) && std::equal_to{}(g, b) && std::equal_to{}(b, 0); } + + [[nodiscard]] constexpr bool operator==(RGB rgb) const noexcept { return std::equal_to{}(r, rgb.r) && std::equal_to{}(g, rgb.g) && std::equal_to{}(b, rgb.b); } + + friend std::ostream& operator<<(std::ostream& ostream, RGB rgb) { + + ostream << "R=" << static_cast(rgb.r) << " G=" << static_cast(rgb.g) << " B=" << static_cast(rgb.b); + return ostream; + } +}; + +template bool check_const([[maybe_unused]]T& element) { return false; } +template bool check_const([[maybe_unused]]T const& element) { return true; } + +constexpr std::uint8_t color_max = std::numeric_limits::max(); + +TEST_CASE("containers_array") { + + using namespace magic_enum::bitwise_operators; + using namespace magic_enum::ostream_operators; + + constexpr magic_enum::containers::array color_rgb_initializer {{{{color_max, 0, 0}, {0, color_max, 0}, {0, 0, color_max}}}}; + REQUIRE(color_rgb_initializer.at(Color::RED) == RGB{color_max, 0, 0}); + REQUIRE(color_rgb_initializer.at(Color::GREEN) == RGB{0, color_max, 0}); + REQUIRE(color_rgb_initializer.at(Color::BLUE) == RGB{0, 0, color_max}); + + /* BUG: a sort will not survive the data integration */ + magic_enum::containers::array color_rgb_container_int{{1U, 4U, 2U}}; + + // ENC: Direct convert to std::array + // std::array compare_before {1U, 4U, 2U}; + constexpr magic_enum::containers::array compare_before{{1U, 4U, 2U}}; + REQUIRE(color_rgb_container_int == compare_before); + + constexpr auto colors = magic_enum::enum_values(); + + std::ignore = std::get<0>(compare_before); + std::ignore = std::get<1>(compare_before); + std::ignore = std::get<2>(compare_before); + + std::ignore = std::get(compare_before); + std::ignore = std::get(compare_before); + std::ignore = std::get(compare_before); + + REQUIRE(std::make_pair(colors[0], color_rgb_container_int[colors[0]]) == std::make_pair(Color::RED, 1U)); + REQUIRE(std::make_pair(colors[1], color_rgb_container_int[colors[1]]) == std::make_pair(Color::GREEN, 4U)); + REQUIRE(std::make_pair(colors[2], color_rgb_container_int[colors[2]]) == std::make_pair(Color::BLUE, 2U)); + + std::sort(std::begin(color_rgb_container_int), std::end(color_rgb_container_int)); + + // Missing: Direct convert to std::array + // std::array compare_after {1U, 2U, 4U}; + constexpr magic_enum::containers::array compare_after{{1U, 2U, 4U}}; + REQUIRE(color_rgb_container_int == compare_after); + + std::ignore = std::get<0>(compare_after); + std::ignore = std::get<1>(compare_after); + std::ignore = std::get<2>(compare_after); + + std::ignore = std::get(compare_after); + std::ignore = std::get(compare_after); + std::ignore = std::get(compare_after); + + REQUIRE(std::make_pair(colors[0], color_rgb_container_int[colors[0]]) == std::make_pair(Color::RED, 1U)); + REQUIRE(std::make_pair(colors[1], color_rgb_container_int[colors[1]]) == std::make_pair(Color::GREEN, 2U)); + REQUIRE(std::make_pair(colors[2], color_rgb_container_int[colors[2]]) == std::make_pair(Color::BLUE, 4U)); + + auto empty = magic_enum::containers::array(); + REQUIRE(empty.empty()); + REQUIRE(empty.size() == 0); + REQUIRE(magic_enum::enum_count() == empty.size()); + + auto color_rgb_container = magic_enum::containers::array(); + REQUIRE_FALSE(color_rgb_container.empty()); + REQUIRE(color_rgb_container.size() == 3); + REQUIRE(magic_enum::enum_count() == color_rgb_container.size()); + + REQUIRE(color_rgb_container.at(Color::RED).empty()); + REQUIRE(color_rgb_container.at(Color::GREEN).empty()); + REQUIRE(color_rgb_container.at(Color::BLUE).empty()); + REQUIRE_THROWS(color_rgb_container.at(Color::BLUE|Color::GREEN).empty()); + + color_rgb_container[Color::RED] = {color_max, 0, 0}; + color_rgb_container[Color::GREEN] = {0, color_max, 0}; + color_rgb_container[Color::BLUE] = {0, 0, color_max}; + + REQUIRE(color_rgb_container.at(Color::RED) == RGB{color_max, 0, 0}); + REQUIRE(color_rgb_container.at(Color::GREEN) == RGB{0, color_max, 0}); + REQUIRE(color_rgb_container.at(Color::BLUE) == RGB{0, 0, color_max}); + + REQUIRE(color_rgb_container.front() == RGB{color_max, 0, 0}); + REQUIRE(color_rgb_container.back() == RGB{0, 0, color_max}); + + REQUIRE(std::get(color_rgb_container) == RGB{color_max, 0, 0}); + REQUIRE(std::get(color_rgb_container) == RGB{0, color_max, 0}); + REQUIRE(std::get(color_rgb_container) == RGB{0, 0, color_max}); + + auto iterator = color_rgb_container.begin(); + REQUIRE_FALSE(check_const(iterator)); + REQUIRE(check_const(color_rgb_container.begin())); + REQUIRE(check_const(color_rgb_container.cbegin())); + + auto color_rgb_container_compare = magic_enum::containers::array(); + color_rgb_container_compare.fill({color_max, color_max, color_max}); + REQUIRE_FALSE(color_rgb_container == color_rgb_container_compare); + + color_rgb_container_compare[Color::RED] = {color_max, 0, 0}; + color_rgb_container_compare[Color::GREEN] = {0, color_max, 0}; + color_rgb_container_compare[Color::BLUE] = {0, 0, color_max}; + REQUIRE(color_rgb_container == color_rgb_container_compare); + + constexpr auto from_to_array = magic_enum::containers::to_array({{color_max, 0, 0}, {0, color_max, 0}, {0, 0, color_max}}); + REQUIRE(from_to_array.at(Color::RED) == RGB{color_max, 0, 0}); + REQUIRE(from_to_array.at(Color::GREEN) == RGB{0, color_max, 0}); + REQUIRE(from_to_array.at(Color::BLUE) == RGB{0, 0, color_max}); +} + +TEST_CASE("containers_bitset") { + + using namespace magic_enum::bitwise_operators; + + auto color_bitset = magic_enum::containers::bitset(); + REQUIRE(color_bitset.to_string().empty()); + REQUIRE(color_bitset.size() == 3); + REQUIRE(magic_enum::enum_count() == color_bitset.size()); + REQUIRE_FALSE(color_bitset.all()); + REQUIRE_FALSE(color_bitset.any()); + REQUIRE(color_bitset.none()); + REQUIRE(color_bitset.count() == 0); + + color_bitset.set(Color::GREEN); + REQUIRE_FALSE(color_bitset.all()); + REQUIRE(color_bitset.any()); + REQUIRE_FALSE(color_bitset.none()); + REQUIRE(color_bitset.count() == 1); + REQUIRE_FALSE(color_bitset.test(Color::RED)); + REQUIRE(color_bitset.test(Color::GREEN)); + REQUIRE_FALSE(color_bitset.test(Color::BLUE)); + + color_bitset.set(Color::BLUE); + REQUIRE_FALSE(color_bitset.all()); + REQUIRE(color_bitset.any()); + REQUIRE_FALSE(color_bitset.none()); + REQUIRE(color_bitset.count() == 2); + REQUIRE_FALSE(color_bitset.test(Color::RED)); + REQUIRE(color_bitset.test(Color::GREEN)); + REQUIRE(color_bitset.test(Color::BLUE)); + + color_bitset.set(Color::RED); + REQUIRE(color_bitset.all()); + REQUIRE(color_bitset.any()); + REQUIRE_FALSE(color_bitset.none()); + REQUIRE(color_bitset.count() == 3); + REQUIRE(color_bitset.test(Color::RED)); + REQUIRE(color_bitset.test(Color::GREEN)); + REQUIRE(color_bitset.test(Color::BLUE)); + + color_bitset.reset(); + REQUIRE_FALSE(color_bitset.all()); + REQUIRE_FALSE(color_bitset.any()); + REQUIRE(color_bitset.none()); + REQUIRE(color_bitset.count() == 0); + REQUIRE_FALSE(color_bitset.test(Color::RED)); + REQUIRE_FALSE(color_bitset.test(Color::GREEN)); + REQUIRE_FALSE(color_bitset.test(Color::BLUE)); + + color_bitset.set(Color::RED); + REQUIRE(color_bitset.test(Color::RED)); + REQUIRE_FALSE(color_bitset.test(Color::GREEN)); + REQUIRE_FALSE(color_bitset.test(Color::BLUE)); + + color_bitset.flip(); + REQUIRE_FALSE(color_bitset.test(Color::RED)); + REQUIRE(color_bitset.test(Color::GREEN)); + REQUIRE(color_bitset.test(Color::BLUE)); + + constexpr magic_enum::containers::bitset color_bitset_all {Color::RED|Color::GREEN|Color::BLUE}; + REQUIRE(color_bitset_all.to_string() == "RED|GREEN|BLUE"); + REQUIRE(color_bitset_all.to_string( {}, '0', '1' ) == "111"); + REQUIRE(color_bitset_all.to_ulong( {} ) == 7); + REQUIRE(color_bitset_all.to_ullong( {} ) == 7); + REQUIRE(color_bitset_all.all()); + REQUIRE(color_bitset_all.any()); + REQUIRE_FALSE(color_bitset_all.none()); + + constexpr magic_enum::containers::bitset color_bitset_red_green {Color::RED|Color::GREEN}; + REQUIRE(color_bitset_red_green.to_string() == "RED|GREEN"); + REQUIRE(color_bitset_red_green.to_string( {}, '0', '1' ) == "110"); + REQUIRE(color_bitset_red_green.to_ulong( {} ) == 3); + REQUIRE(color_bitset_red_green.to_ullong( {} ) == 3); + REQUIRE_FALSE(color_bitset_red_green.all()); + REQUIRE(color_bitset_red_green.any()); + REQUIRE_FALSE(color_bitset_red_green.none()); +} + +TEST_CASE("containers_set") { + + using namespace magic_enum::bitwise_operators; + using namespace magic_enum::ostream_operators; + + auto color_set = magic_enum::containers::set(); + REQUIRE(color_set.empty()); + REQUIRE(color_set.size() == 0); + REQUIRE_FALSE(magic_enum::enum_count() == color_set.size()); + + color_set.insert(Color::RED); + std::ignore = color_set.insert(Color::RED); + color_set.insert(Color::GREEN); + color_set.insert(Color::BLUE); + color_set.insert(Color::RED); + color_set.insert(Color::RED|Color::GREEN); + color_set.insert(Color::RED|Color::BLUE); + color_set.insert(Color::GREEN|Color::BLUE); + color_set.insert(Color::RED|Color::GREEN|Color::BLUE); + + REQUIRE_FALSE(color_set.empty()); + REQUIRE(color_set.size() == 3); + REQUIRE(magic_enum::enum_count() == color_set.size()); + + auto color_set_compare = magic_enum::containers::set(); + color_set_compare.insert(Color::BLUE); + color_set_compare.insert(Color::RED); + color_set_compare.insert(Color::GREEN); + + constexpr magic_enum::containers::set color_set_filled = {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE_FALSE(color_set_filled.empty()); + REQUIRE(color_set_filled.size() == 3); + REQUIRE(magic_enum::enum_count() == color_set_filled.size()); + + magic_enum::containers::set color_set_not_const {Color::RED, Color::GREEN, Color::BLUE}; + REQUIRE_FALSE(color_set_not_const.empty()); + REQUIRE(color_set_not_const.size() == 3); + REQUIRE(magic_enum::enum_count() == color_set_not_const.size()); + color_set_not_const.clear(); + REQUIRE(color_set_not_const.empty()); + REQUIRE(color_set_not_const.size() == 0); + REQUIRE_FALSE(magic_enum::enum_count() == color_set_not_const.size()); +} + +TEST_CASE("containers_flat_set") { + + // constexpr magic_enum::containers::flat_set color_flat_set_filled = {Color::RED, Color::GREEN, Color::BLUE}; + // REQUIRE_FALSE(color_flat_set_filled.empty()); + // REQUIRE(color_flat_set_filled.size() == 3); + // REQUIRE(magic_enum::enum_count() == color_flat_set_filled.size()); +} + +TEST_CASE("map_like_container") { + + using namespace magic_enum::ostream_operators; + + std::vector> map {{Color::GREEN, {0, color_max, 0}}, {Color::BLUE, {0, 0, color_max}}, {Color::RED, {color_max, 0, 0}}}; + for (auto [key, value] : map) { + + std::cout << "Key=" << key << " Value=" << value << std::endl; + } + auto compare = [](std::pair& lhs, + std::pair& rhs) { + return static_cast(lhs.first) < static_cast(rhs.first); + }; + std::sort(std::begin(map), std::end(map), compare); + for (auto [key, value] : map) { + + std::cout << "Key=" << key << " Value=" << value << std::endl; + } +}