Program Listing for File magic_enum_containers.hpp
↰ Return to documentation for file (/tmp/ws/src/magic_enum/include/magic_enum/magic_enum_containers.hpp
)
// __ __ _ ______ _____
// | \/ | (_) | ____| / ____|_ _
// | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_
// | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _|
// | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_|
// |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____|
// __/ | https://github.com/Neargye/magic_enum
// |___/ version 0.9.5
//
// Licensed under the MIT License <http://opensource.org/licenses/MIT>.
// SPDX-License-Identifier: MIT
// Copyright (c) 2019 - 2024 Daniil Goncharov <neargye@gmail.com>.
// Copyright (c) 2022 - 2023 Bela Schaum <schaumb@gmail.com>.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef NEARGYE_MAGIC_ENUM_CONTAINERS_HPP
#define NEARGYE_MAGIC_ENUM_CONTAINERS_HPP
#include "magic_enum.hpp"
#if !defined(MAGIC_ENUM_NO_EXCEPTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
# include <stdexcept>
# define MAGIC_ENUM_THROW(...) throw (__VA_ARGS__)
#else
# include <cstdlib>
# define MAGIC_ENUM_THROW(...) std::abort()
#endif
namespace magic_enum::containers {
namespace detail {
template <typename T, typename = void>
static constexpr bool is_transparent_v{};
template <typename T>
static constexpr bool is_transparent_v<T, std::void_t<typename T::is_transparent>>{true};
template <typename Eq = std::equal_to<>, 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 Cmp = std::less<>, 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 <typename 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 Cmp = std::less<>, 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 Cmp = std::less<>, 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 E = void, typename Cmp = std::less<E>, typename = void>
class indexing {
[[nodiscard]] static constexpr auto get_indices() noexcept {
// reverse result index mapping
std::array<std::size_t, enum_count<E>()> rev_res{};
// std::iota
for (std::size_t i = 0; i < enum_count<E>(); ++i) {
rev_res[i] = i;
}
constexpr auto orig_values = enum_values<E>();
constexpr Cmp cmp{};
// ~std::sort
for (std::size_t i = 0; i < enum_count<E>(); ++i) {
for (std::size_t j = i + 1; j < enum_count<E>(); ++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<E, enum_count<E>()> sorted_values{};
// reverse the sorted indices
std::array<std::size_t, enum_count<E>()> res{};
for (std::size_t i = 0; i < enum_count<E>(); ++i) {
res[rev_res[i]] = i;
sorted_values[i] = orig_values[rev_res[i]];
}
return std::pair{sorted_values, res};
}
static constexpr auto indices = get_indices();
public:
[[nodiscard]] static constexpr const E* begin() noexcept { return indices.first.data(); }
[[nodiscard]] static constexpr const E* end() noexcept { return indices.first.data() + indices.first.size(); }
[[nodiscard]] static constexpr const E* it(std::size_t i) noexcept { return indices.first.data() + i; }
[[nodiscard]] static constexpr optional<std::size_t> at(E val) noexcept {
if (auto i = enum_index(val)) {
return indices.second[*i];
}
return {};
}
};
template <typename E, typename Cmp>
class indexing<E, Cmp, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && (std::is_same_v<Cmp, std::less<E>> || std::is_same_v<Cmp, std::less<>>)>> {
static constexpr auto& values = enum_values<E>();
public:
[[nodiscard]] static constexpr const E* begin() noexcept { return values.data(); }
[[nodiscard]] static constexpr const E* end() noexcept { return values.data() + values.size(); }
[[nodiscard]] static constexpr const E* it(std::size_t i) noexcept { return values.data() + i; }
[[nodiscard]] static constexpr optional<std::size_t> at(E val) noexcept { return enum_index(val); }
};
template <typename Cmp>
struct indexing<void, Cmp, void> {
using is_transparent = std::true_type;
template <typename E>
[[nodiscard]] static constexpr optional<std::size_t> at(E val) noexcept {
return indexing<E, Cmp>::at(val);
}
};
template <typename E = void, typename Cmp = std::less<>, typename = void>
struct name_sort_impl {
[[nodiscard]] constexpr bool operator()(E e1, E e2) const noexcept { return Cmp{}(enum_name(e1), enum_name(e2)); }
};
template <typename Cmp>
struct name_sort_impl<void, Cmp> {
using is_transparent = std::true_type;
template <typename C = Cmp, typename = void>
struct FullCmp : C {};
template <typename C>
struct FullCmp<C, std::enable_if_t<!std::is_invocable_v<C, string_view, string_view> && std::is_invocable_v<C, char_type, char_type>>> {
[[nodiscard]] constexpr bool operator()(string_view s1, string_view s2) const noexcept { return lexicographical_compare<C>(s1, s2); }
};
template <typename E1, typename E2>
[[nodiscard]] constexpr std::enable_if_t<
// at least one of need to be an enum type
(std::is_enum_v<std::decay_t<E1>> || std::is_enum_v<std::decay_t<E2>>) &&
// if both is enum, only accept if the same enum
(!std::is_enum_v<std::decay_t<E1>> || !std::is_enum_v<std::decay_t<E2>> || std::is_same_v<E1, E2>) &&
// is invocable with comparator
(std::is_invocable_r_v<bool, FullCmp<>, std::conditional_t<std::is_enum_v<std::decay_t<E1>>, string_view, E1>, std::conditional_t<std::is_enum_v<std::decay_t<E2>>, string_view, E2>>),
bool>
operator()(E1 e1, E2 e2) const noexcept {
using D1 = std::decay_t<E1>;
using D2 = std::decay_t<E2>;
constexpr FullCmp<> cmp{};
if constexpr (std::is_enum_v<D1> && std::is_enum_v<D2>) {
return cmp(enum_name(e1), enum_name(e2));
} else if constexpr (std::is_enum_v<D1>) {
return cmp(enum_name(e1), e2);
} else /* if constexpr (std::is_enum_v<D2>) */ {
return cmp(e1, enum_name(e2));
}
}
};
struct raw_access_t {};
template <typename Parent, typename Iterator, typename Getter, typename Predicate>
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<std::invoke_result_t<Getter, Parent, Iterator>>;
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 <typename OtherParent, typename OtherIterator, typename = std::enable_if_t<std::is_convertible_v<OtherParent, Parent> && std::is_convertible_v<OtherIterator, Iterator>>*>
constexpr explicit FilteredIterator(const FilteredIterator<OtherParent, OtherIterator, Getter, Predicate>& other)
: parent(other.parent), first(other.first), last(other.last), current(other.current), getter(other.getter), predicate(other.predicate) {}
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; }
};
} // namespace detail
template <typename E = void>
using name_less = detail::name_sort_impl<E>;
template <typename E = void>
using name_greater = detail::name_sort_impl<E, std::greater<>>;
using name_less_case_insensitive = detail::name_sort_impl<void, magic_enum::detail::case_insensitive<std::less<>>>;
using name_greater_case_insensitive = detail::name_sort_impl<void, magic_enum::detail::case_insensitive<std::greater<>>>;
template <typename E = void>
using default_indexing = detail::indexing<E>;
template <typename Cmp = std::less<>>
using comparator_indexing = detail::indexing<void, Cmp>;
// ARRAY //
template <typename E, typename V, typename Index = default_indexing<E>>
struct array {
static_assert(std::is_enum_v<E>);
static_assert(std::is_trivially_constructible_v<Index>);
static_assert(enum_count<E>() > 0 && Index::at(enum_values<E>().front()));
using index_type = Index;
using container_type = std::array<V, enum_count<E>()>;
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::at(pos)) {
return a[*index];
}
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::array::at Unrecognized position"));
}
constexpr const_reference at(E pos) const {
if (auto index = index_type::at(pos)) {
return a[*index];
}
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::array::at: Unrecognized position"));
}
[[nodiscard]] constexpr reference operator[](E pos) {
auto i = index_type::at(pos);
return MAGIC_ENUM_ASSERT(i), a[*i];
}
[[nodiscard]] constexpr const_reference operator[](E pos) const {
auto i = index_type::at(pos);
return MAGIC_ENUM_ASSERT(i), a[*i];
}
[[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<V>) {
for (std::size_t i = 0; 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 <typename E, typename T, std::size_t N, std::size_t... I>
constexpr array<E, std::remove_cv_t<T>> to_array_impl(T (&a)[N], std::index_sequence<I...>) {
return {{a[I]...}};
}
template <typename E, typename T, std::size_t N, std::size_t... I>
constexpr array<E, std::remove_cv_t<T>> to_array_impl(T(&&a)[N], std::index_sequence<I...>) {
return {{std::move(a[I])...}};
}
} // namespace detail
template <typename E, typename T, std::size_t N>
constexpr std::enable_if_t<(enum_count<E>() == N), array<E, std::remove_cv_t<T>>> to_array(T (&a)[N]) {
return detail::to_array_impl<E>(a, std::make_index_sequence<N>{});
}
template <typename E, typename T, std::size_t N>
constexpr std::enable_if_t<(enum_count<E>() == N), array<E, std::remove_cv_t<T>>> to_array(T(&&a)[N]) {
return detail::to_array_impl<E>(std::move(a), std::make_index_sequence<N>{});
}
template <typename E, typename... Ts>
constexpr std::enable_if_t<(enum_count<E>() == sizeof...(Ts)), array<E, std::remove_cv_t<std::common_type_t<Ts...>>>> make_array(Ts&&... ts) {
return {{std::forward<Ts>(ts)...}};
}
inline constexpr detail::raw_access_t raw_access{};
// BITSET //
template <typename E, typename Index = default_indexing<E>>
class bitset {
static_assert(std::is_enum_v<E>);
static_assert(std::is_trivially_constructible_v<Index>);
static_assert(enum_count<E>() > 0 && Index::at(enum_values<E>().front()));
using base_type = std::conditional_t<enum_count<E>() <= 8, std::uint_least8_t,
std::conditional_t<enum_count<E>() <= 16, std::uint_least16_t,
std::conditional_t<enum_count<E>() <= 32, std::uint_least32_t,
std::uint_least64_t>>>;
static constexpr std::size_t bits_per_base = sizeof(base_type) * 8;
static constexpr std::size_t base_type_count = (enum_count<E>() > 0 ? (enum_count<E>() - 1) / bits_per_base + 1 : 0);
static constexpr std::size_t not_interested = base_type_count * bits_per_base - enum_count<E>();
static constexpr base_type last_value_max = (base_type{1} << (bits_per_base - not_interested)) - 1;
template <typename parent_t = bitset*>
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<std::size_t, base_type> 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<bool>(v);
return *this;
}
[[nodiscard]] constexpr operator bool() const noexcept { return (parent->a[num_index] & bit_index) > 0; }
[[nodiscard]] constexpr bool operator~() const noexcept { return !static_cast<bool>(*this); }
constexpr reference_impl& flip() noexcept {
*this = ~*this;
return *this;
}
};
template <typename T>
[[nodiscard]] constexpr T to_(detail::raw_access_t) const {
T res{};
T flag{1};
for (std::size_t i = 0; i < size(); ++i, flag <<= 1) {
if (const_reference{this, i}) {
if (i >= sizeof(T) * 8) {
MAGIC_ENUM_THROW(std::overflow_error("magic_enum::containers::bitset::to: Cannot represent enum in this type"));
}
res |= flag;
}
}
return res;
}
public:
using index_type = Index;
using container_type = std::array<base_type, base_type_count>;
using reference = reference_impl<>;
using const_reference = reference_impl<const bitset*>;
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 = 0; i < (sizeof(val) * 8); ++i, bit <<= 1) {
if ((val & bit) > 0) {
if (i >= enum_count<E>()) {
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::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_type zero = static_cast<char_type>('0'), char_type one = static_cast<char_type>('1'))
: a{{}} {
std::size_t i = 0;
for (auto c : sv.substr(pos, n)) {
if (c == one) {
if (i >= enum_count<E>()) {
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::constructor: Upper bit set in raw string"));
}
reference{this, i} = true;
} else if (c != zero) {
MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized character in raw string"));
}
++i;
}
}
constexpr explicit bitset(detail::raw_access_t, const char_type* str, std::size_t n = ~std::size_t{0}, char_type zero = static_cast<char_type>('0'), char_type one = static_cast<char_type>('1'))
: bitset(string_view{str, (std::min)(std::char_traits<char_type>::length(str), n)}, 0, n, zero, one) {}
constexpr bitset(std::initializer_list<E> starters) : a{{}} {
if constexpr (magic_enum::detail::subtype_v<E> == magic_enum::detail::enum_subtype::flags) {
for (auto& f : starters) {
*this |= bitset(f);
}
} else {
for (auto& f : starters) {
set(f);
}
}
}
template <typename V, std::enable_if_t<std::is_same_v<V, E> && magic_enum::detail::subtype_v<V> == magic_enum::detail::enum_subtype::flags, int> = 0>
constexpr explicit bitset(V starter) : a{{}} {
auto u = enum_underlying(starter);
for (E v : enum_values<E>()) {
if (auto ul = enum_underlying(v); (ul & u) != 0) {
u &= ~ul;
(*this)[v] = true;
}
}
if (u != 0) {
MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized enum value in flag"));
}
}
template <typename Cmp = std::equal_to<>>
constexpr explicit bitset(string_view sv, Cmp&& cmp = {}, char_type sep = static_cast<char_type>('|')) {
for (std::size_t to = 0; (to = magic_enum::detail::find(sv, sep)) != string_view::npos; sv.remove_prefix(to + 1)) {
if (auto v = enum_cast<E>(sv.substr(0, to), cmp)) {
set(v);
} else {
MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized enum value in string"));
}
}
if (!sv.empty()) {
if (auto v = enum_cast<E>(sv, cmp)) {
set(v);
} else {
MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::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 {
auto i = index_type::at(pos);
return MAGIC_ENUM_ASSERT(i), static_cast<bool>(const_reference(this, *i));
}
[[nodiscard]] constexpr reference operator[](E pos) {
auto i = index_type::at(pos);
return MAGIC_ENUM_ASSERT(i), reference{this, *i};
}
constexpr bool test(E pos) const {
if (auto i = index_type::at(pos)) {
return static_cast<bool>(const_reference(this, *i));
}
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::test: Unrecognized position"));
}
[[nodiscard]] constexpr bool all() const noexcept {
if constexpr (base_type_count == 0) {
return true;
}
for (std::size_t i = 0; 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 = 0;
for (auto& v : a) {
c += detail::popcount(v);
}
return c;
}
[[nodiscard]] constexpr std::size_t size() const noexcept { return enum_count<E>(); }
[[nodiscard]] constexpr std::size_t max_size() const noexcept { return enum_count<E>(); }
constexpr bitset& operator&=(const bitset& other) noexcept {
for (std::size_t i = 0; i < base_type_count; ++i) {
a[i] &= other.a[i];
}
return *this;
}
constexpr bitset& operator|=(const bitset& other) noexcept {
for (std::size_t i = 0; i < base_type_count; ++i) {
a[i] |= other.a[i];
}
return *this;
}
constexpr bitset& operator^=(const bitset& other) noexcept {
for (std::size_t i = 0; 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 = 0; 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 = 0; i < base_type_count - (not_interested > 0); ++i) {
a[i] = ~base_type{0};
}
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 i = index_type::at(pos)) {
reference{this, *i} = value;
return *this;
}
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::set: Unrecognized position"));
}
constexpr bitset& reset() noexcept { return *this = bitset{}; }
constexpr bitset& reset(E pos) {
if (auto i = index_type::at(pos)) {
reference{this, *i} = false;
return *this;
}
MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::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 <typename V = E>
[[nodiscard]] constexpr explicit operator std::enable_if_t<magic_enum::detail::subtype_v<V> == magic_enum::detail::enum_subtype::flags, E>() const {
E res{};
for (const auto& e : enum_values<E>()) {
if (test(e)) {
res |= e;
}
}
return res;
}
[[nodiscard]] string to_string(char_type sep = static_cast<char_type>('|')) const {
string name;
for (const auto& e : enum_values<E>()) {
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_type zero = static_cast<char_type>('0'), char_type one = static_cast<char_type>('1')) const {
string name;
name.reserve(size());
for (std::size_t i = 0; 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_<unsigned long long>(raw); }
[[nodiscard]] constexpr unsigned long long to_ulong(detail::raw_access_t raw) const { return to_<unsigned long>(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;
};
template <typename V, int = 0>
explicit bitset(V starter) -> bitset<V>;
// SET //
template <typename E, typename Cmp = std::less<E>>
class set {
using index_type = detail::indexing<E, Cmp>;
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<E, index_type>;
using key_type = E;
using value_type = E;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using key_compare = Cmp;
using value_compare = Cmp;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = detail::FilteredIterator<const set*, const E*, Getter, Predicate>;
using const_iterator = detail::FilteredIterator<const set*, const E*, Getter, Predicate>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr set() noexcept = default;
template <typename InputIt>
constexpr set(InputIt first, InputIt last) {
while (first != last) {
insert(*first++);
}
}
constexpr set(std::initializer_list<E> ilist) {
for (auto e : ilist) {
insert(e);
}
}
template <typename V, std::enable_if_t<std::is_same_v<V, E> && magic_enum::detail::subtype_v<V> == magic_enum::detail::enum_subtype::flags, int> = 0>
constexpr explicit set(V starter) {
auto u = enum_underlying(starter);
for (E v : enum_values<E>()) {
if ((enum_underlying(v) & u) != 0) {
insert(v);
}
}
}
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<E> ilist) {
for (auto e : ilist) {
insert(e);
}
}
constexpr const_iterator begin() const noexcept {
return const_iterator{this, index_type::begin(), index_type::end(), index_type::begin()};
}
constexpr const_iterator end() const noexcept {
return const_iterator{this, index_type::begin(), index_type::end(), index_type::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<iterator, bool> insert(const value_type& value) noexcept {
if (auto i = index_type::at(value)) {
typename container_type::reference ref = a[value];
bool r = !ref;
if (r) {
ref = true;
++s;
}
return {iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)}, r};
}
return {end(), false};
}
constexpr std::pair<iterator, bool> 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 <typename InputIt>
constexpr void insert(InputIt first, InputIt last) noexcept {
while (first != last) {
insert(*first++);
}
}
constexpr void insert(std::initializer_list<value_type> ilist) noexcept {
for (auto v : ilist) {
insert(v);
}
}
template <typename... Args>
constexpr std::pair<iterator, bool> emplace(Args&&... args) noexcept {
return insert({std::forward<Args>(args)...});
}
template <typename... Args>
constexpr iterator emplace_hint(const_iterator, Args&&... args) noexcept {
return emplace(std::forward<Args>(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 <typename K, typename KC = key_compare>
constexpr std::enable_if_t<detail::is_transparent_v<KC>, size_type> erase(K&& x) noexcept {
size_type c = 0;
for (auto [first, last] = detail::equal_range(index_type::begin(), index_type::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::at(key) && a[key]; }
template <typename K, typename KC = key_compare>
[[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, size_type> count(const K& x) const {
size_type c = 0;
for (auto [first, last] = detail::equal_range(index_type::begin(), index_type::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::at(key); i && a.test(key)) {
return const_iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)};
}
return end();
}
template <typename K, typename KC = key_compare>
[[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, const_iterator> find(const K& x) const {
for (auto [first, last] = detail::equal_range(index_type::begin(), index_type::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 <typename K, typename KC = key_compare>
[[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, bool> contains(const K& x) const noexcept {
return count(x) > 0;
}
[[nodiscard]] constexpr std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const noexcept { return {lower_bound(key), upper_bound(key)}; }
template <typename K, typename KC = key_compare>
[[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, std::pair<const_iterator, const_iterator>> 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::at(key)) {
auto it = const_iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)};
return a.test(key) ? it : std::next(it);
}
return end();
}
template <typename K, typename KC = key_compare>
[[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, const_iterator> lower_bound(const K& x) const noexcept {
auto [first, last] = detail::equal_range(index_type::begin(), index_type::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::at(key)) {
return std::next(const_iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)});
}
return end();
}
template <typename K, typename KC = key_compare>
[[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, const_iterator> upper_bound(const K& x) const noexcept {
auto [first, last] = detail::equal_range(index_type::begin(), index_type::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 it = index_type::begin(); it != index_type::end(); ++it) {
if (auto c = rhs.contains(*it); c != lhs.contains(*it)) {
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 <typename Pred>
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 = 0;
};
template <typename V, int = 0>
explicit set(V starter) -> set<V>;
template <auto I, typename E, typename V, typename Index>
constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < enum_count<E>()), V&> get(array<E, V, Index>& a) noexcept {
return a.a[I];
}
template <auto I, typename E, typename V, typename Index>
constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < enum_count<E>()), V&&> get(array<E, V, Index>&& 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<decltype(I)> && I < enum_count<E>()), const V&> get(const array<E, V, Index>& a) noexcept {
return a.a[I];
}
template <auto I, typename E, typename V, typename Index>
constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < enum_count<E>()), const V&&> get(const array<E, V, Index>&& a) noexcept {
return std::move(a.a[I]);
}
template <auto Enum, typename E, typename V, typename Index>
constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && enum_contains(Enum), V&> get(array<E, V, Index>& a) {
return a[Enum];
}
template <auto Enum, typename E, typename V, typename Index>
constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && enum_contains(Enum), V&&> get(array<E, V, Index>&& a) {
return std::move(a[Enum]);
}
template <auto Enum, typename E, typename V, typename Index>
constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && enum_contains(Enum), const V&> get(const array<E, V, Index>& a) {
return a[Enum];
}
template <auto Enum, typename E, typename V, typename Index>
constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && enum_contains(Enum), const V&&> get(const array<E, V, Index>&& a) {
return std::move(a[Enum]);
}
} // namespace magic_enum::containers
#endif // NEARGYE_MAGIC_ENUM_CONTAINERS_HPP