如何编写通用c?
我开始写一组平衡树(替罪羊,splay,aa等),并找到许多共同点。示例是下面的destroy函数。
可以使用void指针定义这样的函数和类似函数而不会导致“解除引用void *指针错误”吗?
示例销毁功能
void splay_node_linked_destroy(SplayNode **this) {
72 if(*this == NULL) {
73 return;
74 }
75 SplayNode *root = (*this)->root, *previous, *next;
76 while(root) {
77 if(previous == root->parent) {
78 // todo use funcs for comparisons for generic
79 if(root->left) {
80 next = root->left;
81 } else if(root->right) {
82 next = root->right;
83 } else {
84 next = root->parent;
85 }
86 } else if(previous == root->left) {
87 if(root->right) {
88 next = root->right;
89 } else {
90 next = root->parent;
91 }
92 } else {
93 next = root->parent;
94 }
95 previous = root;
96 if(next == root->parent) {
97 splay_node_destroy(&root);
98 // todo use callback here to make generic
99 }
100 root = next;
101 }
102 }
答案 0 :(得分:1)
实际上编写处理给定算法的“任意”(通用)数据类型的C函数是非常可能的,而且相当容易。
一个技巧是使用void
指针并设计API,使用包装器函数将指针转换为实际类型,从而获得与C允许的类型检查和安全性一样多的好处。 。唯一困难的部分是编译器通常不会帮助您将类型信息传递给您的实现,尽管一些编译器确实支持typeof()
扩展,这使得编写可以为您完成工作的包装器宏成为可能。< / p>
一些例子:
此示例似乎以我建议的方式使用typeof()
:https://github.com/troydhanson/uthash/tree/master/src
这是一个有趣的基于预处理器宏的库,其灵感来自标准模板库:http://sglib.sourceforge.net/
这可能是C中泛型类型的通用算法最完整的例子之一,虽然它有点丑陋且非常冗长,可能效率不高:http://home.gna.org/gdsl/
虽然它使用嵌入式union
而不是void
指针,但这个答案提供了很好的建议。如果您提前知道所有可能类型的数据将是什么,union
是理想的:
https://stackoverflow.com/a/2891570/816536
从通用数据结构构建高级结构(如列表)的另一种有趣的方法是可以被描述为内向外技术(我也喜欢这个!)。我将在4.4BSD queue(3)和tree(3)宏中找到内部向外技术的规范实现,其中有一些更易读的解释和示例:
这个答案描述了一种严重依赖于预处理器的技术,但它要求您事先知道所有对象类型是什么,或者强制用户为其特定数据类型编写中间标题:{{3} }
另见这些答案:
答案 1 :(得分:0)
以下是通用树销毁的示例:
// GenericTree.h
void genericTreeDestroy(void *treeNode, void (*fUserFree)(void *));
// GenericTree.c
typedef struct TREE_NODE {
struct TREE_NODE *left;
struct TREE_NODE *right;
};
void genericTreeDestroy(struct TREE_NODE *treeNode, void (*fUserFree)(void *))
{
if (treeNode->left) genericTreeDestroy(treeNode->left, fUserFree);
if (treeNode->right) genericTreeDestroy(treeNode->right, fUserFree);
if (fUserFree) fUserFree(treeNode);
free(treeNode);
}
// UserStuff.c
#include "GenericTree.h"
typedef struct MY_TREE_NODE {
struct MY_TREE_NODE *left;
struct MY_TREE_NODE *right;
int some_value;
char *name;
};
void my_freedata(struct MY_TREE_NODE *node);
void main(void)
{
struct MY_TREE_NODE *myTree= calloc(1,sizeof(struct MY_TREE_NODE));
myTree->name= malloc(strlen("Hello world")+1);
genericTreeDestroy(myTree, my_freedata);
}
void my_freedata(struct MY_TREE_NODE *node)
{
free(node->name);
}
诀窍是所有树必须以左右成员开始。 .h文件使用genericTreeDestroy
参数定义void *
,.c文件将其定义为struct TREE_NODE *
。通过这种方式,用户可以将任何树节点类型传递给它。
接下来,用户可以定义任何树节点类型(只要它与左右成员一起生成)并调用泛型函数来销毁它。泛型函数可以被赋予一个函数来清除用户定义的节点类型,如果不需要则可以为null。
以同样的方式定义其他功能。这是一个搜索功能:
// .h
void *generic_tree_search (void *tree, void *value, int (*fUserCmp)(void *value, void *node));
// .c
void *generic_tree_search (struct TREE_NODE *treeNode, void *value, int (*fUserCmp)(void *value, void *node))
{
while (treeNode) {
switch (fUserCmp(value,treeNode) {
case -1: if (treeNode->left) treeNode= treeNode->left; else return(0); break;
case 0: return(treeNode);
case +1: if (treeNode->right) treeNode= treeNode->right; else return(0); break;
}
}
return(0);
}