我正在为链表和树编写一个c库,我正在寻找一种解决方案来推广这些库处理的数据类型,而不需要为我需要处理的每种类型创建一个列表/树库。
例如,我的列表库具有以下功能:
/* list.c */
#include <list.h>
list list_cons(list_element d, list l){
list m = malloc(sizeof(list_node));
m->value = element_copy(d);
m->next = l;
return m;
}
然后我的list.c:
list_element
现在假设在我的主程序中我使用了int列表和double列表,我应该创建2个文件,比如list_float.c / .h和list_int.c / .h
还有一些/* main.c */
list_cons(void *data, list l);
可以是struct,需要像copy / isLess / isEqual这样的函数来比较它们自己
我想在我的代码中写这样的东西:
data
list_cons
是指向我想要的任何类型的指针element_copy
,{{1}}适用于我传递的任何类型的数据(显然我需要复制数据而不是指针data,void *是我必须概括参数的函数类型的唯一想法)
答案 0 :(得分:4)
作为一般性建议,您不应该假设list_cons
是构建链表的唯一方法。有时malloc
不可用或者用户想要在静态数组中预先分配所有内容或想要使用自定义分配器或...
作为具体示例,您可以查看https://github.com/torvalds/linux/blob/master/include/linux/list.h。
如果您需要代码的其他许可证,请在xBSD Unix源代码中搜索数据结构实现。
一般的想法是,您只需要一个链表结构来包含next / prev和类似字段,而不是限制用户使用您的类型名称。所有迭代和基本操作都被定义为预处理器宏,您可以在其上实现复杂的算法。
答案 1 :(得分:2)
请注意,在C11中(阅读其标准n1570和一些C reference site),可以区别对待不同的类型(参见其§6.5.2)。
特别是,实现可以不同地处理int
,double
和指针值(它们的大小和alignment通常不同,请参阅sizeof
&amp; {{3} }),并且一些alignof
约定决定它们在不同的寄存器中传递(例如在函数调用中)。
所以你不能写一些处理(移植)所有int
,double
等的东西......以同样的方式(除非你有ABI个函数;用<stdarg.h>
)
您可能决定实现一些“通用”列表,其内容是一些任意指针。但是你需要一些关于它们的约定(谁正在分配那个指针,谁正在释放它,也许是允许的操作等等)。查看variadic的灵感。
在给定内容的类型名称的情况下,您还可以使用预处理器技术生成Glib doubly-linked lists(列表中的)和实现它的函数。查看abstract data type的灵感。
您还可以使用一些SGLIB技术:您将以某种方式描述元素的类型,并将该描述提供给元编程,这是一些C代码生成器。查看metaprogramming的灵感。生成的C代码将实现列表的抽象数据类型。
不要忘记记忆SWIG,并清楚地描述和记录你的惯例。阅读management issues。
还要考虑一些复杂的情况,比如一些字符串列表(可能是动态分配的{la RAII或使用strdup获得的)。您会发现事情并不简单,并且您需要显式约定(例如,某些字符串可能在两个子列表之间共享?该字符串何时为{{ 1}} - d,...)。
答案 2 :(得分:1)
这可能是使用联盟的好地方。
您可以将基本数据类型定义为要支持的最常见类型的并集。然后,您将定义一个有问题类型的枚举,使用该值来标记联合包含的内容。
enum element_type {
TYPE_INT,
TYPE_DOUBLE
};
typedef union {
int e_int;
double e_double;
} list_element;
struct list_node {
enum element_type type;
list_element value;
struct list_node* next;
};
然后你像这样添加到列表中:
list list_cons(list_element d, enum element_type type, list l){
list m = malloc(sizeof(list_node));
m->type = type;
m->value = element_copy(d);
m->next = l;
return m;
}