/src/magic_enum/include/magic_enum_containers.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | // __ __ _ ______ _____ |
2 | | // | \/ | (_) | ____| / ____|_ _ |
3 | | // | \ / | __ _ __ _ _ ___ | |__ _ __ _ _ _ __ ___ | | _| |_ _| |_ |
4 | | // | |\/| |/ _` |/ _` | |/ __| | __| | '_ \| | | | '_ ` _ \ | | |_ _|_ _| |
5 | | // | | | | (_| | (_| | | (__ | |____| | | | |_| | | | | | | | |____|_| |_| |
6 | | // |_| |_|\__,_|\__, |_|\___| |______|_| |_|\__,_|_| |_| |_| \_____| |
7 | | // __/ | https://github.com/Neargye/magic_enum |
8 | | // |___/ version 0.9.3 |
9 | | // |
10 | | // Licensed under the MIT License <http://opensource.org/licenses/MIT>. |
11 | | // SPDX-License-Identifier: MIT |
12 | | // Copyright (c) 2019 - 2023 Daniil Goncharov <neargye@gmail.com>. |
13 | | // Copyright (c) 2022 - 2023 Bela Schaum <schaumb@gmail.com>. |
14 | | // |
15 | | // Permission is hereby granted, free of charge, to any person obtaining a copy |
16 | | // of this software and associated documentation files (the "Software"), to deal |
17 | | // in the Software without restriction, including without limitation the rights |
18 | | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
19 | | // copies of the Software, and to permit persons to whom the Software is |
20 | | // furnished to do so, subject to the following conditions: |
21 | | // |
22 | | // The above copyright notice and this permission notice shall be included in all |
23 | | // copies or substantial portions of the Software. |
24 | | // |
25 | | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
26 | | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
27 | | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
28 | | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
29 | | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
30 | | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
31 | | // SOFTWARE. |
32 | | |
33 | | #ifndef NEARGYE_MAGIC_ENUM_CONTAINERS_HPP |
34 | | #define NEARGYE_MAGIC_ENUM_CONTAINERS_HPP |
35 | | |
36 | | #include "magic_enum.hpp" |
37 | | |
38 | | #if !defined(MAGIC_ENUM_NO_EXCEPTION) && (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) |
39 | | # include <stdexcept> |
40 | 0 | # define MAGIC_ENUM_THROW(...) throw (__VA_ARGS__) |
41 | | #else |
42 | | # include <cstdlib> |
43 | | # define MAGIC_ENUM_THROW(...) std::abort() |
44 | | #endif |
45 | | |
46 | | namespace magic_enum::containers { |
47 | | |
48 | | namespace detail { |
49 | | |
50 | | template <typename T, typename = void> |
51 | | static constexpr bool is_transparent_v{}; |
52 | | |
53 | | template <typename T> |
54 | | static constexpr bool is_transparent_v<T, std::void_t<typename T::is_transparent>>{true}; |
55 | | |
56 | | template <typename Eq = std::equal_to<>, typename T1, typename T2> |
57 | | constexpr bool equal(T1&& t1, T2&& t2, Eq&& eq = {}) { |
58 | | auto first1 = t1.begin(); |
59 | | auto last1 = t1.end(); |
60 | | auto first2 = t2.begin(); |
61 | | auto last2 = t2.end(); |
62 | | |
63 | | for (; first1 != last1; ++first1, ++first2) { |
64 | | if (first2 == last2 || !eq(*first1, *first2)) { |
65 | | return false; |
66 | | } |
67 | | } |
68 | | return first2 == last2; |
69 | | } |
70 | | |
71 | | template <typename Cmp = std::less<>, typename T1, typename T2> |
72 | | constexpr bool lexicographical_compare(T1&& t1, T2&& t2, Cmp&& cmp = {}) noexcept { |
73 | | auto first1 = t1.begin(); |
74 | | auto last1 = t1.end(); |
75 | | auto first2 = t2.begin(); |
76 | | auto last2 = t2.end(); |
77 | | |
78 | | // copied from std::lexicographical_compare |
79 | | for (; (first1 != last1) && (first2 != last2); ++first1, (void)++first2) { |
80 | | if (cmp(*first1, *first2)) { |
81 | | return true; |
82 | | } |
83 | | if (cmp(*first2, *first1)) { |
84 | | return false; |
85 | | } |
86 | | } |
87 | | return (first1 == last1) && (first2 != last2); |
88 | | } |
89 | | |
90 | | template <typename T> |
91 | 512 | constexpr std::size_t popcount(T x) noexcept { |
92 | 512 | std::size_t c = 0; |
93 | 2.04k | while (x > 0) { |
94 | 1.53k | c += x & 1; |
95 | 1.53k | x >>= 1; |
96 | 1.53k | } |
97 | 512 | return c; |
98 | 512 | } |
99 | | |
100 | | template <typename Cmp = std::less<>, typename ForwardIt, typename E> |
101 | | constexpr ForwardIt lower_bound(ForwardIt first, ForwardIt last, E&& e, Cmp&& comp = {}) { |
102 | | auto count = std::distance(first, last); |
103 | | for (auto it = first; count > 0;) { |
104 | | auto step = count / 2; |
105 | | std::advance(it, step); |
106 | | if (comp(*it, e)) { |
107 | | first = ++it; |
108 | | count -= step + 1; |
109 | | } else { |
110 | | count = step; |
111 | | } |
112 | | } |
113 | | return first; |
114 | | } |
115 | | |
116 | | template <typename Cmp = std::less<>, typename BidirIt, typename E> |
117 | | constexpr auto equal_range(BidirIt begin, BidirIt end, E&& e, Cmp&& comp = {}) { |
118 | | const auto first = lower_bound(begin, end, e, comp); |
119 | | 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()}; |
120 | | } |
121 | | |
122 | | template <typename E = void, typename Cmp = std::less<E>, typename = void> |
123 | | class indexing { |
124 | | [[nodiscard]] static constexpr auto get_indices() noexcept { |
125 | | // reverse result index mapping |
126 | | std::array<std::size_t, enum_count<E>()> rev_res{}; |
127 | | |
128 | | // std::iota |
129 | | for (std::size_t i = 0; i < enum_count<E>(); ++i) { |
130 | | rev_res[i] = i; |
131 | | } |
132 | | |
133 | | constexpr auto orig_values = enum_values<E>(); |
134 | | constexpr Cmp cmp{}; |
135 | | |
136 | | // ~std::sort |
137 | | for (std::size_t i = 0; i < enum_count<E>(); ++i) { |
138 | | for (std::size_t j = i + 1; j < enum_count<E>(); ++j) { |
139 | | if (cmp(orig_values[rev_res[j]], orig_values[rev_res[i]])) { |
140 | | auto tmp = rev_res[i]; |
141 | | rev_res[i] = rev_res[j]; |
142 | | rev_res[j] = tmp; |
143 | | } |
144 | | } |
145 | | } |
146 | | |
147 | | std::array<E, enum_count<E>()> sorted_values{}; |
148 | | // reverse the sorted indices |
149 | | std::array<std::size_t, enum_count<E>()> res{}; |
150 | | for (std::size_t i = 0; i < enum_count<E>(); ++i) { |
151 | | res[rev_res[i]] = i; |
152 | | sorted_values[i] = orig_values[rev_res[i]]; |
153 | | } |
154 | | |
155 | | return std::pair{sorted_values, res}; |
156 | | } |
157 | | |
158 | | static constexpr auto indices = get_indices(); |
159 | | |
160 | | public: |
161 | | [[nodiscard]] static constexpr const E* begin() noexcept { return indices.first.data(); } |
162 | | |
163 | | [[nodiscard]] static constexpr const E* end() noexcept { return indices.first.data() + indices.first.size(); } |
164 | | |
165 | | [[nodiscard]] static constexpr const E* it(std::size_t i) noexcept { return indices.first.data() + i; } |
166 | | |
167 | | [[nodiscard]] static constexpr optional<std::size_t> at(E val) noexcept { |
168 | | if (auto i = enum_index(val)) { |
169 | | return indices.second[*i]; |
170 | | } |
171 | | return {}; |
172 | | } |
173 | | }; |
174 | | |
175 | | template <typename E, typename Cmp> |
176 | | 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<>>)>> { |
177 | | static constexpr auto& values = enum_values<E>(); |
178 | | |
179 | | public: |
180 | 1.53k | [[nodiscard]] static constexpr const E* begin() noexcept { return values.data(); } |
181 | | |
182 | 1.53k | [[nodiscard]] static constexpr const E* end() noexcept { return values.data() + values.size(); } |
183 | | |
184 | 1.53k | [[nodiscard]] static constexpr const E* it(std::size_t i) noexcept { return values.data() + i; } |
185 | | |
186 | 4.09k | [[nodiscard]] static constexpr optional<std::size_t> at(E val) noexcept { return enum_index(val); } |
187 | | }; |
188 | | |
189 | | template <typename Cmp> |
190 | | struct indexing<void, Cmp, void> { |
191 | | using is_transparent = std::true_type; |
192 | | |
193 | | template <typename E> |
194 | | [[nodiscard]] static constexpr optional<std::size_t> at(E val) noexcept { |
195 | | return indexing<E, Cmp>::at(val); |
196 | | } |
197 | | }; |
198 | | |
199 | | template <typename E = void, typename Cmp = std::less<>, typename = void> |
200 | | struct name_sort_impl { |
201 | | [[nodiscard]] constexpr bool operator()(E e1, E e2) const noexcept { return Cmp{}(enum_name(e1), enum_name(e2)); } |
202 | | }; |
203 | | |
204 | | template <typename Cmp> |
205 | | struct name_sort_impl<void, Cmp> { |
206 | | using is_transparent = std::true_type; |
207 | | |
208 | | template <typename C = Cmp, typename = void> |
209 | | struct FullCmp : C {}; |
210 | | |
211 | | template <typename C> |
212 | | 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>>> { |
213 | | [[nodiscard]] constexpr bool operator()(string_view s1, string_view s2) const noexcept { return lexicographical_compare<C>(s1, s2); } |
214 | | }; |
215 | | |
216 | | template <typename E1, typename E2> |
217 | | [[nodiscard]] constexpr std::enable_if_t< |
218 | | // at least one of need to be an enum type |
219 | | (std::is_enum_v<std::decay_t<E1>> || std::is_enum_v<std::decay_t<E2>>) && |
220 | | // if both is enum, only accept if the same enum |
221 | | (!std::is_enum_v<std::decay_t<E1>> || !std::is_enum_v<std::decay_t<E2>> || std::is_same_v<E1, E2>) && |
222 | | // is invocable with comparator |
223 | | (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>>), |
224 | | bool> |
225 | | operator()(E1 e1, E2 e2) const noexcept { |
226 | | using D1 = std::decay_t<E1>; |
227 | | using D2 = std::decay_t<E2>; |
228 | | constexpr FullCmp<> cmp{}; |
229 | | |
230 | | if constexpr (std::is_enum_v<D1> && std::is_enum_v<D2>) { |
231 | | return cmp(enum_name(e1), enum_name(e2)); |
232 | | } else if constexpr (std::is_enum_v<D1>) { |
233 | | return cmp(enum_name(e1), e2); |
234 | | } else /* if constexpr (std::is_enum_v<D2>) */ { |
235 | | return cmp(e1, enum_name(e2)); |
236 | | } |
237 | | } |
238 | | }; |
239 | | |
240 | | struct raw_access_t {}; |
241 | | |
242 | | template <typename Parent, typename Iterator, typename Getter, typename Predicate> |
243 | | struct FilteredIterator { |
244 | | Parent parent; |
245 | | Iterator first; |
246 | | Iterator last; |
247 | | Iterator current; |
248 | | Getter getter; |
249 | | Predicate predicate; |
250 | | |
251 | | using iterator_category = std::bidirectional_iterator_tag; |
252 | | using value_type = std::remove_reference_t<std::invoke_result_t<Getter, Parent, Iterator>>; |
253 | | using difference_type = std::ptrdiff_t; |
254 | | using pointer = value_type*; |
255 | | using reference = value_type&; |
256 | | |
257 | | constexpr FilteredIterator() noexcept = default; |
258 | | constexpr FilteredIterator(const FilteredIterator&) = default; |
259 | | constexpr FilteredIterator& operator=(const FilteredIterator&) = default; |
260 | | constexpr FilteredIterator(FilteredIterator&&) noexcept = default; |
261 | | constexpr FilteredIterator& operator=(FilteredIterator&&) noexcept = default; |
262 | | |
263 | | template <typename OtherParent, typename OtherIterator, typename = std::enable_if_t<std::is_convertible_v<OtherParent, Parent> && std::is_convertible_v<OtherIterator, Iterator>>*> |
264 | | constexpr explicit FilteredIterator(const FilteredIterator<OtherParent, OtherIterator, Getter, Predicate>& other) |
265 | | : parent(other.parent), first(other.first), last(other.last), current(other.current), getter(other.getter), predicate(other.predicate) {} |
266 | | |
267 | | constexpr FilteredIterator(Parent p, Iterator begin, Iterator end, Iterator curr, Getter getter = {}, Predicate pred = {}) |
268 | 1.53k | : parent(p), first(std::move(begin)), last(std::move(end)), current(std::move(curr)), getter{std::move(getter)}, predicate{std::move(pred)} { |
269 | 1.53k | if (current == first && !predicate(parent, current)) { |
270 | 0 | ++*this; |
271 | 0 | } |
272 | 1.53k | } |
273 | | |
274 | | [[nodiscard]] constexpr reference operator*() const { return getter(parent, current); } |
275 | | |
276 | | [[nodiscard]] constexpr pointer operator->() const { return std::addressof(**this); } |
277 | | |
278 | 0 | constexpr FilteredIterator& operator++() { |
279 | 0 | do { |
280 | 0 | ++current; |
281 | 0 | } while (current != last && !predicate(parent, current)); |
282 | 0 | return *this; |
283 | 0 | } |
284 | | |
285 | | [[nodiscard]] constexpr FilteredIterator operator++(int) { |
286 | | FilteredIterator cp = *this; |
287 | | ++*this; |
288 | | return cp; |
289 | | } |
290 | | |
291 | | constexpr FilteredIterator& operator--() { |
292 | | do { |
293 | | --current; |
294 | | } while (current != first && !predicate(parent, current)); |
295 | | return *this; |
296 | | } |
297 | | |
298 | | [[nodiscard]] constexpr FilteredIterator operator--(int) { |
299 | | FilteredIterator cp = *this; |
300 | | --*this; |
301 | | return cp; |
302 | | } |
303 | | |
304 | | [[nodiscard]] friend constexpr bool operator==(const FilteredIterator& lhs, const FilteredIterator& rhs) { return lhs.current == rhs.current; } |
305 | | |
306 | | [[nodiscard]] friend constexpr bool operator!=(const FilteredIterator& lhs, const FilteredIterator& rhs) { return lhs.current != rhs.current; } |
307 | | }; |
308 | | |
309 | | } // namespace detail |
310 | | |
311 | | template <typename E = void> |
312 | | using name_less = detail::name_sort_impl<E>; |
313 | | |
314 | | template <typename E = void> |
315 | | using name_greater = detail::name_sort_impl<E, std::greater<>>; |
316 | | |
317 | | using name_less_case_insensitive = detail::name_sort_impl<void, magic_enum::detail::case_insensitive<std::less<>>>; |
318 | | |
319 | | using name_greater_case_insensitive = detail::name_sort_impl<void, magic_enum::detail::case_insensitive<std::greater<>>>; |
320 | | |
321 | | template <typename E = void> |
322 | | using default_indexing = detail::indexing<E>; |
323 | | |
324 | | template <typename Cmp = std::less<>> |
325 | | using comparator_indexing = detail::indexing<void, Cmp>; |
326 | | |
327 | | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
328 | | // ARRAY // |
329 | | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
330 | | template <typename E, typename V, typename Index = default_indexing<E>> |
331 | | struct array { |
332 | | static_assert(std::is_enum_v<E>); |
333 | | static_assert(std::is_trivially_constructible_v<Index>); |
334 | | static_assert(enum_count<E>() == 0 || Index::at(enum_values<E>().front())); // check Index is constexpr |
335 | | |
336 | | using index_type = Index; |
337 | | using container_type = std::array<V, enum_count<E>()>; |
338 | | |
339 | | using value_type = typename container_type::value_type; |
340 | | using size_type = typename container_type::size_type; |
341 | | using difference_type = typename container_type::difference_type; |
342 | | using reference = typename container_type::reference; |
343 | | using const_reference = typename container_type::const_reference; |
344 | | using pointer = typename container_type::pointer; |
345 | | using const_pointer = typename container_type::const_pointer; |
346 | | using iterator = typename container_type::iterator; |
347 | | using const_iterator = typename container_type::const_iterator; |
348 | | using reverse_iterator = typename container_type::reverse_iterator; |
349 | | using const_reverse_iterator = typename container_type::const_reverse_iterator; |
350 | | |
351 | | constexpr reference at(E pos) { |
352 | | if (auto index = index_type::at(pos)) { |
353 | | return a[*index]; |
354 | | } |
355 | | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::array::at Unrecognized position")); |
356 | | } |
357 | | |
358 | | constexpr const_reference at(E pos) const { |
359 | | if (auto index = index_type::at(pos)) { |
360 | | return a[*index]; |
361 | | } |
362 | | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::array::at: Unrecognized position")); |
363 | | } |
364 | | |
365 | | [[nodiscard]] constexpr reference operator[](E pos) { |
366 | | auto i = index_type::at(pos); |
367 | | return MAGIC_ENUM_ASSERT(i), a[*i]; |
368 | | } |
369 | | |
370 | | [[nodiscard]] constexpr const_reference operator[](E pos) const { |
371 | | auto i = index_type::at(pos); |
372 | | return MAGIC_ENUM_ASSERT(i), a[*i]; |
373 | | } |
374 | | |
375 | | [[nodiscard]] constexpr reference front() noexcept { return a.front(); } |
376 | | |
377 | | [[nodiscard]] constexpr const_reference front() const noexcept { return a.front(); } |
378 | | |
379 | | [[nodiscard]] constexpr reference back() noexcept { return a.back(); } |
380 | | |
381 | | [[nodiscard]] constexpr const_reference back() const noexcept { return a.back(); } |
382 | | |
383 | | [[nodiscard]] constexpr pointer data() noexcept { return a.data(); } |
384 | | |
385 | | [[nodiscard]] constexpr const_pointer data() const noexcept { return a.data(); } |
386 | | |
387 | | [[nodiscard]] constexpr iterator begin() noexcept { return a.begin(); } |
388 | | |
389 | | [[nodiscard]] constexpr const_iterator begin() const noexcept { return a.begin(); } |
390 | | |
391 | | [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return a.cbegin(); } |
392 | | |
393 | | [[nodiscard]] constexpr iterator end() noexcept { return a.end(); } |
394 | | |
395 | | [[nodiscard]] constexpr const_iterator end() const noexcept { return a.end(); } |
396 | | |
397 | | [[nodiscard]] constexpr const_iterator cend() const noexcept { return a.cend(); } |
398 | | |
399 | | [[nodiscard]] constexpr iterator rbegin() noexcept { return a.rbegin(); } |
400 | | |
401 | | [[nodiscard]] constexpr const_iterator rbegin() const noexcept { return a.rbegin(); } |
402 | | |
403 | | [[nodiscard]] constexpr const_iterator crbegin() const noexcept { return a.crbegin(); } |
404 | | |
405 | | [[nodiscard]] constexpr iterator rend() noexcept { return a.rend(); } |
406 | | |
407 | | [[nodiscard]] constexpr const_iterator rend() const noexcept { return a.rend(); } |
408 | | |
409 | | [[nodiscard]] constexpr const_iterator crend() const noexcept { return a.crend(); } |
410 | | |
411 | | [[nodiscard]] constexpr bool empty() const noexcept { return a.empty(); } |
412 | | |
413 | | [[nodiscard]] constexpr size_type size() const noexcept { return a.size(); } |
414 | | |
415 | | [[nodiscard]] constexpr size_type max_size() const noexcept { return a.max_size(); } |
416 | | |
417 | | constexpr void fill(const V& value) { |
418 | | for (auto& v : a) { |
419 | | v = value; |
420 | | } |
421 | | } |
422 | | |
423 | | constexpr void swap(array& other) noexcept(std::is_nothrow_swappable_v<V>) { |
424 | | for (std::size_t i = 0; i < a.size(); ++i) { |
425 | | auto v = std::move(other.a[i]); |
426 | | other.a[i] = std::move(a[i]); |
427 | | a[i] = std::move(v); |
428 | | } |
429 | | } |
430 | | |
431 | | [[nodiscard]] friend constexpr bool operator==(const array& a1, const array& a2) { return detail::equal(a1, a2); } |
432 | | |
433 | | [[nodiscard]] friend constexpr bool operator!=(const array& a1, const array& a2) { return !detail::equal(a1, a2); } |
434 | | |
435 | | [[nodiscard]] friend constexpr bool operator<(const array& a1, const array& a2) { return detail::lexicographical_compare(a1, a2); } |
436 | | |
437 | | [[nodiscard]] friend constexpr bool operator<=(const array& a1, const array& a2) { return !detail::lexicographical_compare(a2, a1); } |
438 | | |
439 | | [[nodiscard]] friend constexpr bool operator>(const array& a1, const array& a2) { return detail::lexicographical_compare(a2, a1); } |
440 | | |
441 | | [[nodiscard]] friend constexpr bool operator>=(const array& a1, const array& a2) { return !detail::lexicographical_compare(a1, a2); } |
442 | | |
443 | | container_type a; |
444 | | }; |
445 | | |
446 | | namespace detail { |
447 | | |
448 | | template <typename E, typename T, std::size_t N, std::size_t... I> |
449 | | constexpr array<E, std::remove_cv_t<T>> to_array_impl(T (&a)[N], std::index_sequence<I...>) { |
450 | | return {{a[I]...}}; |
451 | | } |
452 | | |
453 | | template <typename E, typename T, std::size_t N, std::size_t... I> |
454 | | constexpr array<E, std::remove_cv_t<T>> to_array_impl(T(&&a)[N], std::index_sequence<I...>) { |
455 | | return {{std::move(a[I])...}}; |
456 | | } |
457 | | |
458 | | } // namespace detail |
459 | | |
460 | | template <typename E, typename T, std::size_t N> |
461 | | constexpr std::enable_if_t<(enum_count<E>() == N), array<E, std::remove_cv_t<T>>> to_array(T (&a)[N]) { |
462 | | return detail::to_array_impl<E>(a, std::make_index_sequence<N>{}); |
463 | | } |
464 | | |
465 | | template <typename E, typename T, std::size_t N> |
466 | | constexpr std::enable_if_t<(enum_count<E>() == N), array<E, std::remove_cv_t<T>>> to_array(T(&&a)[N]) { |
467 | | return detail::to_array_impl<E>(std::move(a), std::make_index_sequence<N>{}); |
468 | | } |
469 | | |
470 | | template <typename E, typename... Ts> |
471 | | 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) { |
472 | | return {{std::forward<Ts>(ts)...}}; |
473 | | } |
474 | | |
475 | | inline constexpr detail::raw_access_t raw_access{}; |
476 | | |
477 | | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
478 | | // BITSET // |
479 | | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
480 | | template <typename E, typename Index = default_indexing<E>> |
481 | | class bitset { |
482 | | static_assert(std::is_enum_v<E>); |
483 | | static_assert(std::is_trivially_constructible_v<Index>); |
484 | | static_assert(enum_count<E>() == 0 || Index::at(enum_values<E>().front())); // check Index is constexpr |
485 | | |
486 | | using base_type = std::conditional_t<enum_count<E>() <= 8, std::uint_least8_t, |
487 | | std::conditional_t<enum_count<E>() <= 16, std::uint_least16_t, |
488 | | std::conditional_t<enum_count<E>() <= 32, std::uint_least32_t, |
489 | | std::uint_least64_t>>>; |
490 | | |
491 | | static constexpr std::size_t bits_per_base = sizeof(base_type) * 8; |
492 | | static constexpr std::size_t base_type_count = (enum_count<E>() > 0 ? (enum_count<E>() - 1) / bits_per_base + 1 : 0); |
493 | | static constexpr std::size_t not_interested = base_type_count * bits_per_base - enum_count<E>(); |
494 | | static constexpr base_type last_value_max = (base_type{1} << (bits_per_base - not_interested)) - 1; |
495 | | |
496 | | template <typename parent_t = bitset*> |
497 | | class reference_impl { |
498 | | friend class bitset; |
499 | | |
500 | | parent_t parent; |
501 | | std::size_t num_index; |
502 | | base_type bit_index; |
503 | | |
504 | 2.56k | 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)}) {} magic_enum_fuzzer.cc:magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >::reference_impl<magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >*>::reference_impl(magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >*, unsigned long) Line | Count | Source | 504 | 2.04k | 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)}) {} |
magic_enum_fuzzer.cc:magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >::reference_impl<magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> > const*>::reference_impl(magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> > const*, unsigned long) Line | Count | Source | 504 | 512 | 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)}) {} |
|
505 | | |
506 | 2.56k | 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)) {} magic_enum_fuzzer.cc:magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >::reference_impl<magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >*>::reference_impl(magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >*, std::__1::pair<unsigned long, unsigned char>) Line | Count | Source | 506 | 2.04k | 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)) {} |
magic_enum_fuzzer.cc:magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >::reference_impl<magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> > const*>::reference_impl(magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> > const*, std::__1::pair<unsigned long, unsigned char>) Line | Count | Source | 506 | 512 | 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)) {} |
|
507 | | |
508 | | public: |
509 | 2.04k | constexpr reference_impl& operator=(bool v) noexcept { |
510 | 2.04k | if (v) { |
511 | 2.04k | parent->a[num_index] |= bit_index; |
512 | 2.04k | } else { |
513 | 0 | parent->a[num_index] &= ~bit_index; |
514 | 0 | } |
515 | 2.04k | return *this; |
516 | 2.04k | } |
517 | | |
518 | | constexpr reference_impl& operator=(const reference_impl& v) noexcept { |
519 | | if (this == &v) { |
520 | | return *this; |
521 | | } |
522 | | *this = static_cast<bool>(v); |
523 | | return *this; |
524 | | } |
525 | | |
526 | 2.04k | [[nodiscard]] constexpr explicit operator bool() const noexcept { return (parent->a[num_index] & bit_index) > 0; } magic_enum_fuzzer.cc:magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >::reference_impl<magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >*>::operator bool() const Line | Count | Source | 526 | 1.53k | [[nodiscard]] constexpr explicit operator bool() const noexcept { return (parent->a[num_index] & bit_index) > 0; } |
magic_enum_fuzzer.cc:magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> >::reference_impl<magic_enum::containers::bitset<LLVMFuzzerTestOneInput::FuzzEnum, magic_enum::containers::detail::indexing<LLVMFuzzerTestOneInput::FuzzEnum, std::__1::less<LLVMFuzzerTestOneInput::FuzzEnum>, void> > const*>::operator bool() const Line | Count | Source | 526 | 512 | [[nodiscard]] constexpr explicit operator bool() const noexcept { return (parent->a[num_index] & bit_index) > 0; } |
|
527 | | |
528 | | [[nodiscard]] constexpr bool operator~() const noexcept { return !static_cast<bool>(*this); } |
529 | | |
530 | | constexpr reference_impl& flip() noexcept { |
531 | | *this = ~*this; |
532 | | return *this; |
533 | | } |
534 | | }; |
535 | | |
536 | | template <typename T> |
537 | | [[nodiscard]] constexpr T to_(detail::raw_access_t) const { |
538 | | T res{}; |
539 | | T flag{1}; |
540 | | for (std::size_t i = 0; i < size(); ++i, flag <<= 1) { |
541 | | if (const_reference{this, i}) { |
542 | | if (i >= sizeof(T) * 8) { |
543 | | MAGIC_ENUM_THROW(std::overflow_error("magic_enum::containers::bitset::to: Cannot represent enum in this type")); |
544 | | } |
545 | | res |= flag; |
546 | | } |
547 | | } |
548 | | return res; |
549 | | } |
550 | | |
551 | | public: |
552 | | using index_type = Index; |
553 | | using container_type = std::array<base_type, base_type_count>; |
554 | | using reference = reference_impl<>; |
555 | | using const_reference = reference_impl<const bitset*>; |
556 | | |
557 | 1.53k | constexpr explicit bitset(detail::raw_access_t = raw_access) noexcept : a{{}} {} |
558 | | |
559 | | constexpr explicit bitset(detail::raw_access_t, unsigned long long val) : a{{}} { |
560 | | unsigned long long bit{1}; |
561 | | for (std::size_t i = 0; i < (sizeof(val) * 8); ++i, bit <<= 1) { |
562 | | if ((val & bit) > 0) { |
563 | | if (i >= enum_count<E>()) { |
564 | | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::constructor: Upper bit set in raw number")); |
565 | | } |
566 | | |
567 | | reference{this, i} = true; |
568 | | } |
569 | | } |
570 | | } |
571 | | |
572 | | 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')) |
573 | | : a{{}} { |
574 | | std::size_t i = 0; |
575 | | for (auto c : sv.substr(pos, n)) { |
576 | | if (c == one) { |
577 | | if (i >= enum_count<E>()) { |
578 | | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::constructor: Upper bit set in raw string")); |
579 | | } |
580 | | reference{this, i} = true; |
581 | | } else if (c != zero) { |
582 | | MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized character in raw string")); |
583 | | } |
584 | | ++i; |
585 | | } |
586 | | } |
587 | | |
588 | | 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')) |
589 | | : bitset(string_view{str, (std::min)(std::char_traits<char_type>::length(str), n)}, 0, n, zero, one) {} |
590 | | |
591 | | constexpr bitset(std::initializer_list<E> starters) : a{{}} { |
592 | | if constexpr (magic_enum::detail::subtype_v<E> == magic_enum::detail::enum_subtype::flags) { |
593 | | for (auto& f : starters) { |
594 | | *this |= bitset(f); |
595 | | } |
596 | | } else { |
597 | | for (auto& f : starters) { |
598 | | set(f); |
599 | | } |
600 | | } |
601 | | } |
602 | | 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> |
603 | | constexpr explicit bitset(V starter) : a{{}} { |
604 | | auto u = enum_underlying(starter); |
605 | | for (E v : enum_values<E>()) { |
606 | | if (auto ul = enum_underlying(v); (ul & u) != 0) { |
607 | | u &= ~ul; |
608 | | (*this)[v] = true; |
609 | | } |
610 | | } |
611 | | if (u != 0) { |
612 | | MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized enum value in flag")); |
613 | | } |
614 | | } |
615 | | |
616 | | template <typename Cmp = std::equal_to<>> |
617 | | constexpr explicit bitset(string_view sv, Cmp&& cmp = {}, char_type sep = static_cast<char_type>('|')) { |
618 | | for (std::size_t to = 0; (to = magic_enum::detail::find(sv, sep)) != string_view::npos; sv.remove_prefix(to + 1)) { |
619 | | if (auto v = enum_cast<E>(sv.substr(0, to), cmp)) { |
620 | | set(v); |
621 | | } else { |
622 | | MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized enum value in string")); |
623 | | } |
624 | | } |
625 | | if (!sv.empty()) { |
626 | | if (auto v = enum_cast<E>(sv, cmp)) { |
627 | | set(v); |
628 | | } else { |
629 | | MAGIC_ENUM_THROW(std::invalid_argument("magic_enum::containers::bitset::constructor: Unrecognized enum value in string")); |
630 | | } |
631 | | } |
632 | | } |
633 | | |
634 | | [[nodiscard]] friend constexpr bool operator==(const bitset& lhs, const bitset& rhs) noexcept { return detail::equal(lhs.a, rhs.a); } |
635 | | |
636 | | [[nodiscard]] friend constexpr bool operator!=(const bitset& lhs, const bitset& rhs) noexcept { return !detail::equal(lhs.a, rhs.a); } |
637 | | |
638 | 512 | [[nodiscard]] constexpr bool operator[](E pos) const { |
639 | 512 | auto i = index_type::at(pos); |
640 | 512 | return MAGIC_ENUM_ASSERT(i), static_cast<bool>(const_reference(this, *i)); |
641 | 512 | } |
642 | | |
643 | 1.53k | [[nodiscard]] constexpr reference operator[](E pos) { |
644 | 1.53k | auto i = index_type::at(pos); |
645 | 1.53k | return MAGIC_ENUM_ASSERT(i), reference{this, *i}; |
646 | 1.53k | } |
647 | | |
648 | | constexpr bool test(E pos) const { |
649 | | if (auto i = index_type::at(pos)) { |
650 | | return static_cast<bool>(const_reference(this, *i)); |
651 | | } |
652 | | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::test: Unrecognized position")); |
653 | | } |
654 | | |
655 | 512 | [[nodiscard]] constexpr bool all() const noexcept { |
656 | 512 | if constexpr (base_type_count == 0) { |
657 | 512 | return true; |
658 | 512 | } |
659 | | |
660 | 512 | for (std::size_t i = 0; i < base_type_count - (not_interested > 0); ++i) { |
661 | 0 | auto check = ~a[i]; |
662 | 0 | if (check) { |
663 | 0 | return false; |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | 512 | if constexpr (not_interested > 0) { |
668 | 512 | return a[base_type_count - 1] == last_value_max; |
669 | 512 | } |
670 | 512 | } |
671 | | |
672 | 1.02k | [[nodiscard]] constexpr bool any() const noexcept { |
673 | 1.02k | for (auto& v : a) { |
674 | 1.02k | if (v > 0) { |
675 | 1.02k | return true; |
676 | 1.02k | } |
677 | 1.02k | } |
678 | 0 | return false; |
679 | 1.02k | } |
680 | | |
681 | 512 | [[nodiscard]] constexpr bool none() const noexcept { return !any(); } |
682 | | |
683 | 512 | [[nodiscard]] constexpr std::size_t count() const noexcept { |
684 | 512 | std::size_t c = 0; |
685 | 512 | for (auto& v : a) { |
686 | 512 | c += detail::popcount(v); |
687 | 512 | } |
688 | 512 | return c; |
689 | 512 | } |
690 | | |
691 | 512 | [[nodiscard]] constexpr std::size_t size() const noexcept { return enum_count<E>(); } |
692 | | |
693 | | [[nodiscard]] constexpr std::size_t max_size() const noexcept { return enum_count<E>(); } |
694 | | |
695 | | constexpr bitset& operator&=(const bitset& other) noexcept { |
696 | | for (std::size_t i = 0; i < base_type_count; ++i) { |
697 | | a[i] &= other.a[i]; |
698 | | } |
699 | | return *this; |
700 | | } |
701 | | |
702 | | constexpr bitset& operator|=(const bitset& other) noexcept { |
703 | | for (std::size_t i = 0; i < base_type_count; ++i) { |
704 | | a[i] |= other.a[i]; |
705 | | } |
706 | | return *this; |
707 | | } |
708 | | |
709 | | constexpr bitset& operator^=(const bitset& other) noexcept { |
710 | | for (std::size_t i = 0; i < base_type_count; ++i) { |
711 | | a[i] ^= other.a[i]; |
712 | | } |
713 | | return *this; |
714 | | } |
715 | | |
716 | | [[nodiscard]] constexpr bitset operator~() const noexcept { |
717 | | bitset res; |
718 | | for (std::size_t i = 0; i < base_type_count - (not_interested > 0); ++i) { |
719 | | res.a[i] = ~a[i]; |
720 | | } |
721 | | |
722 | | if constexpr (not_interested > 0) { |
723 | | res.a[base_type_count - 1] = ~a[base_type_count - 1] & last_value_max; |
724 | | } |
725 | | return res; |
726 | | } |
727 | | |
728 | | constexpr bitset& set() noexcept { |
729 | | for (std::size_t i = 0; i < base_type_count - (not_interested > 0); ++i) { |
730 | | a[i] = ~base_type{0}; |
731 | | } |
732 | | |
733 | | if constexpr (not_interested > 0) { |
734 | | a[base_type_count - 1] = last_value_max; |
735 | | } |
736 | | return *this; |
737 | | } |
738 | | |
739 | 512 | constexpr bitset& set(E pos, bool value = true) { |
740 | 512 | if (auto i = index_type::at(pos)) { |
741 | 512 | reference{this, *i} = value; |
742 | 512 | return *this; |
743 | 512 | } |
744 | 512 | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::set: Unrecognized position")); |
745 | 512 | } |
746 | | |
747 | 512 | constexpr bitset& reset() noexcept { return *this = bitset{}; } |
748 | | |
749 | | constexpr bitset& reset(E pos) { |
750 | | if (auto i = index_type::at(pos)) { |
751 | | reference{this, *i} = false; |
752 | | return *this; |
753 | | } |
754 | | MAGIC_ENUM_THROW(std::out_of_range("magic_enum::containers::bitset::reset: Unrecognized position")); |
755 | | } |
756 | | |
757 | | constexpr bitset& flip() noexcept { return *this = ~*this; } |
758 | | |
759 | | [[nodiscard]] friend constexpr bitset operator&(const bitset& lhs, const bitset& rhs) noexcept { |
760 | | bitset cp = lhs; |
761 | | cp &= rhs; |
762 | | return cp; |
763 | | } |
764 | | |
765 | | [[nodiscard]] friend constexpr bitset operator|(const bitset& lhs, const bitset& rhs) noexcept { |
766 | | bitset cp = lhs; |
767 | | cp |= rhs; |
768 | | return cp; |
769 | | } |
770 | | |
771 | | [[nodiscard]] friend constexpr bitset operator^(const bitset& lhs, const bitset& rhs) noexcept { |
772 | | bitset cp = lhs; |
773 | | cp ^= rhs; |
774 | | return cp; |
775 | | } |
776 | | |
777 | | template <typename V = E> |
778 | | [[nodiscard]] constexpr explicit operator std::enable_if_t<magic_enum::detail::subtype_v<V> == magic_enum::detail::enum_subtype::flags, E>() const { |
779 | | E res{}; |
780 | | for (const auto& e : enum_values<E>()) { |
781 | | if (test(e)) { |
782 | | res |= e; |
783 | | } |
784 | | } |
785 | | return res; |
786 | | } |
787 | | |
788 | | [[nodiscard]] string to_string(char_type sep = static_cast<char_type>('|')) const { |
789 | | string name; |
790 | | |
791 | | for (const auto& e : enum_values<E>()) { |
792 | | if (test(e)) { |
793 | | if (!name.empty()) { |
794 | | name.append(1, sep); |
795 | | } |
796 | | auto n = enum_name(e); |
797 | | name.append(n.data(), n.size()); |
798 | | } |
799 | | } |
800 | | return name; |
801 | | } |
802 | | |
803 | | [[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 { |
804 | | string name; |
805 | | name.reserve(size()); |
806 | | for (std::size_t i = 0; i < size(); ++i) { |
807 | | name.append(1, const_reference{this, i} ? one : zero); |
808 | | } |
809 | | return name; |
810 | | } |
811 | | |
812 | | [[nodiscard]] constexpr unsigned long long to_ullong(detail::raw_access_t raw) const { return to_<unsigned long long>(raw); } |
813 | | |
814 | | [[nodiscard]] constexpr unsigned long long to_ulong(detail::raw_access_t raw) const { return to_<unsigned long>(raw); } |
815 | | |
816 | | friend std::ostream& operator<<(std::ostream& o, const bitset& bs) { return o << bs.to_string(); } |
817 | | |
818 | | friend std::istream& operator>>(std::istream& i, bitset& bs) { |
819 | | string s; |
820 | | if (i >> s; !s.empty()) { |
821 | | bs = bitset(string_view{s}); |
822 | | } |
823 | | return i; |
824 | | } |
825 | | |
826 | | private: |
827 | | container_type a; |
828 | | }; |
829 | | |
830 | | template <typename V, int = 0> |
831 | | explicit bitset(V starter) -> bitset<V>; |
832 | | |
833 | | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
834 | | // SET // |
835 | | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
836 | | template <typename E, typename Cmp = std::less<E>> |
837 | | class set { |
838 | | using index_type = detail::indexing<E, Cmp>; |
839 | | struct Getter { |
840 | | constexpr const E& operator()(const set*, const E* p) const noexcept { return *p; } |
841 | | }; |
842 | | struct Predicate { |
843 | 512 | constexpr bool operator()(const set* h, const E* e) const noexcept { return h->a[*e]; } |
844 | | }; |
845 | | |
846 | | public: |
847 | | using container_type = bitset<E, index_type>; |
848 | | using key_type = E; |
849 | | using value_type = E; |
850 | | using size_type = std::size_t; |
851 | | using difference_type = std::ptrdiff_t; |
852 | | using key_compare = Cmp; |
853 | | using value_compare = Cmp; |
854 | | using reference = value_type&; |
855 | | using const_reference = const value_type&; |
856 | | using pointer = value_type*; |
857 | | using const_pointer = const value_type*; |
858 | | using iterator = detail::FilteredIterator<const set*, const E*, Getter, Predicate>; |
859 | | using const_iterator = detail::FilteredIterator<const set*, const E*, Getter, Predicate>; |
860 | | using reverse_iterator = std::reverse_iterator<iterator>; |
861 | | using const_reverse_iterator = std::reverse_iterator<const_iterator>; |
862 | | |
863 | | constexpr set() noexcept = default; |
864 | | |
865 | | template <typename InputIt> |
866 | | constexpr set(InputIt first, InputIt last) { |
867 | | while (first != last) { |
868 | | insert(*first++); |
869 | | } |
870 | | } |
871 | | |
872 | 512 | constexpr set(std::initializer_list<E> ilist) { |
873 | 1.02k | for (auto e : ilist) { |
874 | 1.02k | insert(e); |
875 | 1.02k | } |
876 | 512 | } |
877 | | 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> |
878 | | constexpr explicit set(V starter) { |
879 | | auto u = enum_underlying(starter); |
880 | | for (E v : enum_values<E>()) { |
881 | | if ((enum_underlying(v) & u) != 0) { |
882 | | insert(v); |
883 | | } |
884 | | } |
885 | | } |
886 | | |
887 | | constexpr set(const set&) noexcept = default; |
888 | | constexpr set(set&&) noexcept = default; |
889 | | |
890 | | constexpr set& operator=(const set&) noexcept = default; |
891 | | constexpr set& operator=(set&&) noexcept = default; |
892 | | constexpr set& operator=(std::initializer_list<E> ilist) { |
893 | | for (auto e : ilist) { |
894 | | insert(e); |
895 | | } |
896 | | } |
897 | | |
898 | | constexpr const_iterator begin() const noexcept { |
899 | | return const_iterator{this, index_type::begin(), index_type::end(), index_type::begin()}; |
900 | | } |
901 | | |
902 | 0 | constexpr const_iterator end() const noexcept { |
903 | 0 | return const_iterator{this, index_type::begin(), index_type::end(), index_type::end()}; |
904 | 0 | } |
905 | | |
906 | | constexpr const_iterator cbegin() const noexcept { return begin(); } |
907 | | |
908 | | constexpr const_iterator cend() const noexcept { return end(); } |
909 | | |
910 | | constexpr const_reverse_iterator rbegin() const noexcept { return {end()}; } |
911 | | |
912 | | constexpr const_reverse_iterator rend() const noexcept { return {begin()}; } |
913 | | |
914 | | constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } |
915 | | |
916 | | constexpr const_reverse_iterator crend() const noexcept { return rend(); } |
917 | | |
918 | 512 | [[nodiscard]] constexpr bool empty() const noexcept { return s == 0; } |
919 | | |
920 | 512 | [[nodiscard]] constexpr size_type size() const noexcept { return s; } |
921 | | |
922 | | [[nodiscard]] constexpr size_type max_size() const noexcept { return a.max_size(); } |
923 | | |
924 | 512 | constexpr void clear() noexcept { |
925 | 512 | a.reset(); |
926 | 512 | s = 0; |
927 | 512 | } |
928 | | |
929 | 1.53k | constexpr std::pair<iterator, bool> insert(const value_type& value) noexcept { |
930 | 1.53k | if (auto i = index_type::at(value)) { |
931 | 1.53k | typename container_type::reference ref = a[value]; |
932 | 1.53k | bool r = !ref; |
933 | 1.53k | if (r) { |
934 | 1.53k | ref = true; |
935 | 1.53k | ++s; |
936 | 1.53k | } |
937 | | |
938 | 1.53k | return {iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)}, r}; |
939 | 1.53k | } |
940 | 0 | return {end(), false}; |
941 | 1.53k | } |
942 | | |
943 | 512 | constexpr std::pair<iterator, bool> insert(value_type&& value) noexcept { return insert(value); } |
944 | | |
945 | | constexpr iterator insert(const_iterator, const value_type& value) noexcept { return insert(value).first; } |
946 | | |
947 | | constexpr iterator insert(const_iterator hint, value_type&& value) noexcept { return insert(hint, value); } |
948 | | |
949 | | template <typename InputIt> |
950 | | constexpr void insert(InputIt first, InputIt last) noexcept { |
951 | | while (first != last) { |
952 | | insert(*first++); |
953 | | } |
954 | | } |
955 | | |
956 | | constexpr void insert(std::initializer_list<value_type> ilist) noexcept { |
957 | | for (auto v : ilist) { |
958 | | insert(v); |
959 | | } |
960 | | } |
961 | | |
962 | | template <typename... Args> |
963 | | constexpr std::pair<iterator, bool> emplace(Args&&... args) noexcept { |
964 | | return insert({std::forward<Args>(args)...}); |
965 | | } |
966 | | |
967 | | template <typename... Args> |
968 | | constexpr iterator emplace_hint(const_iterator, Args&&... args) noexcept { |
969 | | return emplace(std::forward<Args>(args)...).first; |
970 | | } |
971 | | |
972 | | constexpr iterator erase(const_iterator pos) noexcept { |
973 | | erase(*pos++); |
974 | | return pos; |
975 | | } |
976 | | |
977 | | constexpr iterator erase(const_iterator first, const_iterator last) noexcept { |
978 | | while ((first = erase(first)) != last) { |
979 | | } |
980 | | return first; |
981 | | } |
982 | | |
983 | | constexpr size_type erase(const key_type& key) noexcept { |
984 | | typename container_type::reference ref = a[key]; |
985 | | bool res = ref; |
986 | | if (res) { |
987 | | --s; |
988 | | } |
989 | | ref = false; |
990 | | return res; |
991 | | } |
992 | | |
993 | | template <typename K, typename KC = key_compare> |
994 | | constexpr std::enable_if_t<detail::is_transparent_v<KC>, size_type> erase(K&& x) noexcept { |
995 | | size_type c = 0; |
996 | | for (auto [first, last] = detail::equal_range(index_type::begin(), index_type::end(), x, key_compare{}); first != last;) { |
997 | | c += erase(*first++); |
998 | | } |
999 | | return c; |
1000 | | } |
1001 | | |
1002 | | void swap(set& other) noexcept { |
1003 | | set cp = *this; |
1004 | | *this = other; |
1005 | | other = cp; |
1006 | | } |
1007 | | |
1008 | | [[nodiscard]] constexpr size_type count(const key_type& key) const noexcept { return index_type::at(key) && a[key]; } |
1009 | | |
1010 | | template <typename K, typename KC = key_compare> |
1011 | | [[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, size_type> count(const K& x) const { |
1012 | | size_type c = 0; |
1013 | | for (auto [first, last] = detail::equal_range(index_type::begin(), index_type::end(), x, key_compare{}); first != last; ++first) { |
1014 | | c += count(*first); |
1015 | | } |
1016 | | return c; |
1017 | | } |
1018 | | |
1019 | | [[nodiscard]] constexpr const_iterator find(const key_type& key) const noexcept { |
1020 | | if (auto i = index_type::at(key); i && a.test(key)) { |
1021 | | return const_iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)}; |
1022 | | } |
1023 | | return end(); |
1024 | | } |
1025 | | |
1026 | | template <typename K, typename KC = key_compare> |
1027 | | [[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, const_iterator> find(const K& x) const { |
1028 | | for (auto [first, last] = detail::equal_range(index_type::begin(), index_type::end(), x, key_compare{}); first != last; ++first) { |
1029 | | if (a.test(*first)) { |
1030 | | return find(*first); |
1031 | | } |
1032 | | } |
1033 | | return end(); |
1034 | | } |
1035 | | |
1036 | | [[nodiscard]] constexpr bool contains(const key_type& key) const noexcept { return count(key); } |
1037 | | |
1038 | | template <typename K, typename KC = key_compare> |
1039 | | [[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, bool> contains(const K& x) const noexcept { |
1040 | | return count(x) > 0; |
1041 | | } |
1042 | | |
1043 | | [[nodiscard]] constexpr std::pair<const_iterator, const_iterator> equal_range(const key_type& key) const noexcept { return {lower_bound(key), upper_bound(key)}; } |
1044 | | |
1045 | | template <typename K, typename KC = key_compare> |
1046 | | [[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, std::pair<const_iterator, const_iterator>> equal_range(const K& x) const noexcept { |
1047 | | return {lower_bound(x), upper_bound(x)}; |
1048 | | } |
1049 | | |
1050 | | [[nodiscard]] constexpr const_iterator lower_bound(const key_type& key) const noexcept { |
1051 | | if (auto i = index_type::at(key)) { |
1052 | | auto it = const_iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)}; |
1053 | | return a.test(key) ? it : std::next(it); |
1054 | | } |
1055 | | return end(); |
1056 | | } |
1057 | | |
1058 | | template <typename K, typename KC = key_compare> |
1059 | | [[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, const_iterator> lower_bound(const K& x) const noexcept { |
1060 | | auto [first, last] = detail::equal_range(index_type::begin(), index_type::end(), x, key_compare{}); |
1061 | | return first != last ? lower_bound(*first) : end(); |
1062 | | } |
1063 | | |
1064 | | [[nodiscard]] constexpr const_iterator upper_bound(const key_type& key) const noexcept { |
1065 | | if (auto i = index_type::at(key)) { |
1066 | | return std::next(const_iterator{this, index_type::begin(), index_type::end(), index_type::it(*i)}); |
1067 | | } |
1068 | | return end(); |
1069 | | } |
1070 | | |
1071 | | template <typename K, typename KC = key_compare> |
1072 | | [[nodiscard]] constexpr std::enable_if_t<detail::is_transparent_v<KC>, const_iterator> upper_bound(const K& x) const noexcept { |
1073 | | auto [first, last] = detail::equal_range(index_type::begin(), index_type::end(), x, key_compare{}); |
1074 | | return first != last ? upper_bound(*std::prev(last)) : end(); |
1075 | | } |
1076 | | |
1077 | | [[nodiscard]] constexpr key_compare key_comp() const { return {}; } |
1078 | | |
1079 | | [[nodiscard]] constexpr value_compare value_comp() const { return {}; } |
1080 | | |
1081 | | [[nodiscard]] constexpr friend bool operator==(const set& lhs, const set& rhs) noexcept { return lhs.a == rhs.a; } |
1082 | | |
1083 | | [[nodiscard]] constexpr friend bool operator!=(const set& lhs, const set& rhs) noexcept { return lhs.a != rhs.a; } |
1084 | | |
1085 | | [[nodiscard]] constexpr friend bool operator<(const set& lhs, const set& rhs) noexcept { |
1086 | | if (lhs.s < rhs.s) { |
1087 | | return true; |
1088 | | } |
1089 | | if (rhs.s < lhs.s) { |
1090 | | return false; |
1091 | | } |
1092 | | |
1093 | | for (auto it = index_type::begin(); it != index_type::end(); ++it) { |
1094 | | if (auto c = rhs.contains(*it); c != lhs.contains(*it)) { |
1095 | | return c; |
1096 | | } |
1097 | | } |
1098 | | return false; |
1099 | | } |
1100 | | |
1101 | | [[nodiscard]] constexpr friend bool operator<=(const set& lhs, const set& rhs) noexcept { return !(rhs < lhs); } |
1102 | | |
1103 | | [[nodiscard]] constexpr friend bool operator>(const set& lhs, const set& rhs) noexcept { return rhs < lhs; } |
1104 | | |
1105 | | [[nodiscard]] constexpr friend bool operator>=(const set& lhs, const set& rhs) noexcept { return !(lhs < rhs); } |
1106 | | |
1107 | | template <typename Pred> |
1108 | | size_type erase_if(Pred pred) { |
1109 | | auto old_size = size(); |
1110 | | for (auto i = begin(), last = end(); i != last;) { |
1111 | | if (pred(*i)) { |
1112 | | i = erase(i); |
1113 | | } else { |
1114 | | ++i; |
1115 | | } |
1116 | | } |
1117 | | return old_size - size(); |
1118 | | } |
1119 | | |
1120 | | private: |
1121 | | container_type a; |
1122 | | std::size_t s = 0; |
1123 | | }; |
1124 | | |
1125 | | template <typename V, int = 0> |
1126 | | explicit set(V starter) -> set<V>; |
1127 | | |
1128 | | } // namespace magic_enum::containers |
1129 | | |
1130 | | namespace std { |
1131 | | |
1132 | | template <auto I, typename E, typename V, typename Index> |
1133 | | constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < magic_enum::enum_count<E>()), V&> get(magic_enum::containers::array<E, V, Index>& a) noexcept { |
1134 | | return a.a[I]; |
1135 | | } |
1136 | | |
1137 | | template <auto I, typename E, typename V, typename Index> |
1138 | | constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < magic_enum::enum_count<E>()), V&&> get(magic_enum::containers::array<E, V, Index>&& a) noexcept { |
1139 | | return std::move(a.a[I]); |
1140 | | } |
1141 | | |
1142 | | template <auto I, typename E, typename V, typename Index> |
1143 | | constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < magic_enum::enum_count<E>()), const V&> get(const magic_enum::containers::array<E, V, Index>& a) noexcept { |
1144 | | return a.a[I]; |
1145 | | } |
1146 | | |
1147 | | template <auto I, typename E, typename V, typename Index> |
1148 | | constexpr std::enable_if_t<(std::is_integral_v<decltype(I)> && I < magic_enum::enum_count<E>()), const V&&> get(const magic_enum::containers::array<E, V, Index>&& a) noexcept { |
1149 | | return std::move(a.a[I]); |
1150 | | } |
1151 | | |
1152 | | template <auto Enum, typename E, typename V, typename Index> |
1153 | | constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && magic_enum::enum_contains(Enum), V&> get(magic_enum::containers::array<E, V, Index>& a) noexcept { |
1154 | | return a[Enum]; |
1155 | | } |
1156 | | |
1157 | | template <auto Enum, typename E, typename V, typename Index> |
1158 | | constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && magic_enum::enum_contains(Enum), V&&> get(magic_enum::containers::array<E, V, Index>&& a) noexcept { |
1159 | | return std::move(a[Enum]); |
1160 | | } |
1161 | | |
1162 | | template <auto Enum, typename E, typename V, typename Index> |
1163 | | constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && magic_enum::enum_contains(Enum), const V&> get(const magic_enum::containers::array<E, V, Index>& a) noexcept { |
1164 | | return a[Enum]; |
1165 | | } |
1166 | | |
1167 | | template <auto Enum, typename E, typename V, typename Index> |
1168 | | constexpr std::enable_if_t<std::is_same_v<decltype(Enum), E> && magic_enum::enum_contains(Enum), const V&&> get(const magic_enum::containers::array<E, V, Index>&& a) noexcept { |
1169 | | return std::move(a[Enum]); |
1170 | | } |
1171 | | |
1172 | | } // namespace std |
1173 | | |
1174 | | #endif // NEARGYE_MAGIC_ENUM_CONTAINERS_HPP |