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 tocontainer_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;
}
``? See for example here: https://en.cppreference.com/w/cpp/container/list
– BitTickler Jul 19 '18 at 17:27``` 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