libstdc++: [_GLIBCXX_DEBUG] Enhance __gnu_debug::inplace_vector
implementation
Review invalidation of iterators to use a RAII approach. A guard
type has been
implemented that is in charge of running an invalidation policy on
destruction
when not in a constant evaluation context.
Invalidation of iterators on assign operation has been reviewed.
All iterators
are being invalidated when using the _GLIBCXX_DEBUG_PEDANTIC mode.
Otherwise
invalidate only iterators that were removed by the assignment
operation.
libstdc++-v3/ChangeLog:
* include/debug/assertions.h (_GLIBCXX_DEBUG_STD_C): New.
* include/debug/inplace_vector
(__gnu_debug::_Inplace_vector_guard): New.
(inplace_vector::_InvalidatePolicy): New.
(inplace_vector::_InvalidateGuard): New.
(inplace_vector::_InvalidateAfterNthPolicy): New.
(inplace_vector::_InvalidateAfterNthGuard): New.
[_GLIBCXX_DEBUG_PEDANTIC](inplace_vector::_PedanticInvalidatePolicy): New.
(inplace_vector::_AssignGuard): New.
(inplace_vector): Adapt using latter guards.
*
testsuite/23_containers/inplace_vector/debug/initializer_list.cc: New
test case.
*
testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc:
New test case.
Tested under Linux x64_86 _GLIBCXX_DEBUG mode.
Ok to commit ?
François
diff --git a/libstdc++-v3/include/debug/assertions.h
b/libstdc++-v3/include/debug/assertions.h
index c4993dc8596..768a7f05228 100644
--- a/libstdc++-v3/include/debug/assertions.h
+++ b/libstdc++-v3/include/debug/assertions.h
@@ -31,6 +31,12 @@
#include <bits/c++config.h>
+#ifdef _GLIBCXX_DEBUG
+# define _GLIBCXX_DEBUG_STD_C ::std::_GLIBCXX_STD_C
+#else
+# define _GLIBCXX_DEBUG_STD_C ::std
+#endif
+
#ifndef _GLIBCXX_DEBUG
// Verify that [_First, _Last) forms a non-empty iterator range.
# define __glibcxx_requires_non_empty_range(_First,_Last) \
diff --git a/libstdc++-v3/include/debug/inplace_vector
b/libstdc++-v3/include/debug/inplace_vector
index e579e8636f9..6ce4d006c90 100644
--- a/libstdc++-v3/include/debug/inplace_vector
+++ b/libstdc++-v3/include/debug/inplace_vector
@@ -46,6 +46,33 @@ namespace std _GLIBCXX_VISIBILITY(default) { namespace
__debug {
#include <debug/safe_sequence.h>
#include <debug/safe_iterator.h>
+namespace __gnu_debug
+{
+ template<typename _Tp, size_t _Nm, typename _InvalidationPolicy>
+ struct _Inplace_vector_guard
+ {
+ using _SafeInplaceVector = std::__debug::inplace_vector<_Tp, _Nm>;
+ using _StdInplaceVector = _GLIBCXX_DEBUG_STD_C::inplace_vector<_Tp, _Nm>;
+ using difference_type = typename _StdInplaceVector::difference_type;
+
+ constexpr
+ _Inplace_vector_guard(_SafeInplaceVector& __vector,
+ difference_type __offset = 0)
+ : _M_vector(__vector), _M_invalidation_policy(__vector, __offset) { }
+
+ constexpr
+ ~_Inplace_vector_guard()
+ {
+ if (!std::is_constant_evaluated())
+ _M_invalidation_policy(_M_vector);
+ }
+
+ private:
+ _SafeInplaceVector& _M_vector;
+ _InvalidationPolicy _M_invalidation_policy;
+ };
+}
+
namespace std _GLIBCXX_VISIBILITY(default)
{
namespace __debug
@@ -80,6 +107,70 @@ namespace __debug
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ private:
+ struct _InvalidatePolicy
+ {
+ size_type _M_size;
+ _Base_const_iterator _M_end;
+
+ constexpr
+ _InvalidatePolicy(const inplace_vector& __vector,
+ difference_type)
+ : _M_size(__vector.size()), _M_end(__vector._M_base().end()) { }
+
+ void
+ operator()(inplace_vector& __vector) const noexcept
+ {
+ const size_type __size = __vector.size();
+ if (__size < _M_size)
+ __vector._M_invalidate_after_nth(__size);
+ else if (__size > _M_size)
+ __vector._M_invalidate_if(_Equal(_M_end));
+ }
+ };
+
+ using _InvalidateGuard =
+ __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidatePolicy>;
+
+ struct _InvalidateAfterNthPolicy
+ {
+ size_type _M_size;
+ difference_type _M_offset;
+
+ constexpr
+ _InvalidateAfterNthPolicy(const inplace_vector& __vector,
+ difference_type __offset)
+ : _M_size(__vector.size()), _M_offset(__offset) { }
+
+ void
+ operator()(inplace_vector& __vector) const noexcept
+ {
+ if (__vector.size() != _M_size)
+ __vector._M_invalidate_after_nth(_M_offset);
+ }
+ };
+
+ using _InvalidateAfterNthGuard =
+ __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidateAfterNthPolicy>;
+
+#ifdef _GLIBCXX_DEBUG_PEDANTIC
+ struct _PedanticInvalidatePolicy
+ {
+ constexpr
+ _PedanticInvalidatePolicy(const inplace_vector&, difference_type) { }
+
+ void
+ operator()(inplace_vector& __vector) const noexcept
+ { __vector._M_invalidate_all(); }
+ };
+
+ using _AssignGuard =
+ __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _PedanticInvalidatePolicy>;
+#else
+ using _AssignGuard = _InvalidateGuard;
+#endif
+
+ public:
constexpr
inplace_vector() noexcept = default;
@@ -120,8 +211,8 @@ namespace __debug
constexpr inplace_vector&
operator=(initializer_list<_Tp> __il)
{
+ _AssignGuard __guard(*this);
_Base::operator=(__il);
- this->_M_invalidate_all();
return *this;
}
@@ -129,65 +220,63 @@ namespace __debug
constexpr void
assign(_InputIterator __first, _InputIterator __last)
{
+ if (std::is_constant_evaluated())
+ return _Base::assign(__gnu_debug::__unsafe(__first),
+ __gnu_debug::__unsafe(__last));
+
typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
__glibcxx_check_valid_range2(__first, __last, __dist);
- const auto __size = size();
- const auto __end = _Base::end();
+ _AssignGuard __guard(*this);
if (__dist.second >= __gnu_debug::__dp_sign)
_Base::assign(__gnu_debug::__unsafe(__first),
__gnu_debug::__unsafe(__last));
else
_Base::assign(__first, __last);
-
- if (size() < __size)
- _M_invalidate_after_nth(size());
- else if (size() > __size)
- this->_M_invalidate_if(_Equal(__end));
}
template<__detail::__container_compatible_range<_Tp> _Rg>
constexpr void
assign_range(_Rg&& __rg)
{
+ _AssignGuard __guard(*this);
_Base::assign_range(std::forward<_Rg>(__rg));
- this->_M_invalidate_all();
}
constexpr void
assign(size_type __n, const _Tp& __u)
{
+ _AssignGuard __guard(*this);
_Base::assign(__n, __u);
- this->_M_invalidate_all();
}
constexpr void
assign(initializer_list<_Tp> __il)
{
+ _AssignGuard __guard(*this);
_Base::assign(__il);
- this->_M_invalidate_all();
}
// iterators
[[nodiscard]]
constexpr iterator
begin() noexcept
- { return { _Base::begin(), this }; }
+ { return iterator(_Base::begin(), this); }
[[nodiscard]]
constexpr const_iterator
begin() const noexcept
- { return { _Base::begin(), this }; }
+ { return const_iterator(_Base::begin(), this); }
[[nodiscard]]
constexpr iterator
end() noexcept
- { return { _Base::end(), this }; }
+ { return iterator(_Base::end(), this); }
[[nodiscard]]
constexpr const_iterator
end() const noexcept
- { return { _Base::end(), this }; }
+ { return const_iterator(_Base::end(), this); }
[[nodiscard]]
constexpr reverse_iterator
@@ -210,12 +299,12 @@ namespace __debug
[[nodiscard]]
constexpr const_iterator
cbegin() const noexcept
- { return { _Base::cbegin(), this }; }
+ { return const_iterator(_Base::cbegin(), this); }
[[nodiscard]]
constexpr const_iterator
cend() const noexcept
- { return { _Base::cend(), this }; }
+ { return const_iterator(_Base::cend(), this); }
[[nodiscard]]
constexpr const_reverse_iterator
@@ -233,15 +322,15 @@ namespace __debug
constexpr void
resize(size_type __n)
{
+ _InvalidateGuard __guard(*this);
_Base::resize(__n);
- _M_invalidate_after_nth(__n);
}
constexpr void
resize(size_type __n, const _Tp& __c)
{
+ _InvalidateGuard __guard(*this);
_Base::resize(__n, __c);
- _M_invalidate_after_nth(__n);
}
using _Base::reserve;
@@ -304,10 +393,8 @@ namespace __debug
constexpr _Tp&
emplace_back(_Args&&... __args)
{
- const auto __end = _Base::cend();
- _Tp& __res = _Base::emplace_back(std::forward<_Args>(__args)...);
- this->_M_invalidate_if(_Equal(__end));
- return __res;
+ _InvalidateGuard __guard(*this);
+ return _Base::emplace_back(std::forward<_Args>(__args)...);
}
constexpr _Tp&
@@ -322,18 +409,15 @@ namespace __debug
constexpr void
append_range(_Rg&& __rg)
{
- const auto __size = size();
- const auto __end = _Base::cend();
+ _InvalidateGuard __guard(*this);
_Base::append_range(__rg);
- if (size() != __size)
- this->_M_invalidate_if(_Equal(__end));
}
constexpr void
pop_back()
{
__glibcxx_check_nonempty();
- _M_invalidate_after_nth(_Base::size() - 1);
+ _InvalidateGuard __guard(*this);
_Base::pop_back();
}
@@ -341,51 +425,30 @@ namespace __debug
constexpr optional<_Tp&>
try_emplace_back(_Args&&... __args)
{
- auto __end = _Base::cend();
- optional<_Tp&> __res
- = _Base::try_emplace_back(std::forward<_Args>(__args)...);
-
- if (__res)
- this->_M_invalidate_if(_Equal(__end));
-
- return __res;
+ _InvalidateGuard __guard(*this);
+ return _Base::try_emplace_back(std::forward<_Args>(__args)...);
}
constexpr optional<_Tp&>
try_push_back(const _Tp& __x)
{
- const auto __end = _Base::cend();
- optional<_Tp&> __res = _Base::try_push_back(__x);
-
- if (__res)
- this->_M_invalidate_if(_Equal(__end));
-
- return __res;
+ _InvalidateGuard __guard(*this);
+ return _Base::try_push_back(__x);
}
constexpr optional<_Tp&>
try_push_back(_Tp&& __x)
{
- const auto __end = _Base::cend();
- optional<_Tp&> __res = _Base::try_push_back(std::move(__x));
-
- if (__res)
- this->_M_invalidate_if(_Equal(__end));
-
- return __res;
+ _InvalidateGuard __guard(*this);
+ return _Base::try_push_back(std::move(__x));
}
template<typename... _Args>
constexpr _Tp&
unchecked_emplace_back(_Args&&... __args)
{
- const auto __end = _Base::cend();
- _Tp& __res =
- _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
-
- this->_M_invalidate_if(_Equal(__end));
-
- return __res;
+ _InvalidateGuard __guard(*this);
+ return _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
}
constexpr _Tp&
@@ -407,9 +470,12 @@ namespace __debug
__glibcxx_check_insert(__position);
const difference_type __offset = __position.base() - _Base::cbegin();
- _Base_iterator __res = _Base::emplace(__position.base(),
- std::forward<_Args>(__args)...);
- _M_invalidate_after_nth(__offset);
+ _Base_iterator __res;
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ __res = _Base::emplace(__position.base(),
+ std::forward<_Args>(__args)...);
+ }
return { __res, this };
}
@@ -427,11 +493,14 @@ namespace __debug
if (std::is_constant_evaluated())
return iterator(_Base::insert(__position.base(), __n, __x), this);
- __glibcxx_check_insert(__position);
+ __glibcxx_check_insert(__position);
const difference_type __offset = __position.base() - _Base::cbegin();
- _Base_iterator __res = _Base::insert(__position.base(), __n, __x);
- _M_invalidate_after_nth(__offset);
- return { __res, this };
+ _Base_iterator __res;
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ __res = _Base::insert(__position.base(), __n, __x);
+ }
+ return iterator(__res, this);
}
template<__any_input_iterator _InputIterator>
@@ -449,14 +518,16 @@ namespace __debug
const difference_type __offset = __position.base() - _Base::cbegin();
_Base_iterator __res;
- if (__dist.second >= __gnu_debug::__dp_sign)
- __res = _Base::insert(__position.base(),
- __gnu_debug::__unsafe(__first),
- __gnu_debug::__unsafe(__last));
- else
- __res = _Base::insert(__position.base(), __first, __last);
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ if (__dist.second >= __gnu_debug::__dp_sign)
+ __res = _Base::insert(__position.base(),
+ __gnu_debug::__unsafe(__first),
+ __gnu_debug::__unsafe(__last));
+ else
+ __res = _Base::insert(__position.base(), __first, __last);
+ }
- _M_invalidate_after_nth(__offset);
return { __res, this };
}
@@ -464,11 +535,12 @@ namespace __debug
constexpr iterator
insert_range(const_iterator __position, _Rg&& __rg)
{
- const auto __size = size();
const difference_type __offset = __position.base() - _Base::cbegin();
- auto __res = _Base::insert_range(__position.base(), __rg);
- if (size() > __size)
- this->_M_invalidate_after_nth(__offset);
+ _Base_iterator __res;
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ __res = _Base::insert_range(__position.base(), __rg);
+ }
return iterator(__res, this);
}
@@ -480,11 +552,13 @@ namespace __debug
return iterator(_Base::insert(__position.base(), __il), this);
__glibcxx_check_insert(__position);
- const auto __size = size();
difference_type __offset = __position.base() - _Base::begin();
- _Base_iterator __res = _Base::insert(__position.base(), __il);
- if (size() > __size)
- this->_M_invalidate_after_nth(__offset);
+ _Base_iterator __res;
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ __res = _Base::insert(__position.base(), __il);
+ }
+
return iterator(__res, this);
}
@@ -496,8 +570,11 @@ namespace __debug
__glibcxx_check_erase(__position);
difference_type __offset = __position.base() - _Base::cbegin();
- _Base_iterator __res = _Base::erase(__position.base());
- this->_M_invalidate_after_nth(__offset);
+ _Base_iterator __res;
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ __res = _Base::erase(__position.base());
+ }
return iterator(__res, this);
}
@@ -509,13 +586,14 @@ namespace __debug
this);
__glibcxx_check_erase_range(__first, __last);
-
if (__first.base() != __last.base())
{
difference_type __offset = __first.base() - _Base::cbegin();
- _Base_iterator __res = _Base::erase(__first.base(),
- __last.base());
- this->_M_invalidate_after_nth(__offset);
+ _Base_iterator __res;
+ {
+ _InvalidateAfterNthGuard __guard(*this, __offset);
+ __res = _Base::erase(__first.base(), __last.base());
+ }
return { __res, this };
}
else
diff --git
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
new file mode 100644
index 00000000000..35d2916021e
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
@@ -0,0 +1,26 @@
+// { dg-do run { target c++26 } }
+
+#include <debug/inplace_vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ __gnu_debug::inplace_vector<int, 6> v = { 0, 1, 2, 3, 4, 5 };
+
+ auto it1 = v.begin() + 1;
+ auto it4 = v.begin() + 4;
+
+ VERIFY( !it1._M_singular() );
+ VERIFY( !it4._M_singular() );
+
+ v = { 10, 11, 12, 13 };
+
+ VERIFY( !it1._M_singular() );
+ VERIFY( it4._M_singular() );
+}
+
+int main()
+{
+ test01();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc
new file mode 100644
index 00000000000..060161e36f2
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc
@@ -0,0 +1,27 @@
+// { dg-do run { target c++26 } }
+// { dg-options "-D_GLIBCXX_DEBUG_PEDANTIC" }
+
+#include <debug/inplace_vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ __gnu_debug::inplace_vector<int, 6> v = { 0, 1, 2, 3, 4, 5 };
+
+ auto it1 = v.begin() + 1;
+ auto it4 = v.begin() + 4;
+
+ VERIFY( !it1._M_singular() );
+ VERIFY( !it4._M_singular() );
+
+ v = { 10, 11, 12, 13 };
+
+ VERIFY( it1._M_singular() );
+ VERIFY( it4._M_singular() );
+}
+
+int main()
+{
+ test01();
+}