-3

I'm searching for a way to convert a double linked list structure into a typesave c++ structure. I want to convert something similar to Birds Double linked list or like Linux's list_head. In particular I want to get rid of the typecast when iterating over all members. The main properties of these kind of structures are: The linked list is implemented as head/node members inside a containing structure. The head/tail uses the same structure as the nodes, therefore the list node entries doesnt need a explicit up pointer to the list head while still having the possible to remove a entry with only a pointer to the node (rem_node(p)).

Here is what I use so far:

#include <stdio.h>
#include <iostream>

template <typename b>
struct node {
    node<b> *next, *prev;
    operator b*() {
        return (b*) (this->next);
    }
};

template <typename a, typename b>
union llist {
    struct {
        node<b> head_node;
        void *head_padding;
    };
    struct {
        void *tail_padding;
        node<b> tail_node;
    };
    struct {
        node<b> *head;
        node<b> *null;
        node<b> *tail;
    };
    operator b*() {
        return (b*) (this->head);
    }
};

template <typename a, typename b>
void
add_head(llist<a,b> &l, node<b> &n)
{
    auto *z = l.head;

    n.next = z;
    n.prev = &l.head_node;
    z->prev = &n;
    l.head = &n;
}

template <typename b>
void
rem_node(node<b> &n)
{
    node<b> *z = n.prev;
    node<b> *x = n.next;

    z->next = x;
    x->prev = z;
    n.next = NULL;
    n.prev = NULL;
}

template <typename a, typename b>
void
init_list(llist<a,b> &l)
{
    l.head = &l.tail_node;
    l.null = NULL;
    l.tail = &l.head_node;
}


struct c1 {
    node<c1> n;
};

struct c0 {
    llist<c0, c1> l;
};

int main(int argc, char **argv) {

    c0 v0;
    c1 e0, e1, e2;
    c1 *i0;

    init_list(v0.l);
    add_head(v0.l, e0.n);
    add_head(v0.l, e1.n);
    add_head(v0.l, e2.n);

#define WALK_LIST(i,list) for(i=list; i->n.next; i=i->n)
    WALK_LIST(i0,v0.l) {
        std::cout << i0 << "\n";
    }
    
    rem_node(e1.n);
    
    std::cout << "\n";
    WALK_LIST(i0,v0.l) {
        std::cout << i0 << "\n";
    }
    
    return 0;
}

The operator b*() { return (b*) (this->next);} works only because the list head/node structure is placed at the beginning of the containing stucture c1/c2. What i really would like is using it anywhere in the code (pseudocode):

struct c1 {
    int pad;
    node<c1> n;
};

struct c0 {
    int pad;
    llist<c0, c1> l;
};
  • Is there some smarter method of implemented this style double linked lists?

  • Is there maybe even a method so that I maybe can skip the typecast?

    I'm not searching for std::list or similar, only how the list_head style double linked list is expessed in c++ in a typesave manner. Maybe there is no such possibility. While there is a pointer to member of a class C::*p that can be given as an templatet argument there is no inverse of that (something similar to container_of). Or is there?

Edit:

(Answer from below) After some thinking I came up with the following structure that I'm now satisfied with:

// g++ -g -std=c++11 81_list.cpp -o 81_list.exe
// 8< ---------------- 81_list.cpp ------------------
#include <stdio.h>
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <cstddef>

using namespace std;

/***************************************/
/* definition of double linked list
 * https://github.com/BIRD/bird/blob/470efcb98cb33de2d5636679eb0f72c88280d6b8/lib/lists.h#L12
 */

template <typename b>
struct node {
    node<b> *next, *prev;

    void
    rem_node()
    {
        node<b> *z = this->prev;
        node<b> *x = this->next;

        z->next = x;
        x->prev = z;
        this->next = NULL;
        this->prev = NULL;
    }

};

template <typename b, node<b> (b::* p)>
union llist {
    struct {
        node<b> head_node;
        void *head_padding;
    };
    struct {
        void *tail_padding;
        node<b> tail_node;
    };
    struct {
        node<b> *head;
        node<b> *null;
        node<b> *tail;
    };

    llist()
    {
        this->head = &this->tail_node;
        this->null = NULL;
        this->tail = &this->head_node;
    }

    void
    add_head(node<b> &n)
    {
        node<b> *z = this->head;

        n.next = z;
        n.prev = &(this->head_node);
        z->prev = &n;
        this->head = &n;
    }

