Skip to content

Commit

Permalink
✨ Add intrusive_list
Browse files Browse the repository at this point in the history
A simple doubly-linked intrusive list. Notably it only provides forward
iteration, to avoid complexity in handling reverse iterators. However it does
allow pushing and popping at both ends.
  • Loading branch information
bdeane-intel committed Sep 7, 2023
1 parent aac48be commit 9312346
Show file tree
Hide file tree
Showing 4 changed files with 504 additions and 0 deletions.
31 changes: 31 additions & 0 deletions docs/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ The following headers are available:
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/ct_string.hpp[`ct_string.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/function_traits.hpp[`function_traits.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/functional.hpp[`functional.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/intrusive_list.hpp[`instrusive_list.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/priority.hpp[`priority.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/type_traits.hpp[`type_traits.hpp`]
* https://github.com/intel/cpp-std-extensions/blob/main/include/stdx/utility.hpp[`utility.hpp`]
Expand Down Expand Up @@ -239,6 +240,36 @@ v.emplace(0, stdx::with_result_of{make_S}); // this constructs S in-place thanks
`with_result_of` can help to achieve in-place construction, effectively by deferring
evaluation of function arguments.

== `intrusive_list.hpp`

`intrusive_list` is a doubly-linked list designed for use at compile-time or
with static objects. It supports pushing and popping at the front or back, and
removal from the middle.

[source,cpp]
----
// A node in an intrusive_list must have prev and next pointers
struct node {
node *prev{};
node *next{};
};
stdx::intrusive_list<node> l;
node n1{};
l.push_front(&n1);
node n2{};
l.push_back(&n2);
node n3{};
l.push_back(&n3);
l.remove(&n2); // removal from the middle is constant-time
l.pop_front();
l.pop_back();
----

== `priority.hpp`

`priority_t<N>` is a class that can be used for easily selecting complex
Expand Down
183 changes: 183 additions & 0 deletions include/stdx/intrusive_list.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#pragma once

#include <stdx/concepts.hpp>

#include <cstddef>
#include <iterator>
#include <type_traits>

namespace stdx {
inline namespace v1 {

#if __cpp_concepts >= 201907L
namespace detail {
template <typename T>
concept base_single_linkable = requires(T node) {
{ node->next } -> same_as<T &>;
};
} // namespace detail

template <typename T>
concept double_linkable = requires(T *node) {
requires detail::base_single_linkable<
std::remove_cvref_t<decltype(node->next)>>;
requires detail::base_single_linkable<
std::remove_cvref_t<decltype(node->prev)>>;
};

#define STDX_DOUBLE_LINKABLE double_linkable
#else
#define STDX_DOUBLE_LINKABLE typename
#endif

template <STDX_DOUBLE_LINKABLE NodeType> class intrusive_list {
template <typename N> struct iterator_t {
using difference_type = std::ptrdiff_t;
using value_type = N;
using pointer = value_type *;
using reference = value_type &;
using iterator_category = std::forward_iterator_tag;

constexpr iterator_t() = default;
constexpr explicit iterator_t(pointer n) : node{n} {}

constexpr auto operator*() -> reference { return *node; }
constexpr auto operator*() const -> reference { return *node; }
constexpr auto operator->() -> pointer { return node; }
constexpr auto operator->() const -> pointer { return node; }

constexpr auto operator++() -> iterator_t & {
node = node->next;
return *this;
}
constexpr auto operator++(int) -> iterator_t {
auto tmp = *this;
++(*this);
return tmp;
}

private:
pointer node{};

#if __cpp_impl_three_way_comparison < 201907L
friend constexpr auto operator==(iterator_t lhs, iterator_t rhs)
-> bool {
return lhs.node == rhs.node;
}
friend constexpr auto operator!=(iterator_t lhs, iterator_t rhs)
-> bool {
return not(lhs == rhs);
}
#else
friend constexpr auto operator==(iterator_t, iterator_t)
-> bool = default;
#endif
};

public:
using value_type = NodeType;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type &;
using const_reference = value_type const &;
using pointer = value_type *;
using const_pointer = value_type const *;
using iterator = iterator_t<value_type>;
using const_iterator = iterator_t<value_type const>;

private:
pointer head{};
pointer tail{};

public:
constexpr auto begin() -> iterator { return iterator{head}; }
constexpr auto begin() const -> const_iterator {
return const_iterator{head};
}
constexpr auto cbegin() const -> const_iterator {
return const_iterator{head};
}
constexpr auto end() -> iterator { return {}; }
constexpr auto end() const -> const_iterator { return {}; }
constexpr auto cend() -> const_iterator { return {}; }

constexpr auto push_front(pointer n) -> void {
if (head != nullptr) {
head->prev = n;
}
n->next = head;
head = n;
n->prev = nullptr;
if (tail == nullptr) {
tail = n;
}
}

constexpr auto push_back(pointer n) -> void {
if (tail != nullptr) {
tail->next = n;
}
n->prev = tail;
tail = n;
n->next = nullptr;
if (head == nullptr) {
head = n;
}
}

constexpr auto pop_front() -> pointer {
pointer poppedNode = head;
head = head->next;

if (head == nullptr) {
tail = nullptr;
} else {
head->prev = nullptr;
}

return poppedNode;
}

constexpr auto pop_back() -> pointer {
pointer poppedNode = tail;
tail = tail->prev;

if (tail == nullptr) {
head = nullptr;
} else {
tail->next = nullptr;
}

return poppedNode;
}

[[nodiscard]] constexpr auto empty() const -> bool {
return head == nullptr;
}

constexpr auto clear() -> void {
head = nullptr;
tail = nullptr;
}

constexpr auto remove(pointer n) -> void {
pointer nextNode = n->next;
pointer prevNode = n->prev;

if (prevNode == nullptr) {
head = nextNode;
} else {
prevNode->next = nextNode;
}

if (nextNode == nullptr) {
tail = prevNode;
} else {
nextNode->prev = prevNode;
}
}
};

#undef STDX_DOUBLE_LINKABLE
} // namespace v1
} // namespace stdx
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_unit_test(
conditional.cpp
ct_string.cpp
function_traits.cpp
intrusive_list.cpp
overload.cpp
priority.cpp
remove_cvref.cpp
Expand Down
Loading

0 comments on commit 9312346

Please sign in to comment.