|
8 | 8 |
|
9 | 9 | #include <concepts> |
10 | 10 | #include <ranges> |
| 11 | +#include <utility> |
11 | 12 |
|
12 | 13 | namespace power_grid_model { |
13 | | -template <class Impl, typename ValueType, std::integral DifferenceType> class IteratorFacade { |
| 14 | +namespace detail { |
| 15 | +template <typename T> |
| 16 | +concept iterator_facadeable_c = std::integral<typename T::difference_type> && std::is_pointer_v<typename T::pointer> && |
| 17 | + std::is_lvalue_reference_v<typename T::reference> && |
| 18 | + requires(T t, std::remove_cvref_t<T> mt, std::add_const_t<T> ct, |
| 19 | + std::add_const_t<T> ct2, typename std::remove_cvref_t<T>::difference_type d) { |
| 20 | + typename T::value_type; |
| 21 | + { *t } -> std::same_as<typename T::reference>; |
| 22 | + { &*t } -> std::same_as<typename T::pointer>; |
| 23 | + { ct <=> ct2 } -> std::same_as<std::strong_ordering>; |
| 24 | + { mt += d } -> std::same_as<std::add_lvalue_reference_t<T>>; |
| 25 | + { ct - ct2 } -> std::same_as<typename T::difference_type>; |
| 26 | + }; |
| 27 | + |
| 28 | +template <typename T, typename Int> |
| 29 | +concept compound_assignable_c = requires(T t, Int n) { |
| 30 | + { t += n } -> std::same_as<std::add_lvalue_reference_t<T>>; |
| 31 | +}; |
| 32 | +} // namespace detail |
| 33 | + |
| 34 | +class IteratorFacade { |
14 | 35 | public: |
15 | | - using iterator = Impl; // CRTP |
16 | | - using const_iterator = std::add_const_t<iterator>; |
17 | | - using value_type = std::remove_cvref_t<ValueType>; |
18 | | - using difference_type = DifferenceType; |
19 | 36 | using iterator_category = std::random_access_iterator_tag; |
20 | | - using pointer = std::add_pointer_t<ValueType>; |
21 | | - using reference = std::add_lvalue_reference_t<ValueType>; |
22 | 37 |
|
23 | | - constexpr auto operator*() const -> decltype(auto) { return static_cast<const_iterator*>(this)->dereference(); } |
24 | | - constexpr auto operator*() -> reference |
25 | | - requires requires(iterator it) { |
26 | | - { it.dereference() } -> std::same_as<reference>; |
27 | | - } |
28 | | - { |
29 | | - return static_cast<iterator*>(this)->dereference(); |
| 38 | + template <typename Self> constexpr decltype(auto) operator->(this Self&& self) { |
| 39 | + return &(*std::forward<Self>(self)); |
30 | 40 | } |
31 | | - constexpr auto operator->() const -> decltype(auto) { return &(*(*this)); } |
32 | | - constexpr auto operator->() -> decltype(auto) { return &(*(*this)); } |
33 | 41 |
|
34 | | - friend constexpr bool operator==(IteratorFacade const& first, IteratorFacade const& second) { |
35 | | - return (first <=> second) == std::strong_ordering::equivalent; |
36 | | - } |
37 | | - friend constexpr std::strong_ordering operator<=>(IteratorFacade const& first, IteratorFacade const& second) { |
38 | | - return first.three_way_compare(second); |
| 42 | + template <typename Self, typename Other> |
| 43 | + requires std::same_as<std::remove_cvref_t<Self>, std::remove_cvref_t<Other>> |
| 44 | + constexpr bool operator==(this Self const& self, Other const& other) { |
| 45 | + return (self <=> other) == std::strong_ordering::equivalent; |
39 | 46 | } |
40 | 47 |
|
41 | | - constexpr auto operator++() -> iterator& { |
42 | | - if constexpr (requires(iterator it) { it.increment(); }) { |
43 | | - static_cast<iterator*>(this)->increment(); |
| 48 | + // NOLINTNEXTLINE(cert-dcl21-cpp) // pre-decrement but clang-tidy incorrectly sees this as post-decrement |
| 49 | + template <typename Self> constexpr std::add_lvalue_reference_t<Self> operator++(this Self& self) { |
| 50 | + if constexpr (requires { self.increment(); }) { // NOTE: IteratorFacade should be a friend class |
| 51 | + self.increment(); |
44 | 52 | } else { |
45 | | - static_cast<iterator*>(this)->advance(1); |
| 53 | + return (self += 1); |
46 | 54 | } |
47 | | - return *static_cast<iterator*>(this); |
| 55 | + return self; |
48 | 56 | } |
49 | | - constexpr auto operator--() -> iterator& { |
50 | | - if constexpr (requires(iterator it) { it.decrement(); }) { |
51 | | - static_cast<iterator*>(this)->decrement(); |
| 57 | + // NOLINTNEXTLINE(cert-dcl21-cpp) // pre-decrement but clang-tidy incorrectly sees this as post-decrement |
| 58 | + template <typename Self> constexpr std::add_lvalue_reference_t<Self> operator--(this Self& self) { |
| 59 | + if constexpr (requires { self.decrement(); }) { // NOTE: IteratorFacade should be a friend class |
| 60 | + self.decrement(); |
52 | 61 | } else { |
53 | | - static_cast<iterator*>(this)->advance(-1); |
| 62 | + return (self += -1); |
54 | 63 | } |
55 | | - return *static_cast<iterator*>(this); |
| 64 | + return self; |
56 | 65 | } |
57 | | - constexpr auto operator++(std::integral auto /*idx*/) -> iterator { |
58 | | - iterator result{*static_cast<iterator*>(this)}; |
59 | | - ++(*this); |
| 66 | + template <typename Self> |
| 67 | + constexpr std::remove_cvref_t<Self> operator++(this Self& self, std::integral auto /*idx*/) { |
| 68 | + using Result = std::remove_cvref_t<Self>; |
| 69 | + Result result{self}; |
| 70 | + ++self; |
60 | 71 | return result; |
61 | 72 | } |
62 | | - constexpr auto operator--(std::integral auto /*idx*/) -> iterator { |
63 | | - iterator result{*static_cast<iterator*>(this)}; |
64 | | - --(*this); |
| 73 | + template <typename Self> |
| 74 | + constexpr std::remove_cvref_t<Self> operator--(this Self& self, std::integral auto /*idx*/) { |
| 75 | + using Result = std::remove_cvref_t<Self>; |
| 76 | + Result result{self}; |
| 77 | + --self; |
65 | 78 | return result; |
66 | 79 | } |
67 | | - constexpr auto operator+=(std::integral auto offset) -> iterator& { |
68 | | - static_cast<iterator*>(this)->advance(offset); |
69 | | - return *static_cast<iterator*>(this); |
| 80 | + template <typename Self> |
| 81 | + constexpr std::add_lvalue_reference_t<Self> operator-=(this Self& self, std::integral auto idx) { |
| 82 | + return (self += (-idx)); |
70 | 83 | } |
71 | | - constexpr auto operator-=(std::integral auto idx) -> iterator& { return ((*this) += (-idx)); } |
72 | 84 |
|
73 | | - friend constexpr auto operator+(iterator const& it, difference_type offset) -> iterator { |
74 | | - iterator result{it}; |
| 85 | + template <typename Self, std::integral Int> |
| 86 | + friend constexpr std::remove_cvref_t<Self> operator+(Self&& self, Int offset) |
| 87 | + requires std::derived_from<std::remove_cvref_t<Self>, IteratorFacade> //&& detail::iterator_facadeable_c<Self> |
| 88 | + { |
| 89 | + using Result = std::remove_cvref_t<Self>; |
| 90 | + Result result{std::forward<Self>(self)}; |
75 | 91 | result += offset; |
76 | 92 | return result; |
77 | 93 | } |
78 | | - friend constexpr auto operator+(difference_type offset, iterator it) -> iterator { return (it += offset); } |
79 | | - friend constexpr auto operator-(iterator const& it, difference_type idx) -> iterator { return it + (-idx); } |
80 | | - friend constexpr auto operator-(IteratorFacade const& first, IteratorFacade const& second) -> difference_type { |
81 | | - return second.distance_to(first); |
| 94 | + template <typename Self, std::integral Int> |
| 95 | + requires std::derived_from<std::remove_cvref_t<Self>, IteratorFacade> //&& detail::iterator_facadeable_c<Self> |
| 96 | + friend constexpr std::remove_cvref_t<Self> operator+(Int offset, Self&& self) { |
| 97 | + return std::forward<Self>(self) + offset; |
| 98 | + } |
| 99 | + template <typename Self, std::integral Int> |
| 100 | + requires std::derived_from<std::remove_cvref_t<Self>, IteratorFacade> //&& detail::iterator_facadeable_c<Self> |
| 101 | + friend constexpr std::remove_cvref_t<Self> operator-(Self&& self, Int idx) { |
| 102 | + return (std::forward<Self>(self)) + (-idx); |
82 | 103 | } |
83 | 104 |
|
84 | | - constexpr auto operator[](difference_type idx) const -> value_type const& { return *(*this + idx); } |
85 | | - |
86 | | - private: |
87 | | - IteratorFacade() = default; // default constructor is private to prevent non-CRTP instantiation |
88 | | - friend Impl; // allow Impl to access private members; this is necessary for CRTP |
89 | | - |
90 | | - // overloads for public bidirectional exposure (difference between MSVC and ClangCL) |
91 | | - constexpr std::strong_ordering three_way_compare(IteratorFacade const& other) const { |
92 | | - return static_cast<std::add_lvalue_reference_t<const_iterator>>(*this).three_way_compare( |
93 | | - static_cast<std::add_lvalue_reference_t<const_iterator>>(other)); |
| 105 | + template <typename Self> |
| 106 | + constexpr decltype(auto) operator[](this Self const& self, typename Self::difference_type idx) { |
| 107 | + return *(self + idx); |
94 | 108 | } |
95 | | - constexpr auto distance_to(IteratorFacade const& other) const -> difference_type { |
96 | | - return static_cast<std::add_lvalue_reference_t<const_iterator>>(*this).distance_to( |
97 | | - static_cast<std::add_lvalue_reference_t<const_iterator>>(other)); |
| 109 | + |
| 110 | + // prevent construction by non-derived and non-iterator-facadeable types |
| 111 | + IteratorFacade() = delete; |
| 112 | + template <typename Self> constexpr explicit IteratorFacade(Self& /*self*/) { |
| 113 | + // cannot be done using constraints because the type is not fully instantiated yet when the compiler |
| 114 | + // instantiates the constructor. Note that this is different from the other methods because those are only |
| 115 | + // instantiated when used. |
| 116 | + static_assert(std::derived_from<std::remove_cvref_t<Self>, IteratorFacade>); |
| 117 | + static_assert(detail::iterator_facadeable_c<std::remove_cvref_t<Self>>); |
98 | 118 | } |
99 | 119 | }; |
100 | 120 |
|
|
0 commit comments