libstdc++
safe_local_iterator.h
Go to the documentation of this file.
1// Safe iterator implementation -*- C++ -*-
2
3// Copyright (C) 2011-2026 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25/** @file debug/safe_local_iterator.h
26 * This file is a GNU debug extension to the Standard C++ Library.
27 */
28
29#ifndef _GLIBCXX_DEBUG_SAFE_LOCAL_ITERATOR_H
30#define _GLIBCXX_DEBUG_SAFE_LOCAL_ITERATOR_H 1
31
33
34#define _GLIBCXX_DEBUG_VERIFY_OPERANDS(_Lhs, _Rhs) \
35 _GLIBCXX_DEBUG_VERIFY((!_Lhs._M_singular() && !_Rhs._M_singular()) \
36 || (_Lhs._M_value_initialized() \
37 && _Rhs._M_value_initialized()), \
38 _M_message(__msg_iter_compare_bad) \
39 ._M_iterator(_Lhs, "lhs") \
40 ._M_iterator(_Rhs, "rhs")); \
41 _GLIBCXX_DEBUG_VERIFY(_Lhs._M_can_compare(_Rhs), \
42 _M_message(__msg_compare_different) \
43 ._M_iterator(_Lhs, "lhs") \
44 ._M_iterator(_Rhs, "rhs")); \
45 _GLIBCXX_DEBUG_VERIFY(_Lhs._M_in_same_bucket(_Rhs), \
46 _M_message(__msg_local_iter_compare_bad) \
47 ._M_iterator(_Lhs, "lhs") \
48 ._M_iterator(_Rhs, "rhs"))
49
50namespace __gnu_debug
51{
52 /** \brief Safe iterator wrapper.
53 *
54 * The class template %_Safe_local_iterator is a wrapper around an
55 * iterator that tracks the iterator's movement among unordered containers
56 * and checks that operations performed on the "safe" iterator are
57 * legal. In additional to the basic iterator operations (which are
58 * validated, and then passed to the underlying iterator),
59 * %_Safe_local_iterator has member functions for iterator invalidation,
60 * attaching/detaching the iterator from unordered containers, and querying
61 * the iterator's state.
62 */
63 template<typename _Iterator, typename _UContainer>
64 class _Safe_local_iterator
65 : private _Iterator
67 {
68 typedef typename _UContainer::size_type size_type;
69
70 typedef std::iterator_traits<_Iterator> _Traits;
71
72 using _IsConstant = std::__are_same<
73 typename _UContainer::_Base::const_local_iterator, _Iterator>;
74
75 using _OtherIterator = std::__conditional_t<
76 _IsConstant::__value,
77 typename _UContainer::_Base::local_iterator,
78 typename _UContainer::_Base::const_local_iterator>;
79
80 typedef _Safe_local_iterator _Self;
81 typedef _Safe_local_iterator<_OtherIterator, _UContainer> _OtherSelf;
82
83 struct _Unchecked { };
84
85 _Safe_local_iterator(const _Safe_local_iterator& __x, _Unchecked) noexcept
86 : _Iterator(__x), _Safe_local_iterator_base(__x, _S_constant())
87 { }
88
89 public:
90 typedef _Iterator iterator_type;
91 typedef typename _Traits::iterator_category iterator_category;
92 typedef typename _Traits::value_type value_type;
93 typedef typename _Traits::difference_type difference_type;
94 typedef typename _Traits::reference reference;
95 typedef typename _Traits::pointer pointer;
96
97 /// @post the iterator is singular and unattached
98 _Safe_local_iterator() noexcept : _Iterator() { }
99
100 /**
101 * @brief Safe iterator construction from an unsafe iterator and
102 * its unordered container.
103 *
104 * @pre @p cont is not NULL
105 * @post this is not singular
106 */
107 _Safe_local_iterator(_Iterator __i,
108 const _Safe_unordered_container_base* __cont)
109 : _Iterator(__i), _Safe_local_iterator_base(__cont, _S_constant())
110 { }
111
112 /**
113 * @brief Copy construction.
114 */
115 _Safe_local_iterator(const _Safe_local_iterator& __x) noexcept
116 : _Iterator(__x)
117 {
118 // _GLIBCXX_RESOLVE_LIB_DEFECTS
119 // DR 408. Is vector<reverse_iterator<char*> > forbidden?
120 _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
121 || __x._M_value_initialized(),
122 _M_message(__msg_init_copy_singular)
123 ._M_iterator(*this, "this")
124 ._M_iterator(__x, "other"));
125 _M_attach(__x._M_safe_container());
126 }
127
128 /**
129 * @brief Move construction.
130 * @post __x is singular and unattached
131 */
132 _Safe_local_iterator(_Safe_local_iterator&& __x) noexcept
133 : _Iterator()
134 {
135 _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
136 || __x._M_value_initialized(),
137 _M_message(__msg_init_copy_singular)
138 ._M_iterator(*this, "this")
139 ._M_iterator(__x, "other"));
140 auto __cont = __x._M_safe_container();
141 __x._M_detach();
142 std::swap(base(), __x.base());
143 _M_attach(__cont);
144 }
145
146 /**
147 * @brief Converting constructor from a mutable iterator to a
148 * constant iterator.
149 */
150 template<typename _MutableIterator>
152 const _Safe_local_iterator<_MutableIterator,
153 typename __gnu_cxx::__enable_if<_IsConstant::__value &&
154 std::__are_same<_MutableIterator, _OtherIterator>::__value,
155 _UContainer>::__type>& __x) noexcept
156 : _Iterator(__x.base())
157 {
158 // _GLIBCXX_RESOLVE_LIB_DEFECTS
159 // DR 408. Is vector<reverse_iterator<char*> > forbidden?
160 _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
161 || __x._M_value_initialized(),
162 _M_message(__msg_init_const_singular)
163 ._M_iterator(*this, "this")
164 ._M_iterator(__x, "other"));
165 _M_attach(__x._M_safe_container());
166 }
167
168 /**
169 * @brief Copy assignment.
170 */
172 operator=(const _Safe_local_iterator& __x)
173 {
174 // _GLIBCXX_RESOLVE_LIB_DEFECTS
175 // DR 408. Is vector<reverse_iterator<char*> > forbidden?
176 _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
177 || __x._M_value_initialized(),
178 _M_message(__msg_copy_singular)
179 ._M_iterator(*this, "this")
180 ._M_iterator(__x, "other"));
181
182 if (this->_M_sequence && this->_M_sequence == __x._M_sequence)
183 {
185 base() = __x.base();
187 }
188 else
189 {
190 _M_detach();
191 base() = __x.base();
192 _M_attach(__x._M_safe_container());
193 }
194
195 return *this;
196 }
197
198 /**
199 * @brief Move assignment.
200 * @post __x is singular and unattached
201 */
203 operator=(_Safe_local_iterator&& __x) noexcept
204 {
205 _GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
206 || __x._M_value_initialized(),
207 _M_message(__msg_copy_singular)
208 ._M_iterator(*this, "this")
209 ._M_iterator(__x, "other"));
210
211 if (std::__addressof(__x) == this)
212 return *this;
213
214 if (this->_M_sequence && this->_M_sequence == __x._M_sequence)
215 {
217 base() = __x.base();
219 }
220 else
221 {
222 _M_detach();
223 base() = __x.base();
224 _M_attach(__x._M_safe_container());
225 }
226
227 __x._M_detach();
228 __x.base() = _Iterator();
229 return *this;
230 }
231
232 /**
233 * @brief Iterator dereference.
234 * @pre iterator is dereferenceable
235 */
236 reference
237 operator*() const
238 {
239 _GLIBCXX_DEBUG_VERIFY(this->_M_dereferenceable(),
240 _M_message(__msg_bad_deref)
241 ._M_iterator(*this, "this"));
242 return *base();
243 }
244
245 /**
246 * @brief Iterator dereference.
247 * @pre iterator is dereferenceable
248 */
249 pointer
251 {
252 _GLIBCXX_DEBUG_VERIFY(this->_M_dereferenceable(),
253 _M_message(__msg_bad_deref)
254 ._M_iterator(*this, "this"));
255 return base().operator->();
256 }
257
258 // ------ Input iterator requirements ------
259 /**
260 * @brief Iterator preincrement
261 * @pre iterator is incrementable
262 */
265 {
266 _GLIBCXX_DEBUG_VERIFY(this->_M_incrementable(),
267 _M_message(__msg_bad_inc)
268 ._M_iterator(*this, "this"));
270 ++base();
271 return *this;
272 }
273
274 /**
275 * @brief Iterator postincrement
276 * @pre iterator is incrementable
277 */
280 {
281 _GLIBCXX_DEBUG_VERIFY(this->_M_incrementable(),
282 _M_message(__msg_bad_inc)
283 ._M_iterator(*this, "this"));
284 _Safe_local_iterator __ret(*this, _Unchecked{});
285 ++*this;
286 return __ret;
287 }
288
289 // ------ Utilities ------
290
291 /// Determine if this is a constant iterator.
292 static constexpr bool
294 { return _IsConstant::__value; }
295
296 /**
297 * @brief Return the underlying iterator
298 */
299 _Iterator&
300 base() noexcept { return *this; }
301
302 const _Iterator&
303 base() const noexcept { return *this; }
304
305 /**
306 * @brief Return the bucket
307 */
308 size_type
309 bucket() const { return base()._M_get_bucket(); }
310
311 /**
312 * @brief Conversion to underlying non-debug iterator to allow
313 * better interaction with non-debug containers.
314 */
315 operator _Iterator() const { return *this; }
316
317 /** Attach iterator to the given unordered container. */
318 void
321
322 /** Likewise, but not thread-safe. */
323 void
326
327 /// Is the iterator dereferenceable?
328 bool
330 { return !this->_M_singular() && !_M_is_end(); }
331
332 /// Is the iterator incrementable?
333 bool
335 { return !this->_M_singular() && !_M_is_end(); }
336
337 /// Is the iterator value-initialized?
338 bool
340 { return _M_version == 0 && base() == _Iterator{}; }
341
342 // Is the iterator range [*this, __rhs) valid?
343 bool
344 _M_valid_range(const _Safe_local_iterator& __rhs,
345 std::pair<difference_type,
346 _Distance_precision>& __dist_info) const;
347
348 // Get distance to __rhs.
349 typename _Distance_traits<_Iterator>::__type
350 _M_get_distance_to(const _Safe_local_iterator& __rhs) const;
351
352 // The unordered container this iterator references.
353 std::__conditional_t<
354 _IsConstant::__value, const _UContainer*, _UContainer*>
355 _M_get_ucontainer() const
356 {
357 // Looks like not const-correct, but if _IsConstant the constness
358 // is restored when returning the container pointer and if not
359 // _IsConstant we are allowed to remove constness.
360 return static_cast<_UContainer*>
361 (const_cast<_Safe_unordered_container_base*>(_M_safe_container()));
362 }
363
364 /// Is this iterator equal to the container's begin(bucket) iterator?
365 bool _M_is_begin() const
366 { return base() == _M_get_ucontainer()->_M_base().begin(bucket()); }
367
368 /// Is this iterator equal to the container's end(bucket) iterator?
369 bool _M_is_end() const
370 { return base() == _M_get_ucontainer()->_M_base().end(bucket()); }
371
372 /// Is this iterator part of the same bucket as the other one?
373 template<typename _Other>
374 bool
375 _M_in_same_bucket(const _Safe_local_iterator<_Other,
376 _UContainer>& __other) const
377 { return bucket() == __other.bucket(); }
378
379 friend inline bool
380 operator==(const _Self& __lhs, const _OtherSelf& __rhs) noexcept
381 {
382 _GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
383 return __lhs.base() == __rhs.base();
384 }
385
386 friend inline bool
387 operator==(const _Self& __lhs, const _Self& __rhs) noexcept
388 {
389 _GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
390 return __lhs.base() == __rhs.base();
391 }
392
393 friend inline bool
394 operator!=(const _Self& __lhs, const _OtherSelf& __rhs) noexcept
395 {
396 _GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
397 return __lhs.base() != __rhs.base();
398 }
399
400 friend inline bool
401 operator!=(const _Self& __lhs, const _Self& __rhs) noexcept
402 {
403 _GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
404 return __lhs.base() != __rhs.base();
405 }
406 };
407
408 /** Safe local iterators know how to check if they form a valid range. */
409 template<typename _Iterator, typename _UContainer>
410 inline bool
413 typename _Distance_traits<_Iterator>::__type& __dist_info)
414 { return __first._M_valid_range(__last, __dist_info); }
415
416 template<typename _Iterator, typename _UContainer>
417 inline bool
418 __valid_range(const _Safe_local_iterator<_Iterator, _UContainer>& __first,
419 const _Safe_local_iterator<_Iterator, _UContainer>& __last)
420 {
421 typename _Distance_traits<_Iterator>::__type __dist_info;
422 return __first._M_valid_range(__last, __dist_info);
423 }
424
425#if __cplusplus < 201103L
426 template<typename _Iterator, typename _UContainer>
427 struct _Unsafe_type<_Safe_local_iterator<_Iterator, _UContainer> >
428 { typedef _Iterator _Type; };
429#endif
430
431 template<typename _Iterator, typename _UContainer>
432 inline _Iterator
434 { return __it.base(); }
435
436} // namespace __gnu_debug
437
438#undef _GLIBCXX_DEBUG_VERIFY_OPERANDS
439
441
442#endif
constexpr _Tp * __addressof(_Tp &__r) noexcept
Same as C++11 std::addressof.
Definition move.h:52
GNU debug classes for public use.
Traits class for iterators.
Struct holding two objects (or references) of arbitrary type.
Definition stl_pair.h:307
pointer operator->() const
Iterator dereference.
bool _M_is_end() const
Is this iterator equal to the container's end(bucket) iterator?
void _M_attach_single(const _Safe_unordered_container_base *__cont)
bool _M_in_same_bucket(const _Safe_local_iterator< _Other, _UContainer > &__other) const
Is this iterator part of the same bucket as the other one?
bool _M_dereferenceable() const
Is the iterator dereferenceable?
bool _M_value_initialized() const
Is the iterator value-initialized?
_Safe_local_iterator(const _Safe_local_iterator &__x) noexcept
Copy construction.
size_type bucket() const
Return the bucket.
reference operator*() const
Iterator dereference.
static constexpr bool _S_constant()
Determine if this is a constant iterator.
_Safe_local_iterator operator++(int)
Iterator postincrement.
bool _M_is_begin() const
Is this iterator equal to the container's begin(bucket) iterator?
_Iterator & base() noexcept
Return the underlying iterator.
void _M_attach(const _Safe_unordered_container_base *__cont)
_Safe_local_iterator(_Safe_local_iterator &&__x) noexcept
Move construction.
_Safe_local_iterator & operator++()
Iterator preincrement.
_Safe_local_iterator & operator=(_Safe_local_iterator &&__x) noexcept
Move assignment.
_Safe_local_iterator(_Iterator __i, const _Safe_unordered_container_base *__cont)
Safe iterator construction from an unsafe iterator and its unordered container.
_Safe_local_iterator(const _Safe_local_iterator< _MutableIterator, typename __gnu_cxx::__enable_if< _IsConstant::__value &&std::__are_same< _MutableIterator, _OtherIterator >::__value, _UContainer >::__type > &__x) noexcept
Converting constructor from a mutable iterator to a constant iterator.
bool _M_incrementable() const
Is the iterator incrementable?
_Safe_local_iterator & operator=(const _Safe_local_iterator &__x)
Copy assignment.
bool _M_singular() const noexcept
__gnu_cxx::__mutex & _M_get_mutex() noexcept
unsigned int _M_version
The container version number. This number may never be 0.
Definition safe_base.h:230
void _M_attach_single(const _Safe_unordered_container_base *__cont, bool __constant) noexcept
void _M_attach(const _Safe_unordered_container_base *__cont, bool __constant)
Base class that supports tracking of local iterators that reference an unordered container.
Scoped lock idiom.