Program Listing for File intrusive_list.h

Return to documentation for file (utils/intrusive_list.h)

#pragma once

#include <spdlog/spdlog.h>

template <typename T>
struct Node {
public:
    Node(T* const obj_ptr) : prev(nullptr), next(nullptr), obj_ptr(obj_ptr) { }

    friend void swap(Node& a, Node& b) {
        using std::swap;
        swap(a.prev, b.prev);
        swap(a.next, b.next);
        swap(a.obj_ptr, b.obj_ptr);
    }

    const T& GetDataRef() const {
        return *obj_ptr;
    }
    T& GetDataRef() {
        return *obj_ptr;
    }

    const T* GetDataPtr() const {
        return obj_ptr;
    }
    T* GetDataPtr() {
        return obj_ptr;
    }

    template <typename R, Node<R> R::*Member>
    friend class IntrusiveList;

private:
    Node* prev;
    Node* next;

    T* obj_ptr;
};

template <typename T, Node<T> T::*Member>
class IntrusiveList {
public:
    IntrusiveList() : begin_node(nullptr), end_node(nullptr), num_elements(0) {
        begin_node.next = &end_node;
        end_node.prev = &begin_node;
    }

    IntrusiveList(const IntrusiveList&) = delete;
    IntrusiveList& operator=(const IntrusiveList&) = delete;

    friend void swap(IntrusiveList& a, IntrusiveList& b) {
        swap(a, b);
    }

    IntrusiveList(IntrusiveList&& o) : IntrusiveList() {
        swap(*this, o);
    }

    IntrusiveList& operator=(IntrusiveList&& o) {
        swap(*this, o);
        return *this;
    }

    void push_back(T& owner) {
        Node<T>& node = owner.*Member;
        // Skip inserting node into list when it's already inserted
        if (node.prev || node.next)
            return;

        node.next = &end_node;
        node.prev = end_node.prev;
        end_node.prev->next = &node;
        end_node.prev = &node;

        // Track number of elements in list
        num_elements++;
    }

    void delete_elem(T& owner) {
        Node<T>& node = owner.*Member;
        // Return if node is not in list altogether
        if (node.prev == nullptr && node.next == nullptr)
            return;

        node.next->prev = node.prev;
        node.prev->next = node.next;

        node.next = node.prev = nullptr;

        // Track number of elements in list
        num_elements--;
    }

    bool in_list(const T& owner) const {
        const Node<T>& node = owner.*Member;
        return node.next || node.prev;
    }

    size_t size() const {
        return num_elements;
    }

    template <typename It, typename NodeType>
    struct BaseIterator;

    using Iterator = BaseIterator<T, Node<T>>;
    using ConstIterator = BaseIterator<const T, const Node<T>>;

    Iterator begin() {
        return Iterator(begin_node.next);
    }
    Iterator end() {
        return Iterator(&end_node);
    }

    ConstIterator begin() const {
        return ConstIterator(begin_node.next);
    }
    ConstIterator end() const {
        return ConstIterator(&end_node);
    }

    Iterator erase(Iterator position) {
        delete_elem(*(position++));
        return position;
    }

private:
    Node<T> begin_node;
    Node<T> end_node;
    size_t num_elements;

    static void swap(IntrusiveList& a, IntrusiveList& b) {
        using std::swap;
        swap(a.begin_node, b.begin_node);
        swap(a.end_node, b.end_node);
        swap(a.num_elements, b.num_elements);

        const auto set_stack_nodes = [](IntrusiveList& l) {
            if (l.size() == 0)
                l.begin_node.next = &l.end_node;

            l.begin_node.next->prev = &l.begin_node;
            l.end_node.prev->next = &l.end_node;
        };

        set_stack_nodes(a);
        set_stack_nodes(b);
    }
};

template <typename T, Node<T> T::*Member>
template <typename It, typename NodeType>
struct IntrusiveList<T, Member>::BaseIterator {
    BaseIterator(NodeType* node_ptr) : node_ptr(node_ptr) { }

    It& operator*() const {
        return node_ptr->GetDataRef();
    }

    It* operator->() const {
        return node_ptr->GetDataPtr();
    }

    It& front() const {
        return node_ptr->GetDataRef();
    }

    BaseIterator& operator++() {
        node_ptr = node_ptr->next;
        return *this;
    }

    BaseIterator operator++(int) {
        Iterator tmp = *this;
        node_ptr = node_ptr->next;
        return tmp;
    }

    friend bool operator==(const BaseIterator& a, const BaseIterator& b) {
        return a.node_ptr == b.node_ptr;
    };
    friend bool operator!=(const BaseIterator& a, const BaseIterator& b) {
        return a.node_ptr != b.node_ptr;
    };

private:
    NodeType* node_ptr;
};