mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-16 22:16:53 +07:00
Merge pull request #37 from enduro2d/feature/intrusive_list
Feature/intrusive_list
This commit is contained in:
@@ -13,11 +13,12 @@
|
||||
#include "color32.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "filesystem.inl"
|
||||
#include "ilist.hpp"
|
||||
#include "image.hpp"
|
||||
#include "iptr.hpp"
|
||||
#include "mesh.hpp"
|
||||
#include "module.hpp"
|
||||
#include "path.hpp"
|
||||
#include "refcount.hpp"
|
||||
#include "streams.hpp"
|
||||
#include "strfmts.hpp"
|
||||
#include "strings.hpp"
|
||||
|
||||
@@ -28,6 +28,9 @@ namespace e2d
|
||||
template < typename T >
|
||||
class intrusive_ptr;
|
||||
|
||||
template < typename T, typename Tag >
|
||||
class intrusive_list;
|
||||
|
||||
template < typename Char >
|
||||
class basic_string_hash;
|
||||
}
|
||||
|
||||
463
headers/enduro2d/utils/ilist.hpp
Normal file
463
headers/enduro2d/utils/ilist.hpp
Normal file
@@ -0,0 +1,463 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename Tag >
|
||||
class intrusive_list_hook;
|
||||
|
||||
template < typename Tag, typename T, typename TP, typename TR >
|
||||
class intrusive_list_iterator;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename Tag >
|
||||
class intrusive_list_hook {
|
||||
public:
|
||||
using node_ptr = intrusive_list_hook*;
|
||||
using const_node_ptr = const intrusive_list_hook*;
|
||||
|
||||
template < typename U, typename UTag >
|
||||
friend class intrusive_list;
|
||||
|
||||
template < typename UTag, typename U, typename UP, typename UR >
|
||||
friend class intrusive_list_iterator;
|
||||
public:
|
||||
intrusive_list_hook() = default;
|
||||
|
||||
~intrusive_list_hook() noexcept {
|
||||
if ( is_linked() ) {
|
||||
unlink();
|
||||
}
|
||||
}
|
||||
|
||||
intrusive_list_hook(node_ptr prev, node_ptr next) noexcept
|
||||
: prev_(prev)
|
||||
, next_(next) {}
|
||||
|
||||
intrusive_list_hook(const intrusive_list_hook& other) noexcept {
|
||||
E2D_UNUSED(other);
|
||||
}
|
||||
|
||||
intrusive_list_hook& operator=(const intrusive_list_hook& other) noexcept {
|
||||
E2D_UNUSED(other);
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
bool unique() const noexcept {
|
||||
return !next_ || next_ == this;
|
||||
}
|
||||
|
||||
bool is_linked() const noexcept {
|
||||
return !unique();
|
||||
}
|
||||
|
||||
void link_before(node_ptr next) noexcept {
|
||||
node_ptr prev = next->prev_;
|
||||
prev_ = prev;
|
||||
next_ = next;
|
||||
next->prev_ = this;
|
||||
prev->next_ = this;
|
||||
}
|
||||
|
||||
void link_after(node_ptr prev) noexcept {
|
||||
node_ptr next = prev->next_;
|
||||
next_ = next;
|
||||
prev_ = prev;
|
||||
prev->next_ = this;
|
||||
next->prev_ = this;
|
||||
}
|
||||
|
||||
void unlink() noexcept {
|
||||
node_ptr next = next_;
|
||||
node_ptr prev = prev_;
|
||||
prev->next_ = next;
|
||||
next->prev_ = prev;
|
||||
prev_ = nullptr;
|
||||
next_ = nullptr;
|
||||
}
|
||||
|
||||
static void swap_prev(node_ptr l, node_ptr r){
|
||||
node_ptr tmp = l->prev_;
|
||||
l->prev_ = r->prev_;
|
||||
r->prev_ = tmp;
|
||||
}
|
||||
|
||||
static void swap_next(node_ptr l, node_ptr r){
|
||||
node_ptr tmp = l->next_;
|
||||
l->next_ = r->next_;
|
||||
r->next_ = tmp;
|
||||
}
|
||||
|
||||
static void swap_nodes(node_ptr l, node_ptr r) noexcept {
|
||||
if ( l != r ) {
|
||||
node_ptr prev_l = l->prev_;
|
||||
node_ptr next_l = l->next_;
|
||||
node_ptr prev_r = r->prev_;
|
||||
node_ptr next_r = r->next_;
|
||||
swap_prev(next_l, next_r);
|
||||
swap_next(prev_l, prev_r);
|
||||
swap_prev(l, r);
|
||||
swap_next(l, r);
|
||||
}
|
||||
}
|
||||
private:
|
||||
node_ptr prev_{nullptr};
|
||||
node_ptr next_{nullptr};
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename Tag, typename T, typename TP, typename TR >
|
||||
class intrusive_list_iterator final
|
||||
: public std::iterator<
|
||||
std::bidirectional_iterator_tag,
|
||||
T,
|
||||
ptrdiff_t,
|
||||
TP,
|
||||
TR>
|
||||
{
|
||||
public:
|
||||
using self_type = intrusive_list_iterator;
|
||||
|
||||
using node_t = intrusive_list_hook<Tag>;
|
||||
using node_ptr = typename node_t::node_ptr;
|
||||
using const_node_ptr = typename node_t::const_node_ptr;
|
||||
|
||||
template < typename UTag, typename U, typename UP, typename UR >
|
||||
friend class intrusive_list_iterator;
|
||||
public:
|
||||
intrusive_list_iterator() = default;
|
||||
|
||||
intrusive_list_iterator(const_node_ptr node) noexcept
|
||||
: node_(const_cast<node_ptr>(node)) {}
|
||||
|
||||
template < typename UTP, typename UTR >
|
||||
intrusive_list_iterator(const intrusive_list_iterator<Tag,T,UTP,UTR>& other) noexcept
|
||||
: node_(const_cast<node_ptr>(other.node_)) {}
|
||||
|
||||
node_ptr node() noexcept {
|
||||
return node_;
|
||||
}
|
||||
|
||||
const_node_ptr node() const noexcept {
|
||||
return node_;
|
||||
}
|
||||
|
||||
self_type& operator++() noexcept {
|
||||
node_ = node_->next_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const self_type operator++(int) noexcept {
|
||||
self_type tmp(*this);
|
||||
node_ = node_->next_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
self_type& operator--() noexcept {
|
||||
node_ = node_->prev_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const self_type operator--(int) noexcept {
|
||||
self_type tmp(*this);
|
||||
node_ = node_->prev_;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
TR operator*() const noexcept {
|
||||
E2D_ASSERT(node_);
|
||||
return static_cast<TR>(*node_);
|
||||
}
|
||||
|
||||
TP operator->() const noexcept {
|
||||
E2D_ASSERT(node_);
|
||||
return static_cast<TP>(node_);
|
||||
}
|
||||
private:
|
||||
node_ptr node_{nullptr};
|
||||
};
|
||||
|
||||
template < typename Tag, typename T, typename LTP, typename LTR, typename RTP, typename RTR >
|
||||
bool operator==(
|
||||
const intrusive_list_iterator<Tag,T,LTP,LTR>& l,
|
||||
const intrusive_list_iterator<Tag,T,RTP,RTR>& r) noexcept
|
||||
{
|
||||
return l.node() == r.node();
|
||||
}
|
||||
|
||||
template < typename Tag, typename T, typename LTP, typename LTR, typename RTP, typename RTR >
|
||||
bool operator!=(
|
||||
const intrusive_list_iterator<Tag,T,LTP,LTR>& l,
|
||||
const intrusive_list_iterator<Tag,T,RTP,RTR>& r) noexcept
|
||||
{
|
||||
return l.node() != r.node();
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename T, typename Tag >
|
||||
class intrusive_list final {
|
||||
public:
|
||||
using self_type = intrusive_list;
|
||||
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using const_pointer = const T*;
|
||||
using reference = T&;
|
||||
using const_reference = const T&;
|
||||
|
||||
using iterator = intrusive_list_iterator<Tag, T, pointer, reference>;
|
||||
using const_iterator = intrusive_list_iterator<Tag, T, const_pointer, const_reference>;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
public:
|
||||
intrusive_list() = default;
|
||||
~intrusive_list() noexcept;
|
||||
|
||||
iterator begin() noexcept;
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator cbegin() const noexcept;
|
||||
|
||||
iterator end() noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
const_iterator cend() const noexcept;
|
||||
|
||||
reverse_iterator rbegin() noexcept;
|
||||
const_reverse_iterator rbegin() const noexcept;
|
||||
const_reverse_iterator crbegin() const noexcept;
|
||||
|
||||
reverse_iterator rend() noexcept;
|
||||
const_reverse_iterator rend() const noexcept;
|
||||
const_reverse_iterator crend() const noexcept;
|
||||
|
||||
T& back() noexcept;
|
||||
const T& back() const noexcept;
|
||||
|
||||
T& front() noexcept;
|
||||
const T& front() const noexcept;
|
||||
|
||||
std::size_t size() const noexcept;
|
||||
bool empty() const noexcept;
|
||||
void clear() noexcept;
|
||||
void swap(intrusive_list& other) noexcept;
|
||||
|
||||
void pop_back() noexcept;
|
||||
void pop_front() noexcept;
|
||||
|
||||
void push_back(T& v) noexcept;
|
||||
void push_front(T& v) noexcept;
|
||||
|
||||
iterator erase(const_iterator pos) noexcept;
|
||||
iterator insert(const_iterator pos, T& v) noexcept;
|
||||
|
||||
static iterator iterator_to(T& v) noexcept;
|
||||
static const_iterator iterator_to(const T& v) noexcept;
|
||||
private:
|
||||
using node_t = intrusive_list_hook<Tag>;
|
||||
using node_ptr = typename node_t::node_ptr;
|
||||
node_t root_{&root_, &root_};
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename T, typename Tag >
|
||||
intrusive_list<T,Tag>::~intrusive_list() noexcept {
|
||||
clear();
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::iterator
|
||||
intrusive_list<T,Tag>::begin() noexcept {
|
||||
return iterator(root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_iterator
|
||||
intrusive_list<T,Tag>::begin() const noexcept {
|
||||
return const_iterator(root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_iterator
|
||||
intrusive_list<T,Tag>::cbegin() const noexcept {
|
||||
return const_iterator(root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::iterator
|
||||
intrusive_list<T,Tag>::end() noexcept {
|
||||
return iterator(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_iterator
|
||||
intrusive_list<T,Tag>::end() const noexcept {
|
||||
return const_iterator(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_iterator
|
||||
intrusive_list<T,Tag>::cend() const noexcept {
|
||||
return const_iterator(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::reverse_iterator
|
||||
intrusive_list<T,Tag>::rbegin() noexcept {
|
||||
return reverse_iterator(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_reverse_iterator
|
||||
intrusive_list<T,Tag>::rbegin() const noexcept {
|
||||
return const_reverse_iterator(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_reverse_iterator
|
||||
intrusive_list<T,Tag>::crbegin() const noexcept {
|
||||
return const_reverse_iterator(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::reverse_iterator
|
||||
intrusive_list<T,Tag>::rend() noexcept {
|
||||
return reverse_iterator(root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_reverse_iterator
|
||||
intrusive_list<T,Tag>::rend() const noexcept {
|
||||
return const_reverse_iterator(root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_reverse_iterator
|
||||
intrusive_list<T,Tag>::crend() const noexcept {
|
||||
return const_reverse_iterator(root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
T& intrusive_list<T,Tag>::back() noexcept {
|
||||
E2D_ASSERT(!empty());
|
||||
return static_cast<T&>(*root_.prev_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
const T& intrusive_list<T,Tag>::back() const noexcept {
|
||||
E2D_ASSERT(!empty());
|
||||
return static_cast<const T&>(*root_.prev_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
T& intrusive_list<T,Tag>::front() noexcept {
|
||||
E2D_ASSERT(!empty());
|
||||
return static_cast<T&>(*root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
const T& intrusive_list<T,Tag>::front() const noexcept {
|
||||
E2D_ASSERT(!empty());
|
||||
return static_cast<const T&>(*root_.next_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
std::size_t intrusive_list<T,Tag>::size() const noexcept {
|
||||
std::size_t result{0u};
|
||||
const node_t* node = root_.next_;
|
||||
while ( node != &root_ ) {
|
||||
++result;
|
||||
node = node->next_;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
bool intrusive_list<T,Tag>::empty() const noexcept {
|
||||
return root_.unique();
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
void intrusive_list<T,Tag>::clear() noexcept {
|
||||
while ( !empty() ) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
void intrusive_list<T,Tag>::swap(intrusive_list<T,Tag>& other) noexcept {
|
||||
intrusive_list_hook<Tag>::swap_nodes(&root_, &other.root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
void intrusive_list<T,Tag>::pop_back() noexcept {
|
||||
E2D_ASSERT(!empty());
|
||||
node_ptr node = root_.prev_;
|
||||
node->unlink();
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
void intrusive_list<T,Tag>::pop_front() noexcept {
|
||||
E2D_ASSERT(!empty());
|
||||
node_ptr node = root_.next_;
|
||||
node->unlink();
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
void intrusive_list<T,Tag>::push_back(T& v) noexcept {
|
||||
node_t& node = static_cast<node_t&>(v);
|
||||
E2D_ASSERT(!node.is_linked());
|
||||
node.link_before(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
void intrusive_list<T,Tag>::push_front(T& v) noexcept {
|
||||
node_t& node = static_cast<node_t&>(v);
|
||||
E2D_ASSERT(!node.is_linked());
|
||||
node.link_after(&root_);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::erase(const_iterator pos) noexcept {
|
||||
node_ptr node = pos.node();
|
||||
E2D_ASSERT(node != &root_ && node->is_linked());
|
||||
++pos;
|
||||
node->unlink();
|
||||
return iterator(pos.node());
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::insert(const_iterator pos, T& v) noexcept {
|
||||
node_t& node = static_cast<node_t&>(v);
|
||||
E2D_ASSERT(!node.is_linked());
|
||||
node.link_before(pos.node());
|
||||
return iterator(&node);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::iterator intrusive_list<T,Tag>::iterator_to(T& v) noexcept {
|
||||
node_t& node = static_cast<node_t&>(v);
|
||||
E2D_ASSERT(node.is_linked());
|
||||
return iterator(&node);
|
||||
}
|
||||
|
||||
template < typename T, typename Tag >
|
||||
typename intrusive_list<T,Tag>::const_iterator intrusive_list<T,Tag>::iterator_to(const T& v) noexcept {
|
||||
const node_t& node = static_cast<const node_t&>(v);
|
||||
E2D_ASSERT(node.is_linked());
|
||||
return const_iterator(&node);
|
||||
}
|
||||
}
|
||||
346
untests/sources/untests_utils/ilist.cpp
Normal file
346
untests/sources/untests_utils/ilist.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
/*******************************************************************************
|
||||
* This file is part of the "Enduro2D"
|
||||
* For conditions of distribution and use, see copyright notice in LICENSE.md
|
||||
* Copyright (C) 2018 Matvey Cherevko
|
||||
******************************************************************************/
|
||||
|
||||
#include "_utils.hpp"
|
||||
using namespace e2d;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct ilist_tag1 {};
|
||||
struct ilist_tag2 {};
|
||||
|
||||
class obj_t
|
||||
: public intrusive_list_hook<ilist_tag1>
|
||||
, public intrusive_list_hook<ilist_tag2>
|
||||
{
|
||||
public:
|
||||
int i{42};
|
||||
obj_t() = default;
|
||||
obj_t(int ni) : i(ni) {}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("ilist") {
|
||||
{
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
REQUIRE(l.empty());
|
||||
REQUIRE_FALSE(l.size());
|
||||
}
|
||||
SECTION("push_back/push_front") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
auto o3 = std::make_unique<obj_t>(3);
|
||||
auto o4 = std::make_unique<obj_t>(4);
|
||||
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
|
||||
l.push_back(*o1);
|
||||
REQUIRE(l.back().i == 1);
|
||||
REQUIRE(l.front().i == 1);
|
||||
|
||||
l.push_back(*o2);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 1);
|
||||
|
||||
l.push_front(*o3);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 3);
|
||||
|
||||
l.push_front(*o4);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 4);
|
||||
}
|
||||
}
|
||||
SECTION("pop_back/pop_front") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
auto o3 = std::make_unique<obj_t>(3);
|
||||
auto o4 = std::make_unique<obj_t>(4);
|
||||
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
l.push_back(*o1);
|
||||
l.push_back(*o2);
|
||||
l.push_back(*o3);
|
||||
l.push_back(*o4);
|
||||
|
||||
l.pop_front();
|
||||
REQUIRE(l.front().i == 2);
|
||||
REQUIRE(l.back().i == 4);
|
||||
|
||||
l.pop_back();
|
||||
REQUIRE(l.front().i == 2);
|
||||
REQUIRE(l.back().i == 3);
|
||||
|
||||
l.pop_front();
|
||||
REQUIRE(l.front().i == 3);
|
||||
REQUIRE(l.back().i == 3);
|
||||
|
||||
l.pop_back();
|
||||
REQUIRE(l.empty());
|
||||
}
|
||||
}
|
||||
SECTION("empty/size/clear") {
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
auto o3 = std::make_unique<obj_t>(2);
|
||||
{
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
REQUIRE(l.empty());
|
||||
REQUIRE_FALSE(l.size());
|
||||
|
||||
l.push_back(*o1);
|
||||
REQUIRE_FALSE(l.empty());
|
||||
REQUIRE(l.size() == 1u);
|
||||
|
||||
l.push_front(*o2);
|
||||
REQUIRE_FALSE(l.empty());
|
||||
REQUIRE(l.size() == 2u);
|
||||
|
||||
l.push_back(*o3);
|
||||
REQUIRE_FALSE(l.empty());
|
||||
REQUIRE(l.size() == 3u);
|
||||
|
||||
l.pop_front();
|
||||
REQUIRE_FALSE(l.empty());
|
||||
REQUIRE(l.size() == 2u);
|
||||
|
||||
l.pop_back();
|
||||
REQUIRE_FALSE(l.empty());
|
||||
REQUIRE(l.size() == 1u);
|
||||
|
||||
l.pop_front();
|
||||
REQUIRE(l.empty());
|
||||
REQUIRE_FALSE(l.size());
|
||||
}
|
||||
{
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
l.push_back(*o1);
|
||||
l.push_back(*o2);
|
||||
l.push_back(*o3);
|
||||
REQUIRE_FALSE(l.empty());
|
||||
REQUIRE(l.size() == 3u);
|
||||
}
|
||||
}
|
||||
SECTION("swap") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
auto o3 = std::make_unique<obj_t>(3);
|
||||
auto o4 = std::make_unique<obj_t>(4);
|
||||
|
||||
intrusive_list<obj_t, ilist_tag1> l1;
|
||||
intrusive_list<obj_t, ilist_tag1> l2;
|
||||
|
||||
l1.push_back(*o1);
|
||||
l1.push_back(*o2);
|
||||
|
||||
l2.push_back(*o3);
|
||||
l2.push_back(*o4);
|
||||
|
||||
l1.swap(l2);
|
||||
|
||||
REQUIRE(l1.back().i == 4);
|
||||
REQUIRE(l1.front().i == 3);
|
||||
|
||||
REQUIRE(l2.back().i == 2);
|
||||
REQUIRE(l2.front().i == 1);
|
||||
}
|
||||
}
|
||||
SECTION("insert/erase") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
auto o3 = std::make_unique<obj_t>(3);
|
||||
auto o4 = std::make_unique<obj_t>(4);
|
||||
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
|
||||
REQUIRE(l.insert(l.end(), *o1)->i == 1); // 1
|
||||
{
|
||||
auto i = l.insert(++l.begin(), *o2); // 1 2
|
||||
REQUIRE(std::distance(l.begin(), i) == 1);
|
||||
}
|
||||
{
|
||||
auto i = l.insert(l.begin(), *o3); // 3 1 2
|
||||
REQUIRE(std::distance(l.begin(), i) == 0);
|
||||
}
|
||||
{
|
||||
auto iter = l.begin();
|
||||
REQUIRE(iter->i == 3);
|
||||
REQUIRE((++iter)->i == 1);
|
||||
REQUIRE((++iter)->i == 2);
|
||||
REQUIRE(++iter == l.end());
|
||||
}
|
||||
|
||||
// 3 1 2 4
|
||||
l.push_back(*o4);
|
||||
|
||||
// 3 1 2
|
||||
REQUIRE(l.erase(--l.end()) == l.end());
|
||||
REQUIRE(l.size() == 3);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 3);
|
||||
|
||||
// 3 2
|
||||
REQUIRE(l.erase(++l.begin())->i == 2);
|
||||
REQUIRE(l.size() == 2);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 3);
|
||||
|
||||
// 2
|
||||
REQUIRE(l.erase(l.begin())->i == 2);
|
||||
REQUIRE(l.size() == 1);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 2);
|
||||
}
|
||||
}
|
||||
SECTION("iterator_to") {
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
auto o3 = std::make_unique<obj_t>(3);
|
||||
auto o4 = std::make_unique<obj_t>(4);
|
||||
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
|
||||
l.push_back(*o1);
|
||||
l.push_back(*o2);
|
||||
l.push_back(*o3);
|
||||
l.push_back(*o4);
|
||||
|
||||
REQUIRE(l.iterator_to(*o1) == l.begin());
|
||||
REQUIRE(l.iterator_to(*o2) == ++l.begin());
|
||||
{
|
||||
const auto& co3 = o3;
|
||||
const auto& co4 = o4;
|
||||
REQUIRE(l.iterator_to(*co3) == --(--l.end()));
|
||||
REQUIRE(l.iterator_to(*co4) == --l.end());
|
||||
}
|
||||
}
|
||||
SECTION("auto_unlink") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
l.push_back(*o1);
|
||||
l.push_back(*o2);
|
||||
|
||||
o1.reset();
|
||||
REQUIRE(l.size() == 1u);
|
||||
REQUIRE(l.back().i == 2);
|
||||
REQUIRE(l.front().i == 2);
|
||||
}
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
l.push_back(*o1);
|
||||
l.push_back(*o2);
|
||||
|
||||
o2.reset();
|
||||
REQUIRE(l.size() == 1u);
|
||||
REQUIRE(l.back().i == 1);
|
||||
REQUIRE(l.front().i == 1);
|
||||
}
|
||||
}
|
||||
SECTION("multitag") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>();
|
||||
intrusive_list<obj_t, ilist_tag1> l1;
|
||||
intrusive_list<obj_t, ilist_tag2> l2;
|
||||
l1.push_back(*o1);
|
||||
l2.push_back(*o1);
|
||||
REQUIRE(l1.size() == 1);
|
||||
REQUIRE(l2.size() == 1);
|
||||
REQUIRE(l1.begin()->i == 42);
|
||||
REQUIRE(l2.begin()->i == 42);
|
||||
l2.pop_back();
|
||||
REQUIRE(l1.size() == 1);
|
||||
REQUIRE(l1.begin()->i == 42);
|
||||
REQUIRE(l2.empty());
|
||||
}
|
||||
}
|
||||
SECTION("iterator") {
|
||||
{
|
||||
auto o1 = std::make_unique<obj_t>(1);
|
||||
auto o2 = std::make_unique<obj_t>(2);
|
||||
intrusive_list<obj_t, ilist_tag1> l;
|
||||
l.push_back(*o1);
|
||||
l.push_back(*o2);
|
||||
std::transform(l.begin(), l.end(), l.begin(), [](obj_t& o){
|
||||
return o.i * 5;
|
||||
});
|
||||
{
|
||||
auto iter = l.begin();
|
||||
REQUIRE((*iter).i == 5);
|
||||
REQUIRE((++iter)->i == 10);
|
||||
}
|
||||
{
|
||||
auto iter = l.rbegin();
|
||||
REQUIRE((*iter).i == 10);
|
||||
REQUIRE((++iter)->i == 5);
|
||||
}
|
||||
REQUIRE(15 == std::accumulate(l.begin(), l.end(), 0, [](int acc, obj_t& o){
|
||||
return acc + o.i;
|
||||
}));
|
||||
REQUIRE(15 == std::accumulate(l.rbegin(), l.rend(), 0, [](int acc, obj_t& o){
|
||||
return acc + o.i;
|
||||
}));
|
||||
{
|
||||
const auto& cl = l;
|
||||
REQUIRE(15 == std::accumulate(cl.begin(), cl.end(), 0, [](int acc, const obj_t& o){
|
||||
return acc + o.i;
|
||||
}));
|
||||
REQUIRE(15 == std::accumulate(cl.rbegin(), cl.rend(), 0, [](int acc, const obj_t& o){
|
||||
return acc + o.i;
|
||||
}));
|
||||
}
|
||||
{
|
||||
REQUIRE(l.begin() == l.begin());
|
||||
REQUIRE(l.begin() == l.cbegin());
|
||||
REQUIRE(l.begin() != l.end());
|
||||
REQUIRE(l.begin() != l.cend());
|
||||
|
||||
REQUIRE(l.cbegin() == l.begin());
|
||||
REQUIRE(l.cbegin() == l.cbegin());
|
||||
REQUIRE(l.cbegin() != l.end());
|
||||
REQUIRE(l.cbegin() != l.cend());
|
||||
|
||||
REQUIRE(l.end() == l.end());
|
||||
REQUIRE(l.end() == l.cend());
|
||||
REQUIRE(l.end() != l.begin());
|
||||
REQUIRE(l.end() != l.cbegin());
|
||||
|
||||
REQUIRE(l.cend() == l.end());
|
||||
REQUIRE(l.cend() == l.cend());
|
||||
REQUIRE(l.cend() != l.begin());
|
||||
REQUIRE(l.cend() != l.cbegin());
|
||||
}
|
||||
{
|
||||
REQUIRE(l.rbegin() == l.rbegin());
|
||||
REQUIRE(l.rbegin() == l.crbegin());
|
||||
REQUIRE(l.rbegin() != l.rend());
|
||||
REQUIRE(l.rbegin() != l.crend());
|
||||
|
||||
REQUIRE(l.crbegin() == l.rbegin());
|
||||
REQUIRE(l.crbegin() == l.crbegin());
|
||||
REQUIRE(l.crbegin() != l.rend());
|
||||
REQUIRE(l.crbegin() != l.crend());
|
||||
|
||||
REQUIRE(l.rend() == l.rend());
|
||||
REQUIRE(l.rend() == l.crend());
|
||||
REQUIRE(l.rend() != l.rbegin());
|
||||
REQUIRE(l.rend() != l.crbegin());
|
||||
|
||||
REQUIRE(l.crend() == l.rend());
|
||||
REQUIRE(l.crend() == l.crend());
|
||||
REQUIRE(l.crend() != l.rbegin());
|
||||
REQUIRE(l.crend() != l.crbegin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace
|
||||
int obj2_t::dtor_counter = 0;
|
||||
}
|
||||
|
||||
TEST_CASE("refcount") {
|
||||
TEST_CASE("iptr") {
|
||||
{
|
||||
intrusive_ptr<obj_t> p;
|
||||
REQUIRE_FALSE(p);
|
||||
Reference in New Issue
Block a user