diff --git a/gdb/unittests/intrusive_list-selftests.c b/gdb/unittests/intrusive_list-selftests.c index 0c09a64627d..fbd89ed8c91 100644 --- a/gdb/unittests/intrusive_list-selftests.c +++ b/gdb/unittests/intrusive_list-selftests.c @@ -18,9 +18,15 @@ #include "gdbsupport/intrusive_list.h" +#include "gdbsupport/owning_intrusive_list.h" #include "gdbsupport/selftest.h" #include +/* Count of how many item_with_base or item_with_member objects are + currently alive. */ + +static int items_alive = 0; + /* An item type using intrusive_list_node by inheriting from it and its corresponding list type. Put another base before intrusive_list_node so that a pointer to the node != a pointer to the item. */ @@ -35,7 +41,13 @@ struct item_with_base : public other_base, { explicit item_with_base (const char *name) : name (name) - {} + { + ++items_alive; + } + + DISABLE_COPY_AND_ASSIGN (item_with_base); + + ~item_with_base () { --items_alive; } const char *const name; }; @@ -50,65 +62,79 @@ struct item_with_member { explicit item_with_member (const char *name) : name (name) - {} + { + ++items_alive; + } + + DISABLE_COPY_AND_ASSIGN (item_with_member); + + ~item_with_member () { --items_alive; } const char *const name; intrusive_list_node node; }; +/* Verify that LIST contains exactly the items in EXPECTED. + + Traverse the list forward and backwards to exercise all links. */ + +template +static void +verify_items (const ListType &list, + gdb::array_view expected) +{ + using item_type = typename ListType::value_type; + + int i = 0; + + for (typename ListType::iterator it = list.begin (); it != list.end (); ++it) + { + const item_type &item = *it; + + SELF_CHECK (i < expected.size ()); + SELF_CHECK (&item == expected[i]); + + /* Access the item, to make sure the object is still alive. */ + SELF_CHECK (strcmp (item.name, expected[i]->name) == 0); + + ++i; + } + + SELF_CHECK (i == expected.size ()); + + for (typename ListType::reverse_iterator it = list.rbegin (); + it != list.rend (); ++it) + { + const item_type &item = *it; + + --i; + + SELF_CHECK (i >= 0); + SELF_CHECK (&item == expected[i]); + + /* Access the item, to make sure the object is still alive. */ + SELF_CHECK (strcmp (item.name, expected[i]->name) == 0); + } + + SELF_CHECK (i == 0); +} + +/* intrusive_list tests + + To run all tests using both the base and member methods, all tests are + declared in this templated class, which is instantiated once for each + list type. */ + using item_with_member_node = intrusive_member_node; using item_with_member_list = intrusive_list; -/* To run all tests using both the base and member methods, all tests are - declared in this templated class, which is instantiated once for each - list type. */ - template struct intrusive_list_test { using item_type = typename ListType::value_type; - /* Verify that LIST contains exactly the items in EXPECTED. - - Traverse the list forward and backwards to exercise all links. */ - - static void - verify_items (const ListType &list, - gdb::array_view expected) - { - int i = 0; - - for (typename ListType::iterator it = list.begin (); - it != list.end (); - ++it) - { - const item_type &item = *it; - - SELF_CHECK (i < expected.size ()); - SELF_CHECK (&item == expected[i]); - - ++i; - } - - SELF_CHECK (i == expected.size ()); - - for (typename ListType::reverse_iterator it = list.rbegin (); - it != list.rend (); - ++it) - { - const item_type &item = *it; - - --i; - - SELF_CHECK (i >= 0); - SELF_CHECK (&item == expected[i]); - } - - SELF_CHECK (i == 0); - } - static void test_move_constructor () { @@ -781,6 +807,811 @@ test_intrusive_list_1 () tests.test_begin_end (); } +/* owning_intrusive_list tests + + To run all tests using both the base and member methods, all tests are + declared in this templated class, which is instantiated once for each + list type. */ + +using item_with_base_owning_list = owning_intrusive_list; +using item_with_member_owning_list + = owning_intrusive_list; + +template +struct owning_intrusive_list_test +{ + using item_type = typename ListType::value_type; + + static void test_move_constructor () + { + { + /* Other list is not empty. */ + ListType list1; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + ListType list2 (std::move (list1)); + SELF_CHECK (items_alive == 3); + + expected = {}; + verify_items (list1, expected); + + expected = { &a, &b, &c }; + verify_items (list2, expected); + } + + { + /* Other list contains 1 element. */ + ListType list1; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + + SELF_CHECK (items_alive == 1); + ListType list2 (std::move (list1)); + SELF_CHECK (items_alive == 1); + + expected = {}; + verify_items (list1, expected); + + expected = { &a }; + verify_items (list2, expected); + } + + { + /* Other list is empty. */ + ListType list1; + std::vector expected; + + SELF_CHECK (items_alive == 0); + ListType list2 (std::move (list1)); + SELF_CHECK (items_alive == 0); + + expected = {}; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + } + + static void test_move_assignment () + { + { + /* Both lists are not empty. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + list2.emplace_back ("d"); + list2.emplace_back ("e"); + + SELF_CHECK (items_alive == 5); + list2 = std::move (list1); + SELF_CHECK (items_alive == 3); + + expected = {}; + verify_items (list1, expected); + + expected = { &a, &b, &c }; + verify_items (list2, expected); + } + + { + /* rhs list is empty. */ + ListType list1; + ListType list2; + std::vector expected; + + list2.emplace_back ("a"); + list2.emplace_back ("b"); + list2.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list2 = std::move (list1); + SELF_CHECK (items_alive == 0); + + expected = {}; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + + { + /* lhs list is empty. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list2 = std::move (list1); + SELF_CHECK (items_alive == 3); + + expected = {}; + verify_items (list1, expected); + + expected = { &a, &b, &c }; + verify_items (list2, expected); + } + + { + /* Both lists contain 1 item. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + list2.emplace_back ("b"); + + SELF_CHECK (items_alive == 2); + list2 = std::move (list1); + SELF_CHECK (items_alive == 1); + + expected = {}; + verify_items (list1, expected); + + expected = { &a }; + verify_items (list2, expected); + } + + { + /* Both lists are empty. */ + ListType list1; + ListType list2; + std::vector expected; + + SELF_CHECK (items_alive == 0); + list2 = std::move (list1); + SELF_CHECK (items_alive == 0); + + expected = {}; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + } + + static void test_swap () + { + { + /* Two non-empty lists. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + auto &d = list2.emplace_back ("d"); + auto &e = list2.emplace_back ("e"); + + SELF_CHECK (items_alive == 5); + std::swap (list1, list2); + SELF_CHECK (items_alive == 5); + + expected = { &d, &e }; + verify_items (list1, expected); + + expected = { &a, &b, &c }; + verify_items (list2, expected); + } + + { + /* Other is empty. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + std::swap (list1, list2); + SELF_CHECK (items_alive == 3); + + expected = {}; + verify_items (list1, expected); + + expected = { &a, &b, &c }; + verify_items (list2, expected); + } + + { + /* *this is empty. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list2.emplace_back ("a"); + auto &b = list2.emplace_back ("b"); + auto &c = list2.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + std::swap (list1, list2); + SELF_CHECK (items_alive == 3); + + expected = { &a, &b, &c }; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + + { + /* Both lists empty. */ + ListType list1; + ListType list2; + std::vector expected; + + SELF_CHECK (items_alive == 0); + std::swap (list1, list2); + SELF_CHECK (items_alive == 0); + + expected = {}; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + + { + /* Swap one element twice. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + + SELF_CHECK (items_alive == 1); + std::swap (list1, list2); + SELF_CHECK (items_alive == 1); + + expected = {}; + verify_items (list1, expected); + + expected = { &a }; + verify_items (list2, expected); + + std::swap (list1, list2); + SELF_CHECK (items_alive == 1); + + expected = { &a }; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + } + + static void test_front_back () + { + ListType list; + const ListType &clist = list; + + auto &a = list.emplace_back ("a"); + list.emplace_back ("b"); + auto &c = list.emplace_back ("c"); + + SELF_CHECK (&list.front () == &a); + SELF_CHECK (&clist.front () == &a); + SELF_CHECK (&list.back () == &c); + SELF_CHECK (&clist.back () == &c); + } + + static void test_push_front () + { + ListType list; + std::vector expected; + + SELF_CHECK (items_alive == 0); + list.push_front (std::make_unique ("a")); + auto &a = list.front (); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + list.push_front (std::make_unique ("b")); + auto &b = list.front (); + expected = { &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + list.push_front (std::make_unique ("c")); + auto &c = list.front (); + expected = { &c, &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + static void test_push_back () + { + ListType list; + std::vector expected; + + SELF_CHECK (items_alive == 0); + list.push_back (std::make_unique ("a")); + auto &a = list.back (); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + list.push_back (std::make_unique ("b")); + auto &b = list.back (); + expected = { &a, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + list.push_back (std::make_unique ("c")); + auto &c = list.back (); + expected = { &a, &b, &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + static void test_insert () + { + std::vector expected; + + { + /* Insert at beginning. */ + ListType list; + + auto &a = *list.insert (list.begin (), std::make_unique ("a")); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + auto &b = *list.insert (list.begin (), std::make_unique ("b")); + expected = { &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + auto &c = *list.insert (list.begin (), std::make_unique ("c")); + expected = { &c, &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + { + /* Insert at end. */ + ListType list; + + auto &a = *list.insert (list.end (), std::make_unique ("a")); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + auto &b = *list.insert (list.end (), std::make_unique ("b")); + expected = { &a, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + auto &c = *list.insert (list.end (), std::make_unique ("c")); + expected = { &a, &b, &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + { + /* Insert in the middle. */ + ListType list; + + auto &a = list.emplace_back ("a"); + auto &b = list.emplace_back ("b"); + + SELF_CHECK (items_alive == 2); + auto &c = *list.insert (list.iterator_to (b), + std::make_unique ("c")); + expected = { &a, &c, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + { + /* Insert in empty list. */ + ListType list; + + SELF_CHECK (items_alive == 0); + auto &a = *list.insert (list.end (), std::make_unique ("a")); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + } + } + + static void test_emplace_front () + { + ListType list; + std::vector expected; + + SELF_CHECK (items_alive == 0); + auto &a = list.emplace_front ("a"); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + auto &b = list.emplace_front ("b"); + expected = { &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + auto &c = list.emplace_front ("c"); + expected = { &c, &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + static void test_emplace_back () + { + ListType list; + std::vector expected; + + SELF_CHECK (items_alive == 0); + auto &a = list.emplace_back ("a"); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + auto &b = list.emplace_back ("b"); + expected = { &a, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + auto &c = list.emplace_back ("c"); + expected = { &a, &b, &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + static void test_emplace () + { + std::vector expected; + + { + /* Emplace at beginning. */ + ListType list; + + auto &a = list.emplace (list.begin (), "a"); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + auto &b = list.emplace (list.begin (), "b"); + expected = { &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + auto &c = list.emplace (list.begin (), "c"); + expected = { &c, &b, &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + { + /* Emplace at end. */ + ListType list; + + auto &a = list.emplace (list.end (), "a"); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + auto &b = list.emplace (list.end (), "b"); + expected = { &a, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + auto &c = list.emplace (list.end (), "c"); + expected = { &a, &b, &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + { + /* Emplace in the middle. */ + ListType list; + + auto &a = list.emplace_back ("a"); + auto &b = list.emplace_back ("b"); + + SELF_CHECK (items_alive == 2); + auto &c = list.emplace (list.iterator_to (b), "c"); + expected = { &a, &c, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + } + + { + /* Emplace in empty list. */ + ListType list; + + SELF_CHECK (items_alive == 0); + auto &a = list.emplace (list.end (), "a"); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + } + } + + static void test_splice () + { + { + /* Two non-empty lists. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + auto &d = list2.emplace_back ("d"); + auto &e = list2.emplace_back ("e"); + + SELF_CHECK (items_alive == 5); + list1.splice (std::move (list2)); + SELF_CHECK (items_alive == 5); + + expected = { &a, &b, &c, &d, &e }; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + + { + /* Receiving list empty. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list2.emplace_back ("a"); + auto &b = list2.emplace_back ("b"); + auto &c = list2.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list1.splice (std::move (list2)); + SELF_CHECK (items_alive == 3); + + expected = { &a, &b, &c }; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + + { + /* Giving list empty. */ + ListType list1; + ListType list2; + std::vector expected; + + auto &a = list1.emplace_back ("a"); + auto &b = list1.emplace_back ("b"); + auto &c = list1.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list1.splice (std::move (list2)); + SELF_CHECK (items_alive == 3); + + expected = { &a, &b, &c }; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + + { + /* Both lists empty. */ + ListType list1; + ListType list2; + std::vector expected; + + SELF_CHECK (items_alive == 0); + list1.splice (std::move (list2)); + SELF_CHECK (items_alive == 0); + + expected = {}; + verify_items (list1, expected); + + expected = {}; + verify_items (list2, expected); + } + } + + static void test_pop_front () + { + ListType list; + std::vector expected; + + list.emplace_back ("a"); + auto &b = list.emplace_back ("b"); + auto &c = list.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list.pop_front (); + expected = { &b, &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + list.pop_front (); + expected = { &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + list.pop_front (); + expected = {}; + verify_items (list, expected); + SELF_CHECK (items_alive == 0); + } + + static void test_pop_back () + { + ListType list; + std::vector expected; + + auto &a = list.emplace_back ("a"); + auto &b = list.emplace_back ("b"); + list.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list.pop_back (); + expected = { &a, &b }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + + list.pop_back (); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + + list.pop_back (); + expected = {}; + verify_items (list, expected); + SELF_CHECK (items_alive == 0); + } + + static void test_release () + { + ListType list; + std::vector expected; + + auto &a = list.emplace_back ("a"); + auto &b = list.emplace_back ("b"); + auto &c = list.emplace_back ("c"); + + { + SELF_CHECK (items_alive == 3); + auto [next_it, released] = list.release (list.iterator_to (b)); + SELF_CHECK (&*next_it == &c); + expected = { &a, &c }; + verify_items (list, expected); + SELF_CHECK (items_alive == 3); + released.reset (); + SELF_CHECK (items_alive == 2); + } + + { + auto [next_it, released] = list.release (list.iterator_to (c)); + SELF_CHECK (next_it == list.end ()); + expected = { &a }; + verify_items (list, expected); + SELF_CHECK (items_alive == 2); + released.reset (); + SELF_CHECK (items_alive == 1); + } + + { + auto [next_it, released] = list.release (list.iterator_to (a)); + SELF_CHECK (next_it == list.end ()); + expected = {}; + verify_items (list, expected); + SELF_CHECK (items_alive == 1); + released.reset (); + SELF_CHECK (items_alive == 0); + } + } + + static void test_clear () + { + ListType list; + std::vector expected; + + list.emplace_back ("a"); + list.emplace_back ("b"); + list.emplace_back ("c"); + + SELF_CHECK (items_alive == 3); + list.clear (); + expected = {}; + verify_items (list, expected); + SELF_CHECK (items_alive == 0); + + /* Verify idempotency. */ + list.clear (); + expected = {}; + verify_items (list, expected); + SELF_CHECK (items_alive == 0); + } + + static void test_empty () + { + ListType list; + + SELF_CHECK (list.empty ()); + auto &a = list.emplace_back ("a"); + SELF_CHECK (!list.empty ()); + list.erase (list.iterator_to (a)); + SELF_CHECK (list.empty ()); + } + + static void test_begin_end () + { + ListType list; + const ListType &clist = list; + + auto &a = list.emplace_back ("a"); + list.emplace_back ("b"); + auto &c = list.emplace_back ("c"); + + SELF_CHECK (&*list.begin () == &a); + SELF_CHECK (&*list.cbegin () == &a); + SELF_CHECK (&*clist.begin () == &a); + SELF_CHECK (&*list.rbegin () == &c); + SELF_CHECK (&*list.crbegin () == &c); + SELF_CHECK (&*clist.rbegin () == &c); + + /* At least check that they compile. */ + list.end (); + list.cend (); + clist.end (); + list.rend (); + list.crend (); + clist.end (); + } +}; + +template +static void +test_owning_intrusive_list_1 () +{ + owning_intrusive_list_test tests; + + tests.test_move_constructor (); + tests.test_move_assignment (); + tests.test_swap (); + tests.test_front_back (); + tests.test_push_front (); + tests.test_push_back (); + tests.test_insert (); + tests.test_emplace_front (); + tests.test_emplace_back (); + tests.test_emplace (); + tests.test_splice (); + tests.test_pop_front (); + tests.test_pop_back (); + tests.test_release (); + tests.test_clear (); + tests.test_empty (); + tests.test_begin_end (); +} + static void test_node_is_linked () { @@ -812,13 +1643,15 @@ test_intrusive_list () { test_intrusive_list_1 (); test_intrusive_list_1 (); + test_owning_intrusive_list_1 (); + test_owning_intrusive_list_1 (); test_node_is_linked (); } void _initialize_intrusive_list_selftests (); + void _initialize_intrusive_list_selftests () { - selftests::register_test - ("intrusive_list", test_intrusive_list); + selftests::register_test ("intrusive_list", test_intrusive_list); } diff --git a/gdbsupport/intrusive_list.h b/gdbsupport/intrusive_list.h index bfa06fc5604..345fd7d18ad 100644 --- a/gdbsupport/intrusive_list.h +++ b/gdbsupport/intrusive_list.h @@ -395,13 +395,13 @@ class intrusive_list void pop_front () noexcept { gdb_assert (!this->empty ()); - erase_element (*m_front); + unlink_element (*m_front); } void pop_back () noexcept { gdb_assert (!this->empty ()); - erase_element (*m_back); + unlink_element (*m_back); } private: @@ -461,7 +461,8 @@ class intrusive_list return this->iterator_to (elem); } - void erase_element (reference elem) noexcept +protected: + void unlink_element (reference elem) noexcept { intrusive_list_node *elem_node = as_node (&elem); @@ -504,7 +505,7 @@ class intrusive_list iterator ret = i; ++ret; - erase_element (*i); + unlink_element (*i); return ret; } diff --git a/gdbsupport/owning_intrusive_list.h b/gdbsupport/owning_intrusive_list.h new file mode 100644 index 00000000000..4c72d9bdf2c --- /dev/null +++ b/gdbsupport/owning_intrusive_list.h @@ -0,0 +1,168 @@ +/* Owning version of intrusive_list for GDB, the GNU debugger. + Copyright (C) 2024 Free Software Foundation, Inc. + + This file is part of GDB. + + This program 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 of the License, or + (at your option) any later version. + + This program 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDBSUPPORT_OWNING_INTRUSIVE_LIST_H +#define GDBSUPPORT_OWNING_INTRUSIVE_LIST_H + +#include "intrusive_list.h" + +/* An owning version of intrusive_list. */ + +template> +class owning_intrusive_list : private intrusive_list +{ + using base = intrusive_list; + +public: + using value_type = typename base::value_type; + using reference = typename base::reference; + using iterator = typename base::iterator; + using reverse_iterator = typename base::reverse_iterator; + using const_iterator = typename base::const_iterator; + using unique_pointer = std::unique_ptr; + + using base::iterator_to; + using base::front; + using base::back; + using base::empty; + using base::begin; + using base::cbegin; + using base::end; + using base::cend; + using base::rbegin; + using base::crbegin; + using base::rend; + using base::crend; + + owning_intrusive_list () noexcept = default; + + owning_intrusive_list (owning_intrusive_list &&other) noexcept + : base (std::move (other)) + { + } + + ~owning_intrusive_list () + { this->clear (); } + + owning_intrusive_list &operator= (owning_intrusive_list &&other) noexcept + { + this->clear (); + this->base::operator= (std::move (other)); + return *this; + } + + void swap (owning_intrusive_list &other) noexcept + { this->base::swap (other); } + + /* Insert ELEM at the front of the list. + + The list takes ownership of ELEM. */ + void push_front (unique_pointer elem) noexcept + { this->base::push_front (*elem.release ()); } + + /* Insert ELEM at the back of the list. + + The list takes ownership of ELEM. */ + void push_back (unique_pointer elem) noexcept + { this->base::push_back (*elem.release ()); } + + /* Insert ELEM before POS in the list. + + The list takes ownership of ELEM. */ + iterator insert (const_iterator pos, unique_pointer elem) noexcept + { return this->base::insert (pos, *elem.release ()); } + + void splice (owning_intrusive_list &&other) noexcept + { this->base::splice (std::move (other)); } + + /* Remove the element at the front of the list. The element is destroyed. */ + void pop_front () noexcept + { + unique_pointer holder (&this->front ()); + this->base::pop_front (); + } + + /* Remove the element at the back of the list. The element is destroyed. */ + void pop_back () noexcept + { + unique_pointer holder (&this->back ()); + this->base::pop_back (); + } + + /* Remove the element pointed by I from the list. The element + pointed by I is destroyed. */ + iterator erase (const_iterator i) noexcept + { + unique_pointer holder (&*i); + return this->base::erase (i); + } + + /* Remove all elements from the list. All elements are destroyed. */ + void clear () noexcept + { + while (!this->empty ()) + this->pop_front (); + } + + /* Construct an element in-place at the front of the list. + + Return a reference to the new element. */ + template + reference emplace_front (Args &&...args) + { return this->emplace (this->begin (), std::forward (args)...); } + + /* Construct an element in-place at the back of the list. + + Return a reference to the new element. */ + template + reference emplace_back (Args &&...args) + { return this->emplace (this->end (), std::forward (args)...); } + + /* Construct an element in-place in the list, before POS. + + Return a reference to the new element. */ + template + reference emplace (const_iterator pos, Args &&...args) + { + return *this->insert (pos, + std::make_unique (std::forward (args)...)); + } + + /* Return type for the release method. */ + struct release_ret + { + /* Iterator to the following element in the list. */ + iterator next; + + /* The released element. */ + unique_pointer released; + }; + + release_ret release (const_iterator i) noexcept + { + iterator next = i; + ++next; + unique_pointer released (&*i); + + this->unlink_element (*i); + + return { next, std::move (released) }; + } +}; + +#endif /* GDBSUPPORT_OWNING_INTRUSIVE_LIST_H */