From 0663fc707886d2416a3db87cfd13f9bcd6c512bf Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 27 Jun 2026 17:00:04 +0200 Subject: [PATCH] Fix etl::unique_ptr::swap() for deleters (#1482) * Fix etl::unique_ptr::swap() for deleters Fixes https://github.com/ETLCPP/etl/issues/1481 * Fix etl::deque iterator swap() Swap all members, not just the index. --- include/etl/deque.h | 4 ++ include/etl/memory.h | 2 + test/test_deque.cpp | 36 ++++++++++++++++ test/test_memory.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) diff --git a/include/etl/deque.h b/include/etl/deque.h index bd9afa46..4ac0ac92 100644 --- a/include/etl/deque.h +++ b/include/etl/deque.h @@ -457,6 +457,8 @@ namespace etl using ETL_OR_STD::swap; // Allow ADL swap(index, other.index); + swap(p_deque, other.p_deque); + swap(p_buffer, other.p_buffer); } private: @@ -703,6 +705,8 @@ namespace etl void swap(const_iterator& other) { ETL_OR_STD::swap(index, other.index); + ETL_OR_STD::swap(p_deque, other.p_deque); + ETL_OR_STD::swap(p_buffer, other.p_buffer); } private: diff --git a/include/etl/memory.h b/include/etl/memory.h index 6a675f19..40dd1183 100644 --- a/include/etl/memory.h +++ b/include/etl/memory.h @@ -2085,6 +2085,7 @@ namespace etl using ETL_OR_STD::swap; swap(p, value.p); + swap(deleter, value.deleter); } //********************************* @@ -2292,6 +2293,7 @@ namespace etl using ETL_OR_STD::swap; swap(p, v.p); + swap(deleter, v.deleter); } //********************************* diff --git a/test/test_deque.cpp b/test/test_deque.cpp index d26096d1..9daf01b8 100644 --- a/test/test_deque.cpp +++ b/test/test_deque.cpp @@ -632,6 +632,42 @@ namespace CHECK(third >= first); } + //************************************************************************* + // The member swap() must exchange the complete iterator state (index, + // owning deque and buffer). + TEST(test_iterator_swap_different_deques) + { + DataInt data1 = {1, 2, 3, 4, 5}; + DataInt data2 = {10, 20, 30, 40, 50}; + + DataInt::iterator itr1 = data1.begin() + 1; // -> 2 + DataInt::iterator itr2 = data2.begin() + 3; // -> 40 + + itr1.swap(itr2); + + CHECK_EQUAL(40, *itr1); + CHECK_EQUAL(2, *itr2); + CHECK(&itr1.container() == &data2); + CHECK(&itr2.container() == &data1); + } + + //************************************************************************* + TEST(test_const_iterator_swap_different_deques) + { + DataInt data1 = {1, 2, 3, 4, 5}; + DataInt data2 = {10, 20, 30, 40, 50}; + + DataInt::const_iterator itr1 = data1.begin() + 1; // -> 2 + DataInt::const_iterator itr2 = data2.begin() + 3; // -> 40 + + itr1.swap(itr2); + + CHECK_EQUAL(40, *itr1); + CHECK_EQUAL(2, *itr2); + CHECK(&itr1.container() == &data2); + CHECK(&itr2.container() == &data1); + } + //************************************************************************* TEST(test_iterator_comparison_rollover_left) { diff --git a/test/test_memory.cpp b/test/test_memory.cpp index 325e07ca..012904f5 100644 --- a/test/test_memory.cpp +++ b/test/test_memory.cpp @@ -923,6 +923,56 @@ namespace CHECK_EQUAL(1, *up2); } + //************************************************************************* + TEST(test_unique_ptr_swap_deleters) + { + struct Deleter + { + char id; + + explicit Deleter(const char id_) + : id(id_) + { + } + + Deleter(const Deleter&) = delete; + Deleter& operator=(const Deleter&) = delete; + + Deleter(Deleter&& other) noexcept + : id(other.id) + { + other.id = ' '; + } + + Deleter& operator=(Deleter&& other) noexcept + { + if (&other != this) + { + id = other.id; + other.id = ' '; + } + + return *this; + } + + ~Deleter() = default; + + void operator()(int*) const {} + }; + + int a_obj = 1; + int b_obj = 2; + etl::unique_ptr up1(&a_obj, Deleter('A')); + etl::unique_ptr up2(&b_obj, Deleter('B')); + + up1.swap(up2); + + CHECK_EQUAL(2, *up1); + CHECK_EQUAL('B', up1.get_deleter().id); + CHECK_EQUAL(1, *up2); + CHECK_EQUAL('A', up2.get_deleter().id); + } + //************************************************************************* TEST(test_unique_ptr_from_nullptr_assignment) { @@ -1067,6 +1117,56 @@ namespace CHECK_EQUAL(3, up2[3]); } + //************************************************************************* + TEST(test_unique_ptr_array_swap_deleters) + { + struct Deleter + { + char id; + + explicit Deleter(const char id_) + : id(id_) + { + } + + Deleter(const Deleter&) = delete; + Deleter& operator=(const Deleter&) = delete; + + Deleter(Deleter&& other) noexcept + : id(other.id) + { + other.id = ' '; + } + + Deleter& operator=(Deleter&& other) noexcept + { + if (&other != this) + { + id = other.id; + other.id = ' '; + } + + return *this; + } + + ~Deleter() = default; + + void operator()(int*) const {} + }; + + int a_obj = 1; + int b_obj = 2; + etl::unique_ptr up1(&a_obj, Deleter('A')); + etl::unique_ptr up2(&b_obj, Deleter('B')); + + up1.swap(up2); + + CHECK_EQUAL(2, up1[0]); + CHECK_EQUAL('B', up1.get_deleter().id); + CHECK_EQUAL(1, up2[0]); + CHECK_EQUAL('A', up2.get_deleter().id); + } + //************************************************************************* TEST(test_unique_ptr_array_from_nullptr_assignment) {