--- /dev/null
+#ifndef __COCO_ADT_DLINKED_LIST_H__
+#define __COCO_ADT_DLINKED_LIST_H__
+
+#include "coco/ADT/PtrLink.h"
+
+#include <cassert>
+
+namespace coco
+{
+
+// **CAUTION** Child SHOULD inherit DLinkedList<Child, Parent>::Node
+template<typename Child, typename Parent> struct DLinkedList
+{
+ class Head
+ {
+ public:
+ Head(Parent *parent, PtrLink<Child, Parent> *link) : _parent{parent}, _link{link}
+ {
+ _head = nullptr;
+ _tail = nullptr;
+ }
+
+ public:
+ Head(const Head &) = delete;
+ Head(Head &&) = delete;
+
+ public:
+ Child *head(void) const { return _head; }
+ Child *tail(void) const { return _tail; }
+
+ public:
+ bool empty(void) const
+ {
+ if (_head == nullptr)
+ {
+ assert(_head == _tail);
+ return true;
+ }
+
+ assert(_head != nullptr);
+ assert(_tail != nullptr);
+ return false;
+ }
+
+ public:
+ void enlist(Child *child)
+ {
+ assert((child->prev() == nullptr) || (child->prev()->parent() == _parent));
+ assert((child->next() == nullptr) || (child->next()->parent() == _parent));
+
+ if (empty())
+ {
+ _head = child;
+ _tail = child;
+ }
+ else
+ {
+ if (child->next() == _head)
+ {
+ // _child is a new head
+ assert(child->prev() == nullptr);
+ _head = child;
+ }
+
+ if (child->prev() == _tail)
+ {
+ // _child is a new tail
+ assert(child->next() == nullptr);
+ _tail = child;
+ }
+ }
+
+ // Update parent-child relation
+ _link->set(child, _parent);
+ }
+
+ public:
+ void append(Child *child)
+ {
+ if (empty())
+ {
+ enlist(child);
+ }
+ else
+ {
+ child->insertAfter(_tail);
+ }
+ }
+
+ private:
+ Parent * const _parent;
+ PtrLink<Child, Parent> * const _link;
+
+ private:
+ Child * _head;
+ Child * _tail;
+ };
+
+ class Node
+ {
+ public:
+ Node(const PtrLink<Child, Parent> *link) : _link{link}
+ {
+ _prev = nullptr;
+ _next = nullptr;
+ }
+
+ public:
+ virtual ~Node() = default;
+
+ public:
+ Parent *parent(void) const { return _link->find(reinterpret_cast<const Child *>(this)); }
+
+ public:
+ Child *prev(void) const { return _prev; }
+ Child *next(void) const { return _next; }
+
+ public:
+ virtual Head *head(void) const = 0;
+
+ public:
+ void insertAfter(Child *prev)
+ {
+ assert(prev != nullptr);
+ assert(prev->head() != nullptr);
+
+ assert(_prev == nullptr);
+ assert(_next == nullptr);
+
+ // REQUIRE Child should inherit Node class
+ auto curr = reinterpret_cast<Child *>(this);
+
+ // Update the link of the current node
+ _prev = prev;
+ _next = prev->next();
+
+ // Update the link of the sibling nodes
+ if (auto next = prev->next())
+ {
+ next->_prev = curr;
+ }
+ prev->_next = curr;
+
+ // Update parent-child relation
+ assert(parent() == nullptr);
+ prev->head()->enlist(curr);
+ assert(parent() == prev->parent());
+ };
+
+ private:
+ const PtrLink<Child, Parent> * const _link;
+
+ private:
+ Child *_prev;
+ Child *_next;
+ };
+};
+
+} // namespace coco
+
+#endif // __COCO_ADT_DLINKED_LIST_H__
--- /dev/null
+#include "coco/ADT/DLinkedList.h"
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+class Parent;
+class Child;
+
+using ChildList = coco::DLinkedList<Child, Parent>::Head;
+
+class Parent
+{
+public:
+ Parent(coco::PtrLink<Child, Parent> *link) : _list{this, link}
+ {
+ // DO NOTHING
+ }
+
+public:
+ ChildList *children(void) { return &_list; }
+
+private:
+ ChildList _list;
+};
+
+class Child : public coco::DLinkedList<Child, Parent>::Node
+{
+public:
+ Child(const coco::PtrLink<Child, Parent> *link) : coco::DLinkedList<Child, Parent>::Node{link}
+ {
+ // DO NOTHING
+ }
+
+public:
+ ChildList *head(void) const override { return parent()->children(); }
+};
+
+} // namespace
+
+TEST(ADT_DLINKED_LINK, insert_two_elements)
+{
+ auto link = new coco::PtrLink<::Child, ::Parent>{};
+ auto parent = new ::Parent{link};
+
+ ASSERT_EQ(parent->children()->head(), nullptr);
+ ASSERT_EQ(parent->children()->tail(), nullptr);
+
+ auto child_1 = new ::Child{link};
+
+ ASSERT_EQ(child_1->parent(), nullptr);
+ ASSERT_EQ(child_1->prev(), nullptr);
+ ASSERT_EQ(child_1->next(), nullptr);
+
+ parent->children()->append(child_1);
+
+ ASSERT_EQ(child_1->parent(), parent);
+ ASSERT_EQ(child_1->prev(), nullptr);
+ ASSERT_EQ(child_1->next(), nullptr);
+
+ ASSERT_EQ(parent->children()->head(), child_1);
+ ASSERT_EQ(parent->children()->tail(), child_1);
+
+ auto child_2 = new ::Child{link};
+
+ ASSERT_EQ(child_2->parent(), nullptr);
+ ASSERT_EQ(child_2->prev(), nullptr);
+ ASSERT_EQ(child_2->next(), nullptr);
+
+ child_2->insertAfter(child_1);
+
+ ASSERT_EQ(child_2->parent(), parent);
+ ASSERT_EQ(child_2->prev(), child_1);
+ ASSERT_EQ(child_2->next(), nullptr);
+
+ ASSERT_EQ(child_1->parent(), parent);
+ ASSERT_EQ(child_1->prev(), nullptr);
+ ASSERT_EQ(child_1->next(), child_2);
+
+ ASSERT_EQ(parent->children()->head(), child_1);
+ ASSERT_EQ(parent->children()->tail(), child_2);
+
+ delete child_2;
+ delete child_1;
+ delete parent;
+ delete link;
+}