    static b *container_of(node<b> &ptr) {
        return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));
    }

    struct lit {
        lit(node<b> *i) : i(i) {}

        lit & operator++()
        {
            i = i->next;
            return *this;
        }
        bool operator!=(const lit &that) const
        {
            return i != that.i;
        }
        b &operator*()
        {
            return *container_of(*i);
        }
        node<b> *i;
    };

    lit begin() const { return lit(this->head); }
    lit end() const { return lit(this->tail->next); }
};

/*********************************************/
/* example of usage: */

struct containnode {
    int pad; /* padding allowed */
    node<containnode> n;
    node<containnode> m;
};

struct containlist0 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::n> l;
};

struct containlist1 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::m> l;
};

int main(int argc, char **argv) {

    containlist0 list0;
    containlist1 list1;
    containnode e0, e1, e2;
    containnode *v[3] = { &e0, &e1, &e2 };

    /* add to list */
    for (auto *e : v) {
        list0.l.add_head(e->n);
        list1.l.add_head(e->m);
    }

    /* remove from list0 and print list0 and list1 */
    for (auto *e : v) {
        for (auto &i: list0.l) 
            cout << &i << "\n";
        
        cout << "\n";
        e->n.rem_node();

        for (auto &i: list1.l) 
            cout << &i << "\n";

    }

    return 0;
}

Above is based on Bird's double link list. Below is based on linux list_head and list_head functions:

// g++ -g -std=c++11 81_list.cpp -o 81_list.exe
// 8< ---------------- 81_list.cpp ------------------
#include <stdio.h>
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <cstddef>

using namespace std;

/***************************************/
/* definition of double linked list
 *
 */

template <typename b>
struct node {
    node<b> *next, *prev;

    void
    rem_node()
    {
        next->prev = prev;
        prev->next = next;
    }
};

template <typename b, node<b> (b::* p)>
struct llist {
    node<b> head;

    llist()
    {
        head.prev = &head;
        head.next = &head;
    }

    void
    add_head(node<b> &n)
    {
        node<b> *prev, *next;
        prev = &head;
        next = head.next;

        next->prev = &n;
        n.next = next;
        n.prev = prev;
        prev->next = &n;
    }

    static b *container_of(const node<b> &ptr) {
        return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));
    }

    struct lit {
    lit(const node<b> *i) : i(i) {}

    lit & operator++()
    {
        i = i->next;
        return *this;
    }
    bool operator!=(const lit &that) const
    {
        return i != that.i;
    }
    b &operator*()
    {
        return *container_of(*i);
    }
    const node<b> *i;
    };

    lit begin() const { return lit(this->head.next); }
    lit end() const { return lit(&this->head); }
};

/*********************************************/
/* example of usage: */

struct containnode {
    int pad; /* padding allowed */
    node<containnode> n;

    node<containnode> m;
};

struct containlist0 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::n> l;
};

struct containlist1 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::m> l;
};

int main(int argc, char **argv) {

    containlist0 list0;
    containlist1 list1;
    containnode e0, e1, e2;
    containnode *v[3] = { &e0, &e1, &e2 };

    /* add to list */
    for (auto *e : v) {
        list0.l.add_head(e->n);
        list1.l.add_head(e->m);
    }

    /* remove from list0 and print list0 and list1 */
    for (auto *e : v) {

        e->n.rem_node();

        cout << "\nlist0:\n";
        for (auto &i: list0.l) {
            cout << &i << "\n";
        }

        cout << "\nlist1:\n";
        for (auto &i: list1.l) {
            cout << &i << "\n";
        }

    }

    return 0;
}
Community
  • 1
  • 1
