[K/N] Intrusive single linked list ^KT-51436

Merge-request: KT-MR-5854
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
Alexander Shabalin
2022-03-14 13:20:41 +00:00
committed by Space
parent 6ae55a98cd
commit 03b09346a5
2 changed files with 908 additions and 0 deletions
@@ -0,0 +1,284 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#pragma once
#include <cstddef>
#include <iterator>
#include <limits>
#include "KAssert.h"
#include "Utils.hpp"
namespace kotlin {
template <typename T>
struct DefaultIntrusiveForwardListTraits {
static T* next(const T& value) noexcept { return value.next_; }
static void setNext(T& value, T* next) noexcept { value.next_ = next; }
};
// Intrusive variant of `std::forward_list`. Notable differences:
// * The container does not own nodes. Care must be taken not to allow a node
// to be in two containers at once, or twice into the same container.
// * The container is move-only, and moving invalidates `before_begin` iterator.
// * insert_after, erase_after take `iterator` instead of `const_iterator`, because
// they do in fact require mutability via `Traits::setNext`.
// * When the node leaves the container, nothing clears `next` pointer inside it.
//
// `Traits` must have 2 methods:
// static T* next(const T& value);
// static void setNext(T& value, T* next);
// NOTE: `setNext` and `next` must be callable even on uninitialized `T` (i.e. they
// should only access storage inside `T`).
template <typename T, typename Traits = DefaultIntrusiveForwardListTraits<T>>
class intrusive_forward_list : private MoveOnly {
public:
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
class iterator {
public:
using difference_type = intrusive_forward_list::difference_type;
using value_type = intrusive_forward_list::value_type;
using pointer = intrusive_forward_list::pointer;
using reference = intrusive_forward_list::reference;
using iterator_category = std::forward_iterator_tag;
iterator() noexcept = default;
iterator(const iterator&) noexcept = default;
iterator& operator=(const iterator&) noexcept = default;
reference operator*() noexcept { return *node_; }
pointer operator->() noexcept { return node_; }
iterator& operator++() noexcept {
node_ = next(node_);
return *this;
}
iterator operator++(int) noexcept {
auto result = *this;
++(*this);
return result;
}
bool operator==(const iterator& rhs) const noexcept { return node_ == rhs.node_; }
bool operator!=(const iterator& rhs) const noexcept { return !(*this == rhs); }
private:
friend class intrusive_forward_list;
explicit iterator(pointer node) noexcept : node_(node) {}
intrusive_forward_list::pointer node_ = nullptr;
};
class const_iterator {
public:
using difference_type = intrusive_forward_list::difference_type;
using value_type = const intrusive_forward_list::value_type;
using pointer = intrusive_forward_list::const_pointer;
using reference = intrusive_forward_list::const_reference;
using iterator_category = std::forward_iterator_tag;
const_iterator() noexcept = default;
const_iterator(const const_iterator&) noexcept = default;
const_iterator& operator=(const const_iterator&) noexcept = default;
const_iterator(iterator it) noexcept : node_(it.node_) {}
reference operator*() noexcept { return *node_; }
pointer operator->() noexcept { return node_; }
const_iterator& operator++() noexcept {
node_ = next(node_);
return *this;
}
const_iterator operator++(int) noexcept {
auto result = *this;
++(*this);
return result;
}
bool operator==(const const_iterator& rhs) const noexcept { return node_ == rhs.node_; }
bool operator!=(const const_iterator& rhs) const noexcept { return !(*this == rhs); }
private:
friend class intrusive_forward_list;
explicit const_iterator(pointer node) noexcept : node_(node) {}
pointer node_ = nullptr;
};
intrusive_forward_list() noexcept {
setNext(head(), nullptr);
}
intrusive_forward_list(intrusive_forward_list&& rhs) noexcept : size_(rhs.size_) {
setNext(head(), next(rhs.head()));
setNext(rhs.head(), nullptr);
rhs.size_ = 0;
}
template <typename InputIt>
intrusive_forward_list(InputIt first, InputIt last) noexcept {
setNext(head(), nullptr);
assign(std::move(first), std::move(last));
}
~intrusive_forward_list() = default;
intrusive_forward_list& operator=(intrusive_forward_list&& rhs) noexcept {
intrusive_forward_list tmp(std::move(rhs));
swap(tmp);
return *this;
}
void swap(intrusive_forward_list& rhs) noexcept {
using std::swap;
auto thisNext = next(head());
auto rhsNext = next(rhs.head());
swap(thisNext, rhsNext);
setNext(head(), thisNext);
setNext(rhs.head(), rhsNext);
swap(size_, rhs.size_);
}
template <typename InputIt>
void assign(InputIt first, InputIt last) noexcept {
clear();
insert_after(before_begin(), std::move(first), std::move(last));
}
reference front() noexcept { return *next(head()); }
const_reference front() const noexcept { return *next(head()); }
iterator before_begin() noexcept { return iterator(head()); }
const_iterator before_begin() const noexcept { return const_iterator(head()); }
const_iterator cbefore_begin() const noexcept { return const_iterator(head()); }
iterator begin() noexcept { return iterator(next(head())); }
const_iterator begin() const noexcept { return const_iterator(next(head())); }
const_iterator cbegin() const noexcept { return const_iterator(next(head())); }
iterator end() noexcept { return iterator(); }
const_iterator end() const noexcept { return const_iterator(); }
const_iterator cend() const noexcept { return const_iterator(); }
bool empty() const noexcept { return size_ == 0; }
size_type max_size() const noexcept { return std::numeric_limits<size_type>::max(); }
void clear() noexcept { setNext(head(), nullptr); size_ = 0; }
iterator insert_after(iterator pos, reference value) noexcept {
pointer nextNode = next(pos.node_);
setNext(pos.node_, &value);
setNext(&value, nextNode);
++size_;
return iterator(&value);
}
template <typename InputIt>
iterator insert_after(iterator pos, InputIt first, InputIt last) noexcept {
pointer nextNode = next(pos.node_);
pointer prevNode = pos.node_;
size_t newSize = size_;
for (auto it = first; it != last; ++it) {
setNext(prevNode, &*it);
prevNode = &*it;
++newSize;
}
setNext(prevNode, nextNode);
size_ = newSize;
return iterator(prevNode);
}
iterator erase_after(iterator pos) noexcept {
pointer prevNode = pos.node_;
pointer nodeToErase = next(pos.node_);
if (!nodeToErase) {
return end();
}
pointer nextNode = next(nodeToErase);
setNext(prevNode, nextNode);
setNext(nodeToErase, nullptr);
--size_;
return iterator(nextNode);
}
iterator erase_after(iterator first, iterator last) noexcept {
size_ -= std::distance(first, last) - 1;
setNext(first.node_, last.node_);
return last;
}
void push_front(reference value) noexcept { insert_after(before_begin(), value); }
void pop_front() noexcept { erase_after(before_begin()); }
void remove(reference value) noexcept {
// TODO: no need to move on after finding the first match.
return remove_if([&value](const_reference x) { return &x == &value; });
}
template <typename P>
void remove_if(P p) noexcept {
size_t newSize = size_;
pointer prev = head();
pointer node = next(prev);
while (node) {
if (p(*node)) {
// The node is being removed.
node = next(node);
setNext(prev, node);
--newSize;
} else {
// The node is staying.
prev = node;
node = next(node);
}
}
size_ = newSize;
}
// TODO: Implement splice_after.
size_type size() const noexcept {
return size_;
}
private:
static pointer next(const_pointer node) noexcept { return Traits::next(*node); }
static void setNext(pointer node, pointer next) noexcept { return Traits::setNext(*node, next); }
pointer head() noexcept {
return reinterpret_cast<pointer>(headStorage_);
}
const_pointer head() const noexcept {
return reinterpret_cast<const_pointer>(headStorage_);
}
alignas(value_type) char headStorage_[sizeof(value_type)] = { 0 };
size_t size_ = 0;
};
template <typename InputIt>
intrusive_forward_list(InputIt, InputIt) -> intrusive_forward_list<typename std::iterator_traits<InputIt>::value_type>;
} // namespace kotlin
@@ -0,0 +1,624 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "IntrusiveList.hpp"
#include <forward_list>
#include <type_traits>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "Types.h"
#include "Utils.hpp"
using namespace kotlin;
namespace {
class Element {
public:
explicit Element(int value) : value_(value) {}
Element(const Element&) = default;
Element(Element&&) = default;
Element& operator=(const Element&) = default;
Element& operator=(Element&&) = default;
int& operator*() { return value_; }
const int& operator*() const { return value_; }
bool operator==(const Element& rhs) const { return value_ == rhs.value_; }
bool operator!=(const Element& rhs) const { return !(*this == rhs); }
private:
int value_;
};
class Node : private Pinned {
public:
explicit Node(int value) : value_(value) {}
int& operator*() { return value_; }
const int& operator*() const { return value_; }
private:
friend struct DefaultIntrusiveForwardListTraits<Node>;
int value_;
// Use non-null marker to make sure inserting into the list properly updates this value.
Node* next_ = reinterpret_cast<Node*>(0x1);
};
template <typename List>
KStdList<typename List::value_type> create(std::initializer_list<int> list) {
KStdList<typename List::value_type> result;
for (auto x : list) {
result.emplace_back(x);
}
return result;
}
MATCHER_P(isEmpty, expected, (expected == !negation) ? "is empty" : "is not empty") {
bool actual = arg.empty();
*result_listener << (actual ? "is empty" : "is not empty");
return expected == actual;
}
size_t getSize(const std::forward_list<Element>& list) {
return std::distance(list.begin(), list.end());
}
size_t getSize(const intrusive_forward_list<Node>& list) {
return list.size();
}
MATCHER_P(hasSize, expected, "") {
size_t actual = getSize(arg);
*result_listener << "of size " << actual;
return expected == actual;
}
MATCHER_P(derefsTo, expected, "") {
const auto& actual = *arg;
*result_listener << "derefs to " << testing::PrintToString(actual);
return expected == actual;
}
template <typename List, typename... Args>
auto elementsAre(Args... args) {
return testing::AllOf(
isEmpty(sizeof...(args) == 0),
hasSize(sizeof...(args)),
testing::ElementsAre(derefsTo(args)...)
);
}
#define EXPECT_ELEMENTS_ARE(list, ...) \
EXPECT_THAT(list, ::elementsAre<decltype(list)>(__VA_ARGS__));
} // namespace
TEST(IntrusiveForwardListTest, CTAD) {
auto values = create<intrusive_forward_list<Node>>({1, 2, 3, 4});
intrusive_forward_list list(values.begin(), values.end());
static_assert(std::is_same_v<decltype(list)::value_type, Node>);
}
// Testing that operations on `intrusive_forward_list` give the same results as those on `std::forward_list`.
template <typename T>
class ForwardListTest : public testing::Test {};
using ForwardListTestTypes = testing::Types<std::forward_list<Element>, intrusive_forward_list<Node>>;
struct ForwardListTestNames {
template <typename T>
static std::string GetName(int) {
if constexpr (std::is_same_v<T, std::forward_list<Element>>) {
return "forward_list";
} else if constexpr (std::is_same_v<T, intrusive_forward_list<Node>>) {
return "intrusive_forward_list";
} else {
return "unknown";
}
}
};
TYPED_TEST_SUITE(ForwardListTest, ForwardListTestTypes, ForwardListTestNames);
TYPED_TEST(ForwardListTest, DefaultCtor) {
using List = TypeParam;
List list;
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, RangeCtor) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, RangeCtorEmpty) {
using List = TypeParam;
auto values = create<List>({});
List list(values.begin(), values.end());
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, MoveCtor) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
List otherList(std::move(list));
EXPECT_ELEMENTS_ARE(list);
EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, MoveAssignment) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto otherValues = create<List>({5, 6, 7, 8});
List otherList(otherValues.begin(), otherValues.end());
otherList = std::move(list);
EXPECT_ELEMENTS_ARE(list);
EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, MutableIterator) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
for (auto& x : list) {
*x += 5;
}
EXPECT_ELEMENTS_ARE(list, 6, 7, 8, 9);
}
TYPED_TEST(ForwardListTest, MutableIteratorEmpty) {
using List = TypeParam;
List list;
for (auto& x : list) {
*x += 5;
}
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, BeforeBeginIterator) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = list.before_begin();
++it;
EXPECT_THAT(it, list.begin());
}
TYPED_TEST(ForwardListTest, BeforeBeginIteratorEmpty) {
using List = TypeParam;
List list;
auto it = list.before_begin();
++it;
EXPECT_THAT(it, list.end());
}
TYPED_TEST(ForwardListTest, MoveAssignmentIntoEmpty) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
List otherList;
otherList = std::move(list);
EXPECT_ELEMENTS_ARE(list);
EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, MoveAssignmentFromEmpty) {
using List = TypeParam;
List list;
auto otherValues = create<List>({5, 6, 7, 8});
List otherList(otherValues.begin(), otherValues.end());
otherList = std::move(list);
EXPECT_ELEMENTS_ARE(list);
EXPECT_ELEMENTS_ARE(otherList);
}
TYPED_TEST(ForwardListTest, Swap) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto otherValues = create<List>({5, 6, 7, 8});
List otherList(otherValues.begin(), otherValues.end());
using std::swap;
swap(list, otherList);
EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8);
EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, SwapFirstEmpty) {
using List = TypeParam;
List list;
auto otherValues = create<List>({5, 6, 7, 8});
List otherList(otherValues.begin(), otherValues.end());
using std::swap;
swap(list, otherList);
EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8);
EXPECT_ELEMENTS_ARE(otherList);
}
TYPED_TEST(ForwardListTest, SwapSecondEmpty) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
List otherList;
using std::swap;
swap(list, otherList);
EXPECT_ELEMENTS_ARE(list);
EXPECT_ELEMENTS_ARE(otherList, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, SwapBothEmpty) {
using List = TypeParam;
List list;
List otherList;
using std::swap;
swap(list, otherList);
EXPECT_ELEMENTS_ARE(list);
EXPECT_ELEMENTS_ARE(otherList);
}
TYPED_TEST(ForwardListTest, Assign) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto newValues = create<List>({5, 6, 7, 8});
list.assign(newValues.begin(), newValues.end());
EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8);
}
TYPED_TEST(ForwardListTest, AssignFromEmpty) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto newValues = create<List>({});
list.assign(newValues.begin(), newValues.end());
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, AssignIntoEmpty) {
using List = TypeParam;
List list;
auto newValues = create<List>({5, 6, 7, 8});
list.assign(newValues.begin(), newValues.end());
EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8);
}
TYPED_TEST(ForwardListTest, Front) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
EXPECT_THAT(*list.front(), 1);
const List& constList = list;
EXPECT_THAT(*constList.front(), 1);
}
TYPED_TEST(ForwardListTest, Clear) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
list.clear();
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, ClearEmpty) {
using List = TypeParam;
List list;
list.clear();
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, PushFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
typename List::value_type value(5);
list.push_front(value);
EXPECT_ELEMENTS_ARE(list, 5, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, PushFrontEmpty) {
using List = TypeParam;
List list;
typename List::value_type value(5);
list.push_front(value);
EXPECT_ELEMENTS_ARE(list, 5);
}
TYPED_TEST(ForwardListTest, PopFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
list.pop_front();
EXPECT_ELEMENTS_ARE(list, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, PopFrontIntoEmpty) {
using List = TypeParam;
auto values = create<List>({1});
List list(values.begin(), values.end());
list.pop_front();
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, Remove) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto& value = *std::next(list.begin(), 2);
ASSERT_THAT(*value, 3);
list.remove(value);
EXPECT_ELEMENTS_ARE(list, 1, 2, 4);
}
TYPED_TEST(ForwardListTest, RemoveMissing) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
typename List::value_type value(5);
list.remove(value);
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, RemoveAll) {
using List = TypeParam;
auto values = create<List>({1});
List list(values.begin(), values.end());
auto& value = list.front();
list.remove(value);
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, RemoveEmpty) {
using List = TypeParam;
List list;
typename List::value_type value(5);
list.remove(value);
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, RemoveIf) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
list.remove_if([](const auto& x) { return *x == 3; });
EXPECT_ELEMENTS_ARE(list, 1, 2, 4);
}
TYPED_TEST(ForwardListTest, RemoveIfMissing) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
list.remove_if([](const auto& x) { return *x == 5; });
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, RemoveIfAll) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
list.remove_if([](const auto& x) { return true; });
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, RemoveIfEmpty) {
using List = TypeParam;
List list;
list.remove_if([](const auto& x) { return *x == 3; });
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, RemoveIfAllEmpty) {
using List = TypeParam;
List list;
list.remove_if([](const auto& x) { return true; });
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, InsertAfter) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 2);
ASSERT_THAT(**it, 3);
typename List::value_type value(5);
auto result = list.insert_after(it, value);
EXPECT_THAT(result, std::next(list.begin(), 3));
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 5, 4);
}
TYPED_TEST(ForwardListTest, InsertAfterFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = list.before_begin();
typename List::value_type value(5);
auto result = list.insert_after(it, value);
EXPECT_THAT(result, list.begin());
EXPECT_ELEMENTS_ARE(list, 5, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, InsertAfterFrontEmpty) {
using List = TypeParam;
List list;
auto it = list.before_begin();
typename List::value_type value(5);
auto result = list.insert_after(it, value);
EXPECT_THAT(result, list.begin());
EXPECT_ELEMENTS_ARE(list, 5);
}
TYPED_TEST(ForwardListTest, InsertAfterRange) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 2);
ASSERT_THAT(**it, 3);
auto otherValues = create<List>({5, 6, 7, 8});
auto result = list.insert_after(it, otherValues.begin(), otherValues.end());
EXPECT_THAT(result, std::next(list.begin(), 6));
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 5, 6, 7, 8, 4);
}
TYPED_TEST(ForwardListTest, InsertAfterEmptyRange) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 2);
ASSERT_THAT(**it, 3);
auto otherValues = create<List>({});
auto result = list.insert_after(it, otherValues.begin(), otherValues.end());
EXPECT_THAT(result, std::next(list.begin(), 2));
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, InsertAfterRangeFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = list.before_begin();
auto otherValues = create<List>({5, 6, 7, 8});
auto result = list.insert_after(it, otherValues.begin(), otherValues.end());
EXPECT_THAT(result, std::next(list.begin(), 3));
EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, InsertAfterEmptyRangeFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = list.before_begin();
auto otherValues = create<List>({});
auto result = list.insert_after(it, otherValues.begin(), otherValues.end());
EXPECT_THAT(result, list.before_begin());
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, InsertAfterRangeFrontEmpty) {
using List = TypeParam;
List list;
auto it = list.before_begin();
auto otherValues = create<List>({5, 6, 7, 8});
auto result = list.insert_after(it, otherValues.begin(), otherValues.end());
EXPECT_THAT(result, std::next(list.begin(), 3));
EXPECT_ELEMENTS_ARE(list, 5, 6, 7, 8);
}
TYPED_TEST(ForwardListTest, InsertAfterEmptyRangeFrontEmpty) {
using List = TypeParam;
List list;
auto it = list.before_begin();
auto otherValues = create<List>({});
auto result = list.insert_after(it, otherValues.begin(), otherValues.end());
EXPECT_THAT(result, list.before_begin());
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, EraseAfter) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 1);
ASSERT_THAT(**it, 2);
auto result = list.erase_after(it);
EXPECT_THAT(result, std::next(list.begin(), 2));
EXPECT_ELEMENTS_ARE(list, 1, 2, 4);
}
TYPED_TEST(ForwardListTest, EraseAfterNearEnd) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 2);
ASSERT_THAT(**it, 3);
auto result = list.erase_after(it);
EXPECT_THAT(result, list.end());
EXPECT_ELEMENTS_ARE(list, 1, 2, 3);
}
TYPED_TEST(ForwardListTest, EraseAfterFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = list.before_begin();
auto result = list.erase_after(it);
EXPECT_THAT(result, list.begin());
EXPECT_ELEMENTS_ARE(list, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, EraseAfterToEmpty) {
using List = TypeParam;
auto values = create<List>({1});
List list(values.begin(), values.end());
auto it = list.before_begin();
auto result = list.erase_after(it);
EXPECT_THAT(result, list.end());
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, EraseAfterRange) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 2);
ASSERT_THAT(**it, 3);
auto result = list.erase_after(list.begin(), it);
EXPECT_THAT(result, std::next(list.begin(), 1));
EXPECT_ELEMENTS_ARE(list, 1, 3, 4);
}
TYPED_TEST(ForwardListTest, EraseAfterRangeToEnd) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto result = list.erase_after(list.begin(), list.end());
EXPECT_THAT(result, list.end());
EXPECT_ELEMENTS_ARE(list, 1);
}
TYPED_TEST(ForwardListTest, EraseAfterEmptyRange) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto result = list.erase_after(std::next(list.begin(), 1), std::next(list.begin(), 2));
EXPECT_THAT(result, std::next(list.begin(), 2));
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}
TYPED_TEST(ForwardListTest, EraseAfterRangeFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto it = std::next(list.begin(), 2);
ASSERT_THAT(**it, 3);
auto result = list.erase_after(list.before_begin(), it);
EXPECT_THAT(result, list.begin());
EXPECT_ELEMENTS_ARE(list, 3, 4);
}
TYPED_TEST(ForwardListTest, EraseAfterRangeFrontToEnd) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto result = list.erase_after(list.before_begin(), list.end());
EXPECT_THAT(result, list.end());
EXPECT_ELEMENTS_ARE(list);
}
TYPED_TEST(ForwardListTest, EraseAfterEmptyRangeFront) {
using List = TypeParam;
auto values = create<List>({1, 2, 3, 4});
List list(values.begin(), values.end());
auto result = list.erase_after(list.before_begin(), list.begin());
EXPECT_THAT(result, list.begin());
EXPECT_ELEMENTS_ARE(list, 1, 2, 3, 4);
}