mirror of
https://github.com/gcc-mirror/gcc.git
synced 2025-08-10 02:41:42 +00:00

In revision a35dd276cb
the debug sequence
have been made mutable to allow attach iterators to const containers.
This change completes this fix by also declaring debug unordered container
members mutable.
Additionally the debug iterator sequence is now a pointer-to-const and so
_Safe_sequence_base _M_attach and all other methods are const qualified.
Not-const methods exported are preserved for abi backward compatibility.
libstdc++-v3/ChangeLog:
PR c++/116369
* config/abi/pre/gnu-versioned-namespace.ver: Use new const qualified symbols.
* config/abi/pre/gnu.ver: Add new const qualified symbols.
* include/debug/safe_base.h
(_Safe_iterator_base::_M_sequence): Declare as pointer-to-const.
(_Safe_iterator_base::_M_attach, _M_attach_single): New, take pointer-to-const
_Safe_sequence_base.
(_Safe_sequence_base::_M_detach_all, _M_detach_singular, _M_revalidate_singular)
(_M_swap, _M_get_mutex): New, const qualified.
(_Safe_sequence_base::_M_attach, _M_attach_single, _M_detach, _M_detach_single):
const qualify.
* include/debug/safe_container.h (_Safe_container<>::_M_cont): Add const qualifier.
(_Safe_container<>::_M_swap_base): New.
(_Safe_container(_Safe_container&&, const _Alloc&, std::false_type)):
Adapt to use latter.
(_Safe_container<>::operator=(_Safe_container&&)): Likewise.
(_Safe_container<>::_M_swap): Likewise and take parameter as const reference.
* include/debug/safe_unordered_base.h
(_Safe_local_iterator_base::_M_safe_container): New.
(_Safe_local_iterator_base::_Safe_local_iterator_base): Take
_Safe_unordered_container_base as pointer-to-const.
(_Safe_unordered_container_base::_M_attach, _M_attach_single): New, take
container as _Safe_unordered_container_base pointer-to-const.
(_Safe_unordered_container_base::_M_local_iterators, _M_const_local_iterators):
Add mutable.
(_Safe_unordered_container_base::_M_detach_all, _M_swap): New, const qualify.
(_Safe_unordered_container_base::_M_attach_local, _M_attach_local_single)
(_M_detach_local, _M_detach_local_single): Add const qualifier.
* include/debug/safe_unordered_container.h (_Safe_unordered_container::_M_self()): New.
* include/debug/safe_unordered_container.tcc
(_Safe_unordered_container::_M_invalidate_if, _M_invalidated_local_if): Use latter.
* include/debug/safe_iterator.h (_Safe_iterator<>::_M_attach, _M_attach_single):
Take _Safe_sequence_base as pointer-to-const.
(_Safe_iterator<>::_M_get_sequence): Add const_cast and comment about it.
* include/debug/safe_local_iterator.h (_Safe_local_iterator<>): Replace usages
of _M_sequence member by _M_safe_container().
(_Safe_local_iterator<>::_M_attach, _M_attach_single): Take
_Safe_unordered_container_base as pointer-to-const.
(_Safe_local_iterator<>::_M_get_sequence): Rename into...
(_Safe_local_iterator<>::_M_get_ucontainer): ...this. Add necessary const_cast and
comment to explain it.
(_Safe_local_iterator<>::_M_is_begin, _M_is_end): Adapt.
* include/debug/safe_local_iterator.tcc: Adapt.
* include/debug/safe_sequence.h
(_Safe_sequence<>::_M_invalidate_if, _M_transfer_from_if): Add const qualifier.
* include/debug/safe_sequence.tcc: Adapt.
* include/debug/deque (std::__debug::deque::erase): Adapt to use new const
qualified methods.
* include/debug/formatter.h: Adapt.
* include/debug/forward_list (_Safe_forward_list::_M_this): Add const
qualification and return pointer for consistency with 'this' keyword.
(_Safe_forward_list::_M_swap_aux): Rename into...
(_Safe_forward_list::_S_swap_aux): ...this and take sequence as const reference.
(forward_list<>::resize): Adapt to use const methods.
* include/debug/list (list<>::resize): Likewise.
* src/c++11/debug.cc: Adapt to const qualification.
* testsuite/util/testsuite_containers.h
(forward_members_unordered::forward_members_unordered): Add check on local_iterator
conversion to const_local_iterator.
(forward_members::forward_members): Add check on iterator conversion to
const_iterator.
* testsuite/23_containers/unordered_map/const_container.cc: New test case.
* testsuite/23_containers/unordered_multimap/const_container.cc: New test case.
* testsuite/23_containers/unordered_multiset/const_container.cc: New test case.
* testsuite/23_containers/unordered_set/const_container.cc: New test case.
* testsuite/23_containers/vector/debug/mutex_association.cc: Adapt.
447 lines
14 KiB
C++
447 lines
14 KiB
C++
// Safe iterator implementation -*- C++ -*-
|
|
|
|
// Copyright (C) 2011-2025 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file debug/safe_local_iterator.h
|
|
* This file is a GNU debug extension to the Standard C++ Library.
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_DEBUG_SAFE_LOCAL_ITERATOR_H
|
|
#define _GLIBCXX_DEBUG_SAFE_LOCAL_ITERATOR_H 1
|
|
|
|
#include <debug/safe_unordered_base.h>
|
|
|
|
#define _GLIBCXX_DEBUG_VERIFY_OPERANDS(_Lhs, _Rhs) \
|
|
_GLIBCXX_DEBUG_VERIFY((!_Lhs._M_singular() && !_Rhs._M_singular()) \
|
|
|| (_Lhs._M_value_initialized() \
|
|
&& _Rhs._M_value_initialized()), \
|
|
_M_message(__msg_iter_compare_bad) \
|
|
._M_iterator(_Lhs, "lhs") \
|
|
._M_iterator(_Rhs, "rhs")); \
|
|
_GLIBCXX_DEBUG_VERIFY(_Lhs._M_can_compare(_Rhs), \
|
|
_M_message(__msg_compare_different) \
|
|
._M_iterator(_Lhs, "lhs") \
|
|
._M_iterator(_Rhs, "rhs")); \
|
|
_GLIBCXX_DEBUG_VERIFY(_Lhs._M_in_same_bucket(_Rhs), \
|
|
_M_message(__msg_local_iter_compare_bad) \
|
|
._M_iterator(_Lhs, "lhs") \
|
|
._M_iterator(_Rhs, "rhs"))
|
|
|
|
namespace __gnu_debug
|
|
{
|
|
/** \brief Safe iterator wrapper.
|
|
*
|
|
* The class template %_Safe_local_iterator is a wrapper around an
|
|
* iterator that tracks the iterator's movement among unordered containers
|
|
* and checks that operations performed on the "safe" iterator are
|
|
* legal. In additional to the basic iterator operations (which are
|
|
* validated, and then passed to the underlying iterator),
|
|
* %_Safe_local_iterator has member functions for iterator invalidation,
|
|
* attaching/detaching the iterator from unordered containers, and querying
|
|
* the iterator's state.
|
|
*/
|
|
template<typename _Iterator, typename _UContainer>
|
|
class _Safe_local_iterator
|
|
: private _Iterator
|
|
, public _Safe_local_iterator_base
|
|
{
|
|
typedef _Iterator _Iter_base;
|
|
typedef _Safe_local_iterator_base _Safe_base;
|
|
|
|
typedef typename _UContainer::size_type size_type;
|
|
|
|
typedef std::iterator_traits<_Iterator> _Traits;
|
|
|
|
using _IsConstant = std::__are_same<
|
|
typename _UContainer::_Base::const_local_iterator, _Iterator>;
|
|
|
|
using _OtherIterator = std::__conditional_t<
|
|
_IsConstant::__value,
|
|
typename _UContainer::_Base::local_iterator,
|
|
typename _UContainer::_Base::const_local_iterator>;
|
|
|
|
typedef _Safe_local_iterator _Self;
|
|
typedef _Safe_local_iterator<_OtherIterator, _UContainer> _OtherSelf;
|
|
|
|
struct _Unchecked { };
|
|
|
|
_Safe_local_iterator(const _Safe_local_iterator& __x,
|
|
_Unchecked) noexcept
|
|
: _Iter_base(__x.base())
|
|
{ _M_attach(__x._M_safe_container()); }
|
|
|
|
public:
|
|
typedef _Iterator iterator_type;
|
|
typedef typename _Traits::iterator_category iterator_category;
|
|
typedef typename _Traits::value_type value_type;
|
|
typedef typename _Traits::difference_type difference_type;
|
|
typedef typename _Traits::reference reference;
|
|
typedef typename _Traits::pointer pointer;
|
|
|
|
/// @post the iterator is singular and unattached
|
|
_Safe_local_iterator() noexcept : _Iter_base() { }
|
|
|
|
/**
|
|
* @brief Safe iterator construction from an unsafe iterator and
|
|
* its unordered container.
|
|
*
|
|
* @pre @p cont is not NULL
|
|
* @post this is not singular
|
|
*/
|
|
_Safe_local_iterator(_Iterator __i,
|
|
const _Safe_unordered_container_base* __cont)
|
|
: _Iter_base(__i), _Safe_base(__cont, _S_constant())
|
|
{ }
|
|
|
|
/**
|
|
* @brief Copy construction.
|
|
*/
|
|
_Safe_local_iterator(const _Safe_local_iterator& __x) noexcept
|
|
: _Iter_base(__x.base())
|
|
{
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// DR 408. Is vector<reverse_iterator<char*> > forbidden?
|
|
_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
|
|
|| __x._M_value_initialized(),
|
|
_M_message(__msg_init_copy_singular)
|
|
._M_iterator(*this, "this")
|
|
._M_iterator(__x, "other"));
|
|
_M_attach(__x._M_safe_container());
|
|
}
|
|
|
|
/**
|
|
* @brief Move construction.
|
|
* @post __x is singular and unattached
|
|
*/
|
|
_Safe_local_iterator(_Safe_local_iterator&& __x) noexcept
|
|
: _Iter_base()
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
|
|
|| __x._M_value_initialized(),
|
|
_M_message(__msg_init_copy_singular)
|
|
._M_iterator(*this, "this")
|
|
._M_iterator(__x, "other"));
|
|
auto __cont = __x._M_safe_container();
|
|
__x._M_detach();
|
|
std::swap(base(), __x.base());
|
|
_M_attach(__cont);
|
|
}
|
|
|
|
/**
|
|
* @brief Converting constructor from a mutable iterator to a
|
|
* constant iterator.
|
|
*/
|
|
template<typename _MutableIterator>
|
|
_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
|
|
: _Iter_base(__x.base())
|
|
{
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// DR 408. Is vector<reverse_iterator<char*> > forbidden?
|
|
_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
|
|
|| __x._M_value_initialized(),
|
|
_M_message(__msg_init_const_singular)
|
|
._M_iterator(*this, "this")
|
|
._M_iterator(__x, "other"));
|
|
_M_attach(__x._M_safe_container());
|
|
}
|
|
|
|
/**
|
|
* @brief Copy assignment.
|
|
*/
|
|
_Safe_local_iterator&
|
|
operator=(const _Safe_local_iterator& __x)
|
|
{
|
|
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
|
// DR 408. Is vector<reverse_iterator<char*> > forbidden?
|
|
_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
|
|
|| __x._M_value_initialized(),
|
|
_M_message(__msg_copy_singular)
|
|
._M_iterator(*this, "this")
|
|
._M_iterator(__x, "other"));
|
|
|
|
if (this->_M_sequence && this->_M_sequence == __x._M_sequence)
|
|
{
|
|
__gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
|
|
base() = __x.base();
|
|
_M_version = __x._M_sequence->_M_version;
|
|
}
|
|
else
|
|
{
|
|
_M_detach();
|
|
base() = __x.base();
|
|
_M_attach(__x._M_safe_container());
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* @brief Move assignment.
|
|
* @post __x is singular and unattached
|
|
*/
|
|
_Safe_local_iterator&
|
|
operator=(_Safe_local_iterator&& __x) noexcept
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY(!__x._M_singular()
|
|
|| __x._M_value_initialized(),
|
|
_M_message(__msg_copy_singular)
|
|
._M_iterator(*this, "this")
|
|
._M_iterator(__x, "other"));
|
|
|
|
if (std::__addressof(__x) == this)
|
|
return *this;
|
|
|
|
if (this->_M_sequence && this->_M_sequence == __x._M_sequence)
|
|
{
|
|
__gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
|
|
base() = __x.base();
|
|
_M_version = __x._M_sequence->_M_version;
|
|
}
|
|
else
|
|
{
|
|
_M_detach();
|
|
base() = __x.base();
|
|
_M_attach(__x._M_safe_container());
|
|
}
|
|
|
|
__x._M_detach();
|
|
__x.base() = _Iterator();
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* @brief Iterator dereference.
|
|
* @pre iterator is dereferenceable
|
|
*/
|
|
reference
|
|
operator*() const
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY(this->_M_dereferenceable(),
|
|
_M_message(__msg_bad_deref)
|
|
._M_iterator(*this, "this"));
|
|
return *base();
|
|
}
|
|
|
|
/**
|
|
* @brief Iterator dereference.
|
|
* @pre iterator is dereferenceable
|
|
*/
|
|
pointer
|
|
operator->() const
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY(this->_M_dereferenceable(),
|
|
_M_message(__msg_bad_deref)
|
|
._M_iterator(*this, "this"));
|
|
return base().operator->();
|
|
}
|
|
|
|
// ------ Input iterator requirements ------
|
|
/**
|
|
* @brief Iterator preincrement
|
|
* @pre iterator is incrementable
|
|
*/
|
|
_Safe_local_iterator&
|
|
operator++()
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY(this->_M_incrementable(),
|
|
_M_message(__msg_bad_inc)
|
|
._M_iterator(*this, "this"));
|
|
__gnu_cxx::__scoped_lock __l(this->_M_get_mutex());
|
|
++base();
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* @brief Iterator postincrement
|
|
* @pre iterator is incrementable
|
|
*/
|
|
_Safe_local_iterator
|
|
operator++(int)
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY(this->_M_incrementable(),
|
|
_M_message(__msg_bad_inc)
|
|
._M_iterator(*this, "this"));
|
|
_Safe_local_iterator __ret(*this, _Unchecked{});
|
|
++*this;
|
|
return __ret;
|
|
}
|
|
|
|
// ------ Utilities ------
|
|
|
|
/// Determine if this is a constant iterator.
|
|
static constexpr bool
|
|
_S_constant()
|
|
{ return _IsConstant::__value; }
|
|
|
|
/**
|
|
* @brief Return the underlying iterator
|
|
*/
|
|
_Iterator&
|
|
base() noexcept { return *this; }
|
|
|
|
const _Iterator&
|
|
base() const noexcept { return *this; }
|
|
|
|
/**
|
|
* @brief Return the bucket
|
|
*/
|
|
size_type
|
|
bucket() const { return base()._M_get_bucket(); }
|
|
|
|
/**
|
|
* @brief Conversion to underlying non-debug iterator to allow
|
|
* better interaction with non-debug containers.
|
|
*/
|
|
operator _Iterator() const { return *this; }
|
|
|
|
/** Attach iterator to the given unordered container. */
|
|
void
|
|
_M_attach(const _Safe_unordered_container_base* __cont)
|
|
{ _Safe_base::_M_attach(__cont, _S_constant()); }
|
|
|
|
/** Likewise, but not thread-safe. */
|
|
void
|
|
_M_attach_single(const _Safe_unordered_container_base* __cont)
|
|
{ _Safe_base::_M_attach_single(__cont, _S_constant()); }
|
|
|
|
/// Is the iterator dereferenceable?
|
|
bool
|
|
_M_dereferenceable() const
|
|
{ return !this->_M_singular() && !_M_is_end(); }
|
|
|
|
/// Is the iterator incrementable?
|
|
bool
|
|
_M_incrementable() const
|
|
{ return !this->_M_singular() && !_M_is_end(); }
|
|
|
|
/// Is the iterator value-initialized?
|
|
bool
|
|
_M_value_initialized() const
|
|
{ return _M_version == 0 && base() == _Iter_base{}; }
|
|
|
|
// Is the iterator range [*this, __rhs) valid?
|
|
bool
|
|
_M_valid_range(const _Safe_local_iterator& __rhs,
|
|
std::pair<difference_type,
|
|
_Distance_precision>& __dist_info) const;
|
|
|
|
// Get distance to __rhs.
|
|
typename _Distance_traits<_Iterator>::__type
|
|
_M_get_distance_to(const _Safe_local_iterator& __rhs) const;
|
|
|
|
// The unordered container this iterator references.
|
|
std::__conditional_t<
|
|
_IsConstant::__value, const _UContainer*, _UContainer*>
|
|
_M_get_ucontainer() const
|
|
{
|
|
// Looks like not const-correct, but if _IsConstant the constness
|
|
// is restored when returning the container pointer and if not
|
|
// _IsConstant we are allowed to remove constness.
|
|
return static_cast<_UContainer*>
|
|
(const_cast<_Safe_unordered_container_base*>(_M_safe_container()));
|
|
}
|
|
|
|
/// Is this iterator equal to the container's begin(bucket) iterator?
|
|
bool _M_is_begin() const
|
|
{ return base() == _M_get_ucontainer()->_M_base().begin(bucket()); }
|
|
|
|
/// Is this iterator equal to the container's end(bucket) iterator?
|
|
bool _M_is_end() const
|
|
{ return base() == _M_get_ucontainer()->_M_base().end(bucket()); }
|
|
|
|
/// Is this iterator part of the same bucket as the other one?
|
|
template<typename _Other>
|
|
bool
|
|
_M_in_same_bucket(const _Safe_local_iterator<_Other,
|
|
_UContainer>& __other) const
|
|
{ return bucket() == __other.bucket(); }
|
|
|
|
friend inline bool
|
|
operator==(const _Self& __lhs, const _OtherSelf& __rhs) noexcept
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
|
|
return __lhs.base() == __rhs.base();
|
|
}
|
|
|
|
friend inline bool
|
|
operator==(const _Self& __lhs, const _Self& __rhs) noexcept
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
|
|
return __lhs.base() == __rhs.base();
|
|
}
|
|
|
|
friend inline bool
|
|
operator!=(const _Self& __lhs, const _OtherSelf& __rhs) noexcept
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
|
|
return __lhs.base() != __rhs.base();
|
|
}
|
|
|
|
friend inline bool
|
|
operator!=(const _Self& __lhs, const _Self& __rhs) noexcept
|
|
{
|
|
_GLIBCXX_DEBUG_VERIFY_OPERANDS(__lhs, __rhs);
|
|
return __lhs.base() != __rhs.base();
|
|
}
|
|
};
|
|
|
|
/** Safe local iterators know how to check if they form a valid range. */
|
|
template<typename _Iterator, typename _UContainer>
|
|
inline bool
|
|
__valid_range(const _Safe_local_iterator<_Iterator, _UContainer>& __first,
|
|
const _Safe_local_iterator<_Iterator, _UContainer>& __last,
|
|
typename _Distance_traits<_Iterator>::__type& __dist_info)
|
|
{ return __first._M_valid_range(__last, __dist_info); }
|
|
|
|
template<typename _Iterator, typename _UContainer>
|
|
inline bool
|
|
__valid_range(const _Safe_local_iterator<_Iterator, _UContainer>& __first,
|
|
const _Safe_local_iterator<_Iterator, _UContainer>& __last)
|
|
{
|
|
typename _Distance_traits<_Iterator>::__type __dist_info;
|
|
return __first._M_valid_range(__last, __dist_info);
|
|
}
|
|
|
|
#if __cplusplus < 201103L
|
|
template<typename _Iterator, typename _UContainer>
|
|
struct _Unsafe_type<_Safe_local_iterator<_Iterator, _UContainer> >
|
|
{ typedef _Iterator _Type; };
|
|
#endif
|
|
|
|
template<typename _Iterator, typename _UContainer>
|
|
inline _Iterator
|
|
__unsafe(const _Safe_local_iterator<_Iterator, _UContainer>& __it)
|
|
{ return __it.base(); }
|
|
|
|
} // namespace __gnu_debug
|
|
|
|
#undef _GLIBCXX_DEBUG_VERIFY_OPERANDS
|
|
|
|
#include <debug/safe_local_iterator.tcc>
|
|
|
|
#endif
|