Konrad Eisele
  • 3,088
  • 20
  • 35
  • 6
    How about ``#include ``? See for example here: https://en.cppreference.com/w/cpp/container/list – BitTickler Jul 19 '18 at 17:27
  • @BitTickler: I want the head/node to be a member of a containing structure, the head of `````` can be embedded, however the node entries are external and you would need a seperate ```up```pointer to be able to remove the item from the list when only having a pointer to the node. I want to skip that. – Konrad Eisele Jul 19 '18 at 17:31
  • Related: https://stackoverflow.com/questions/3361145/intrusive-lists – πάντα ῥεῖ Jul 19 '18 at 17:35
  • @Konrad Also check https://www.boost.org/doc/libs/1_35_0/doc/html/intrusive/list.html – πάντα ῥεῖ Jul 19 '18 at 17:36
  • 1
    @πάνταῥεῖ: Note that you can replace the version number in a Boost link with "release", and boost.org will redirect to the *current* version of the documentation (so your link does not get stale over the years). – DevSolar Jul 19 '18 at 17:44
  • I am not sure I understand the problem yet. I see a use case where you have pointers to instances and they are owned by someone else and you add them to a list, for example as a container for a subset of the nodes (after some sort of select operation). Then, the same instance could be an element of multiple lists. But this is not the case, given you mention the (single) up pointer to the containing list. Or the use case, where the list owns the instances and a function which wants to remove an instance from a list without having a reference to the list... – BitTickler Jul 19 '18 at 17:45
  • @πάνταῥεῖ: I'm looked into boost/intrusive/list.h. The implementation is quite complicated... I wonder weather the case of a node being part of 2 lists is handled? Is there some doc that describes how intrusive/list.h works in essence? – Konrad Eisele Jul 19 '18 at 18:02
  • @Konrad With an _intrusive list_ a node cannot participate / being a part of more than a single list. – πάντα ῥεῖ Jul 19 '18 at 18:05
  • The only difference between intrusive and non intrusive lists is, that there is a form of aggregation taking place in case of non intrusive lists. Compare: ``typedef struct Foo_tag { Foo_tag *prev; Foo_tag *next; int data } Foo_t`` with ``template struct Node { Node *prev; Node*next; T data }``. – BitTickler Jul 19 '18 at 18:08
  • @BitTickler: I added the ```rem_node()``` method too. Maybe it gets clearer then. – Konrad Eisele Jul 19 '18 at 18:11
  • 1
    I start to think (without fully understanding), that the way the list you refer to (linux kernel, right?) is implemented is either due to some unmentioned criteria (e.g. cache locality) or due to the fact, that C does not have templates and as such, writing a "generic" list implementation requires one hack or another. – BitTickler Jul 19 '18 at 18:21
  • See for example microsofts ``SList```reference - which is a "generic" singly linked list suitable for C... https://learn.microsoft.com/en-us/windows/desktop/sync/using-singly-linked-lists – BitTickler Jul 19 '18 at 18:27
  • @BitTickler: ```list_head``` is from the linux kernel, BSD has a similar structure. I'm using one variant that is implemented in the Bird routing protocol suite. I'm looking into weather I can rewrite it ```more c++ style``` and wonder weather there is some added value adding templating (weather there is a possibility to get rid of the (void*) casts). The handy thing with this kind of structure is (and that is why I want to use it) is that you can remove an element without a reference to the list. (And you can have one element be member of several lists) – Konrad Eisele Jul 19 '18 at 18:28
  • 2
    My personal approach would probably be as follows: If T is the type of data you want to store, use ``std::list`` or if ownership of the instances is a problem (memory leaks), consider using ``std::list >``. Of course, this forces your hand regarding where the instances of T are residing (heap). Then I would see if I hit any road blocks with my port. – BitTickler Jul 19 '18 at 18:33
  • @BitTickler: Ok, thanks. Maybe the simple answer is that this structure is too c-ish for templating to add some value. I cannot somehow define a template instance that has the containing structure offset calculated automatically... Or maybe it is possible with some template metaprogramming trick? – Konrad Eisele Jul 19 '18 at 18:37
  • "Pointer to list": you can use an iterator instead. Using `std::list`, the iterator does not get invalidated if elements are inserted or deleted before or after (in contrast to std::vector; a quality applying to std::map::iterator as well...). – Aconcagua Jul 20 '18 at 06:20
  • @Aconcagua : As stated above : I dont want a pointer to the list container stored in the element and I dont want to interate the whole list to remove a node. The ```list_head``` aproach scales much better because The list head/tail is marked as a node itself and the data is embedded in the node/head. – Konrad Eisele Jul 20 '18 at 21:12
  • 1
    You might provide the list as singleton, then you don't need a pointer/refernce to either. On the other hand, about how many elements are we talking at all? A few hundred? Then it would cost you a few kilo-bytes of memory, in times of giga-byte memory being available, it's just not worth all the effort of maintaining an own implementation (keyword: "premature optimisation"). – Aconcagua Jul 20 '18 at 22:56
  • Are you aware that you need sentinels for head and tail? Imagine you want to erase the first or last element of the list, how would you otherwise adjust the list itself? – Aconcagua Jul 20 '18 at 22:58
  • You do not store a `b` element in any node; instead, you cast a pointer to `node` to pointer to `b`, which will yield undefined behaviour, as both types are totally unrelated. – Aconcagua Jul 20 '18 at 23:27
  • @Aconcagua : Sorry for the confusion. Not shure what you are refencing to but I added the version that I'm satisfied with now below (answer). It is the Bird double linked list in c++ fashion. It is 'kindof' typesave . – Konrad Eisele Jul 21 '18 at 20:15
  • @πάνταῥεῖ: The below answer is intrusive however not as super complex as boost. : -) – Konrad Eisele Jul 21 '18 at 20:15
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/176493/discussion-between-konrad-eisele-and-aconcagua). – Konrad Eisele Jul 21 '18 at 20:27

2 Answers2

0

Simplifying your own answer, especially removing some illegal code (type punning via union):

struct node
{
    node* prev;
    node* next;
};

struct llist
{
    node head_node;
    node tail_node;

    llist()
    {
        head_node.prev = nullptr;
        head_node.next = &tail_node;
        tail_node.prev = &head_node;
        tail_node.next = nullptr;
    }

    void add_head(node& n)
    {
        head_node.next->prev = &n;
        n.next = head_node.next;
        head_node.next = &n;
        n.prev = &this->head_node;
    }
};

So far, you do not need any template at all...

template <typename b> // function now needs to be a template itself...
static b* container_of(node& ptr)
{
    return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));
}

At very first, p is not defined:

template <typename b>
static b* container_of(node& ptr, node b::*p)
{
    return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));
}

containnode c;
containnode* cn = llist::container_of<containnode>(c.n, &containnode::n);
containnode* cm = llist::container_of<containnode>(c.m, &containnode::m);

However, this is extremely dangerous! Imagine what happens if you got the node from a list and now use the bad member function pointer:

containnode* BAD = llist::container_of<containnode>(c.n, &containnode::m);
//                                                    ^                ^

Rather consider storing the data directly in the nodes themselves (reintroducing the template parameter!):

template <typename T>
struct node
{
    node* prev;
    node* next;
    T data;
};

template <typename T>
struct llist
{
    node<T> head_node;
    node<T> tail_node;

    void add_head(node<T>& n);

    // container_of dropped, rest unchanged
};

This limits the types that can be used (as is, T needs to be default constructible for the sentinels, you might provide an appropriate constructor go get around this limitation, though).

If you want to insert one and the same element into multiple lists, do so via pointer:

Element e;
node<Element*> n0(&e); // appropriate constructor provided
node<Element*> n1(&e); // appropriate constructor provided

Now you can add n0 and n1 into different lists...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • I added a version that uses Linux ```list_head``` style structure. This doesn't use a union. I dont think it matters but just to show. – Konrad Eisele Jul 22 '18 at 17:57
-1

After some thinking I found a way to define the container_of() function in template fashion using a pointer to member template argument.

  • Node can be part of multiple lists
  • 2 pointers per node, 3 pointers per list head
  • Node can be removed without pointer to list

The implementation is not typesave because of the container_of(). In particular there are two types of errors: You can add a Node to a wrong typed list (can be fixed with adding more template signature) and declare a node with a wrong basetype (cannot be fixed because you cannot determine the containing type and check that at compile time (or can you?)):

// https://stackoverflow.com/questions/51428593/howto-write-a-c-style-double-linked-list-implementation-similar-to-kernels-lis
// g++ -g -std=c++11 81_list.cpp -o 81_list.exe
// 8< ---------------- 81_list.cpp ------------------
#include <stdio.h>
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <cstddef>

using namespace std;

/***************************************/
/* definition of double linked list
 * https://github.com/BIRD/bird/blob/470efcb98cb33de2d5636679eb0f72c88280d6b8/lib/lists.h#L12
 */

template <typename b>
struct node {
    node<b> *next, *prev;

    void
    rem_node()
    {
        node<b> *z = this->prev;
        node<b> *x = this->next;

        z->next = x;
        x->prev = z;
        this->next = NULL;
        this->prev = NULL;
    }

};

template <typename b, node<b> (b::* p)>
union llist {
    struct {
        node<b> head_node;
        void *head_padding;
    };
    struct {
        void *tail_padding;
        node<b> tail_node;
    };
    struct {
        node<b> *head;
        node<b> *null;
        node<b> *tail;
    };

    llist()
    {
        this->head = &this->tail_node;
        this->null = NULL;
        this->tail = &this->head_node;
    }

    void
    add_head(node<b> &n)
    {
        node<b> *z = this->head;

        n.next = z;
        n.prev = &(this->head_node);
        z->prev = &n;
        this->head = &n;
    }

    static b *container_of(node<b> &ptr) {
        return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));
    }

    struct lit {
        lit(node<b> *i) : i(i) {}

        lit & operator++()
        {
            i = i->next;
            return *this;
        }
        bool operator!=(const lit &that) const
        {
            return i != that.i;
        }
        b &operator*()
        {
            return *container_of(*i);
        }
        node<b> *i;
    };

    lit begin() const { return lit(this->head); }
    lit end() const { return lit(this->tail->next); }
};

/*********************************************/
/* example of usage: */

struct containnode {
    int pad; /* padding allowed */
    node<containnode> n;
    node<containnode> m;
};

struct containlist0 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::n> l;
};

struct containlist1 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::m> l;
};

int main(int argc, char **argv) {

    containlist0 list0;
    containlist1 list1;
    containnode e0, e1, e2;
    containnode *v[3] = { &e0, &e1, &e2 };

    /* add to list */
    for (auto *e : v) {
        list0.l.add_head(e->n);
        list1.l.add_head(e->m);
    }

    /* remove from list0 and print list0 and list1 */
    for (auto *e : v) {
        for (auto &i: list0.l) 
            cout << &i << "\n";

        cout << "\n";
        e->n.rem_node();

        for (auto &i: list1.l) 
            cout << &i << "\n";

    }

    return 0;
}

The version based on list_head without a union:

// g++ -g -std=c++11 81_list.cpp -o 81_list.exe
// 8< ---------------- 81_list.cpp ------------------
#include <stdio.h>
#include <iostream>
#include <stdio.h>
#include <iostream>
#include <cstddef>

using namespace std;

/***************************************/
/* definition of double linked list
 *
 */

template <typename b>
struct node {
    node<b> *next, *prev;

    void
    rem_node()
    {
        next->prev = prev;
        prev->next = next;
    }
};

template <typename b, node<b> (b::* p)>
struct llist {
    node<b> head;

    llist()
    {
        head.prev = &head;
        head.next = &head;
    }

    void
    add_head(node<b> &n)
    {
        node<b> *prev, *next;
        prev = &head;
        next = head.next;

        next->prev = &n;
        n.next = next;
        n.prev = prev;
        prev->next = &n;
    }

    static b *container_of(const node<b> &ptr) {
        return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));
    }

    struct lit {
    lit(const node<b> *i) : i(i) {}

    lit & operator++()
    {
        i = i->next;
        return *this;
    }
    bool operator!=(const lit &that) const
    {
        return i != that.i;
    }
    b &operator*()
    {
        return *container_of(*i);
    }
    const node<b> *i;
    };

    lit begin() const { return lit(this->head.next); }
    lit end() const { return lit(&this->head); }
};

/*********************************************/
/* example of usage: */

struct containnode {
    int pad; /* padding allowed */
    node<containnode> n;

    node<containnode> m;
};

struct containlist0 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::n> l;
};

struct containlist1 {
    int pad; /* padding allowed */
    llist<containnode, &containnode::m> l;
};

int main(int argc, char **argv) {

    containlist0 list0;
    containlist1 list1;
    containnode e0, e1, e2;
    containnode *v[3] = { &e0, &e1, &e2 };

    /* add to list */
    for (auto *e : v) {
        list0.l.add_head(e->n);
        list1.l.add_head(e->m);
    }

    /* remove from list0 and print list0 and list1 */
    for (auto *e : v) {

        e->n.rem_node();

        cout << "\nlist0:\n";
        for (auto &i: list0.l) {
            cout << &i << "\n";
        }

        cout << "\nlist1:\n";
        for (auto &i: list1.l) {
            cout << &i << "\n";
        }

    }

    return 0;
}
Konrad Eisele
  • 3,088
  • 20
  • 35
  • What's the point of template parameter `a`? What's the whole point of it all? An object can **never** be in more than one place, bit it lists or whatever. Only pointers/references to an object. – Walter Jul 21 '18 at 20:33
  • @Walter: Parameter a was a remainder of the old version. Have removed it. – Konrad Eisele Jul 21 '18 at 20:36
  • @Walter: I added more example code, still unshure? – Konrad Eisele Jul 21 '18 at 20:46
  • @Walter: This is a intrusive list. The object **can** be part of two lists. – Konrad Eisele Jul 21 '18 at 20:59
  • **Nothing can be in two places at once**. Not even your object. This is a fundamental law of nature. You can have something **represented** at different places at once, and I suspect that's what you mean, but then you should be more precise in your language. – Walter Jul 21 '18 at 21:21
  • @Walter: Please tell me where I stated that **something can be in two places at once**. I'm not aware of that :-). – Konrad Eisele Jul 21 '18 at 21:23
  • Btw, your code does not use the type of the object anywhere and the template parameter `b` is not used either, other than in `node`, which will be the same for *any* `b`. Finally, this *`return (b*) (((char*)&ptr) - (long)&(((b*)0)->*p));`* looks like horrible (for any C++ person) C hack. No self-respecting C++ programmer would ever consider writing such code. What does it do??? Are you sure all these casts are legal in C++? – Walter Jul 21 '18 at 21:27
  • You stated "*The object can be part of two lists.*" The *object*, not a pointer to the object. – Walter Jul 21 '18 at 21:29
  • @Walter: The container_of (and with it the cast) if the answer to question that I ask here. If you can give a answer that implements the c-ish double linked list in a better fashion (that is what I asked) then please go ahead. I hope you can produce code that smells nice, your comments dont. – Konrad Eisele Jul 21 '18 at 21:31
  • I would love to, but your question remains unclear as does your code. Perhaps you can explain what you want to achieve w/o reference to any code. – Walter Jul 21 '18 at 21:32
  • @Walter: Linux's and FreeBSD's ```list_head``` structure is a very powerful construct. If you work with this structure once and get a feel for it then you will understand why I want to convert it to be used with c++. Please take the effort. The version that I use is from Bird routing suite. I have given a link above. – Konrad Eisele Jul 21 '18 at 21:35
  • @Walter: ???? No, I have the same surename but the person is not me. :-) – Konrad Eisele Jul 21 '18 at 21:38
  • @Walter: Not that one... – Konrad Eisele Jul 21 '18 at 21:39
  • Okay, so I looked into the explanations at Boost.Intrusive. I see no or little advantage (one indirection less) in such constructs, only trouble. One simple option that avoids copying/moving objects (into containers) is to use containers of `shared_ptr`. That way, the memory management is correctly dealt with (a big problem for intrusive containers) and different types of objects (as long as they are derived from `T`) can be represented in the container. The only issue is that the objects are still kept on the heap, but you can create your own stack-based equivalent to `shared_ptr<>` ... – Walter Jul 21 '18 at 21:58
  • Btw, *your object* cannot be part of two lists. For that it would need two pairs of `next`, `prev` pointers, one for each list. – Walter Jul 21 '18 at 22:02
  • @Walter: Boost doesnt support being part of 2 lists. My implementation does. Just read the example I wrote: ```node n;`node m;```. What I think is the biggest advantage of ```list_head``` is that you can remove a node from the list without knowledge of the list. The list head/tail is itself only a node using the same structure. It is more elegant because you dont introduce a cyclic reference compared to the backreference to the list that you need to add for each node otherwise. It is minimal in mem usage. – Konrad Eisele Jul 22 '18 at 06:14
  • @Walter: Because of the ```container_of``` casting the construct is not 100% typesave. But you can make it more robust by adding more signature to the template of node<> to make it unique to each list. This would prevent a node being added to the wrong type list. The last problem however is that you specify the wrong type in the node instantiation: ```struct containing { ... node n; ... }```. I could not comeup with any c++ meta feature/trick that would let me check the type of the class that is *containing* the node as a *member*. If I could check that it would be typesave. – Konrad Eisele Jul 22 '18 at 06:39
  • @Walter: Maybe you know such a trick (test equality pf the instantiation parameter and the containing class type). Or maybe you can comeup with a smarter construct that would make it possible to test this relation? – Konrad Eisele Jul 22 '18 at 06:44
  • Am I right that you want to delete the node from a list without having a pointer to the list? Your code will fail badly if you try to remove head or tail from the list, as you do not adjust the list's head and tail pointers! To get this right, consider using sentinel nodes, such that: `begin() { return head_sentinel_node->next; } end() { return tail_sentinel_node; }` - if you now remove the (true) head/tail of the list, the `rem_node` will adjust the sentinels (instead of running into undefined behaviour because of trying to set head->prev(nullptr!)->next). – Aconcagua Jul 22 '18 at 11:09
  • @Aconcagua : Please read the description of the Bird linked list at the beginning of the question. The trick is that the list structure itself is what you call the head/tail sentinel. Also note the „union“. – Konrad Eisele Jul 22 '18 at 11:23
  • You are aware that type punning via unions is not allowed in C++ (-> undefined behaviour!)? – Aconcagua Jul 22 '18 at 11:43