我正在尝试在C中的链表上编写通用搜索功能。 功能如下:
void* get_when_true(linked_list *l,
int (*condition)(const void*)) {
node **walk = &l->head;
while (*walk != NULL && !condition((*walk)->data)) {
walk = &((*walk)->next);
}
return (*walk)->data;
}
基本上 condition 是一个函数指针,该函数将针对链表的每个元素进行测试,每当此函数返回true时,将返回特定节点。
我的链表中的每个节点都存储了一些ID。我的问题是,如果我不想搜索特定的ID,我必须编写一个单独的搜索功能,只能检查该特定的ID。基本上如果我不想寻找ID 10.我必须写一个函数
int isID_10(const void *a) {
// cast A to appropriate poitner
return a->ID == 10;
}
如果我想测试其他一些说30的ID,我将不得不再写一个不同的功能..我想你明白了我的意思。
我只能考虑currying来解决这个问题..但不幸的是C不提供currying,我不能使用一些特定于编译器的工作,因为我的代码应该在所有平台上编译。
使用全局变量作为搜索ID是一种解决方案,但我不想避免它。
如何解决这个问题?
答案 0 :(得分:3)
您应该向get_when_true
添加另一个传递给回调的参数。这可以只是另一个void *
,因此它可以指向任何类型的结构。
void* get_when_true(linked_list *l, void *ctx,
int (*condition)(const void* data, void *ctx)) {
node **walk = &l->head;
while (*walk != NULL && !condition((*walk)->data, ctx)) {
walk = &((*walk)->next);
}
return (*walk)->data;
}
这是一个回调:
int isID(const void *data, void *ctx) {
int id = (int)ctx;
const struct whatever *a = data;
return a->ID == id;
}
像这样使用:
get_when_true(list, (void*)10, isID);
在这里,我甚至不需要一个结构,所以我"欺骗"然后将单个整数转换为void*
。
由于C本身不支持闭包,因为有数据附加"对于(回调)函数指针,我编写的任何接受回调的函数也需要传递给回调的void*
。
虽然C不支持闭包,但有一个GNU扩展提供了嵌套函数,可以访问父函数范围内的变量。因此,使用您的原始代码(没有我的附加参数):
int get_when_id(linked_list *l, int id) {
int condition(const void *data) {
const struct whatever *a = data;
return a->ID == id; // id is "closed over" from parent
}
return get_when_true(list, condition);
}
这需要可执行堆栈。如果你看一下反汇编,你会发现实际传递给get_when_true
的函数指针不是condition
本身的地址。而是传递堆栈上生成的可执行文件 thunk 的地址。这个thunk将指向封闭变量的指针传递给实际的condition
函数,因此它们会自动在那里可用。
另见: