[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:
committed by
Space
parent
6ae55a98cd
commit
03b09346a5
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user