[PATCH][_GLIBCXX_DEBUG] Enhance inplace_vector implementation

François Dumont frs.dumont@gmail.com
Mon Jul 6 16:40:00 GMT 2026


     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


-------------- next part --------------
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();
+}


More information about the Libstdc++ mailing list