1//
2// immer: immutable data structures for C++
3// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4//
5// This software is distributed under the Boost Software License, Version 1.0.
6// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7//
8
9#pragma once
10
11#include <cstddef>
12#include <iterator>
13#include <type_traits>
14
15namespace immer {
16namespace detail {
17
18struct iterator_core_access
19{
20 template <typename T>
21 static decltype(auto) dereference(T&& x)
22 { return x.dereference(); }
23
24 template <typename T>
25 static decltype(auto) increment(T&& x)
26 { return x.increment(); }
27
28 template <typename T>
29 static decltype(auto) decrement(T&& x)
30 { return x.decrement(); }
31
32 template <typename T1, typename T2>
33 static decltype(auto) equal(T1&& x1, T2&& x2)
34 { return x1.equal(x2); }
35
36 template <typename T, typename D>
37 static decltype(auto) advance(T&& x, D d)
38 { return x.advance(d); }
39
40 template <typename T1, typename T2>
41 static decltype(auto) distance_to(T1&& x1, T2&& x2)
42 { return x1.distance_to(x2); }
43};
44
45/*!
46 * Minimalistic reimplementation of boost::iterator_facade
47 */
48template <typename DerivedT,
49 typename IteratorCategoryT,
50 typename T,
51 typename ReferenceT = T&,
52 typename DifferenceTypeT = std::ptrdiff_t,
53 typename PointerT = T*>
54class iterator_facade
55 : public std::iterator<IteratorCategoryT,
56 T,
57 DifferenceTypeT,
58 PointerT,
59 ReferenceT>
60{
61protected:
62 using access_t = iterator_core_access;
63
64 constexpr static auto is_random_access =
65 std::is_base_of<std::random_access_iterator_tag,
66 IteratorCategoryT>::value;
67 constexpr static auto is_bidirectional =
68 std::is_base_of<std::bidirectional_iterator_tag,
69 IteratorCategoryT>::value;
70
71 class reference_proxy
72 {
73 friend iterator_facade;
74 DerivedT iter_;
75
76 reference_proxy(DerivedT iter)
77 : iter_{std::move(iter)} {}
78 public:
79 operator ReferenceT() const { return *iter_; }
80 };
81
82 const DerivedT& derived() const
83 {
84 static_assert(std::is_base_of<iterator_facade, DerivedT>::value,
85 "must pass a derived thing");
86 return *static_cast<const DerivedT*>(this);
87 }
88 DerivedT& derived()
89 {
90 static_assert(std::is_base_of<iterator_facade, DerivedT>::value,
91 "must pass a derived thing");
92 return *static_cast<DerivedT*>(this);
93 }
94
95public:
96 ReferenceT operator*() const
97 {
98 return access_t::dereference(derived());
99 }
100 PointerT operator->() const
101 {
102 return &access_t::dereference(derived());
103 }
104 reference_proxy operator[](DifferenceTypeT n) const
105 {
106 static_assert(is_random_access, "");
107 return derived() + n;
108 }
109
110 bool operator==(const DerivedT& rhs) const
111 {
112 return access_t::equal(derived(), rhs);
113 }
114 bool operator!=(const DerivedT& rhs) const
115 {
116 return !access_t::equal(derived(), rhs);
117 }
118
119 DerivedT& operator++()
120 {
121 access_t::increment(derived());
122 return derived();
123 }
124 DerivedT operator++(int)
125 {
126 auto tmp = derived();
127 access_t::increment(derived());
128 return tmp;
129 }
130
131 DerivedT& operator--()
132 {
133 static_assert(is_bidirectional || is_random_access, "");
134 access_t::decrement(derived());
135 return derived();
136 }
137 DerivedT operator--(int)
138 {
139 static_assert(is_bidirectional || is_random_access, "");
140 auto tmp = derived();
141 access_t::decrement(derived());
142 return tmp;
143 }
144
145 DerivedT& operator+=(DifferenceTypeT n)
146 {
147 access_t::advance(derived(), n);
148 return derived();
149 }
150 DerivedT& operator-=(DifferenceTypeT n)
151 {
152 access_t::advance(derived(), -n);
153 return derived();
154 }
155
156 DerivedT operator+(DifferenceTypeT n) const
157 {
158 static_assert(is_random_access, "");
159 auto tmp = derived();
160 return tmp += n;
161 }
162 friend DerivedT operator+(DifferenceTypeT n, const DerivedT& i)
163 {
164 static_assert(is_random_access, "");
165 return i + n;
166 }
167 DerivedT operator-(DifferenceTypeT n) const
168 {
169 static_assert(is_random_access, "");
170 auto tmp = derived();
171 return tmp -= n;
172 }
173 DifferenceTypeT operator-(const DerivedT& rhs) const
174 {
175 static_assert(is_random_access, "");
176 return access_t::distance_to(rhs, derived());
177 }
178
179 bool operator<(const DerivedT& rhs) const
180 {
181 static_assert(is_random_access, "");
182 return access_t::distance_to(derived(), rhs) > 0;
183 }
184 bool operator<=(const DerivedT& rhs) const
185 {
186 static_assert(is_random_access, "");
187 return access_t::distance_to(derived(), rhs) >= 0;
188 }
189 bool operator>(const DerivedT& rhs) const
190 {
191 static_assert(is_random_access, "");
192 return access_t::distance_to(derived(), rhs) < 0;
193 }
194 bool operator>=(const DerivedT& rhs) const
195 {
196 static_assert(is_random_access, "");
197 return access_t::distance_to(derived(), rhs) <= 0;
198 }
199};
200
201} // namespace detail
202} // namespace immer
203