如何编写一个类似于kernellist_head的c++风格的双链表实现

Howto write a c++ style double linked list implementation similar to kernels list_head

本文关键字:c++ head 风格 实现 链表 kernellist 何编写 类似于 一个      更新时间:2023-10-16

我正在寻找一种将双链表结构转换为typesave c++结构的方法。我想转换一些类似Birds Double链表或类似Linux的list_head的东西。特别是,当迭代所有成员时,我想去掉类型转换。这类结构的主要特性是:链表被实现为包含结构中的头/节点成员。头/尾使用与节点相同的结构,因此列表节点条目不需要指向列表头的显式up指针,同时仍然可以删除带有的条目,其中只有指向节点的指针(rem_node(p)(。

以下是我目前使用的:

#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;
}

operator b*() { return (b*) (this->next);}之所以有效,是因为列表头/节点结构位于包含结构c1/c2的开始处。我真正想要的是在代码中的任何地方使用它(伪代码(:

struct c1 {
int pad;
node<c1> n;
};
struct c0 {
int pad;
llist<c0, c1> l;
};
  • 有没有更聪明的方法来实现这种风格的双链表?

  • 有没有一种方法可以让我跳过打字?

    我不搜索std::list或类似的内容,只搜索如何在c++中以typesave方式导出list_head样式的双链表。也许没有这种可能性。虽然有一个指向类C::*p成员的指针可以作为模板集参数给定,但没有相反的指针(类似于container_of(。还是有?

编辑:

(答案如下(经过思考,我想出了以下办法我现在满意的结构:

// 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;
}

以上是基于Bird的双链接列表。以下是基于linux的list_head和list_head函数:

// 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;
}

简化您自己的答案,特别是删除一些非法代码(通过联合键入punning(:

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;
}
};

到目前为止,您根本不需要任何模板。。。

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));
}

起初,p没有定义:

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);

然而,这是非常危险的!想象一下,如果你从列表中获得节点,现在使用坏成员函数指针:,会发生什么

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

相反,可以考虑将数据直接存储在节点中(重新引入模板参数!(:

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
};

这限制了可以使用的类型(事实上,T需要是哨兵的默认可构造类型,您可以提供一个适当的构造函数来绕过这个限制(。

如果你想在多个列表中插入一个相同的元素,可以通过指针:来完成

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

现在您可以将n0和n1添加到不同的列表中。。。

经过思考,我找到了一种定义container_of()函数,使用指向成员模板参数的指针。

  • 节点可以是多个列表的一部分
  • 每个节点2个指针,每个列表头3个指针
  • 可以在没有指向列表的指针的情况下删除节点

由于container_of()的原因,实现不是typesave。特别是有两种类型的错误:您可以将节点添加到错误类型的列表中(可以通过添加更多的模板签名来修复(,并声明具有错误基类型的节点(无法修复,因为您无法确定包含类型并在编译时检查它(或者可以吗?((:

// 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;
}

基于list_head而没有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;
}