在不使用匿名函数的情况下创建函数适配器
Creating a function adapter without the use of anonymous functions
我正在编写链表实现并想创建一个 foreach
函数。我想要这个函数的两个版本:一个供内部使用,迭代节点本身,另一个供最终用户迭代节点中保存的数据。由于每个代码几乎相同,我想根据第一个版本定义第二个版本。现在,我有两个 typedef
用于在每个版本中迭代的函数,以及 foreach 的内部版本:
// The internal-use version that takes a node
typedef void(*Foreach_Node_Function)(size_t index, Node* node);
// The end-user version that takes raw data
typedef void(*Foreach_Function)(size_t index, void* data);
void static foreach_node(Linked_List* ll, Foreach_Node_Function f) {
Node* current = ll->head;
for (size_t i = 0; i < length(ll); i++) {
(*f)(i, current);
current = current->next;
}
}
我需要能够编写一个适配器,将 Foreach_Node_Function
转换为 Foreach_Function
。如果我可以访问匿名函数,这将是微不足道的。我可以在 Foreach_Function
上关闭一个函数,并在传递给定数据之前修改它。像(假装 lambda
是一回事):
void foreach(Linked_List* ll, Foreach_Function f) {
Foreach_Node_Function adapted = lambda(size_t i, Node* node) {
// Only pass on the data to f
(*f)(i, node->data);
};
foreach_node(ll, adapted);
}
C 似乎不支持匿名函数,除非您愿意求助于 compiler specific hacks,我看不出没有它们如何实现。
这是否可以在 C 中实现,如果可以,如何实现?
通常当您有这样的回调函数时,您会添加一个额外的 void *
参数(通常称为 回调数据 或 用户数据) 让调用者将额外的数据传递给回调函数。所以你会:
typedef void(*Foreach_Node_Function)(size_t index, Node* node, void *userdata);
typedef void(*Foreach_Function)(size_t index, Node* node, void *userdata);
void static foreach_node(Linked_List* ll, Foreach_Node_Function f, void *f_userdata);
然后,你可以将正在适配的回调,以及它的 void *
参数传递给适配器回调:
struct foreach_node_adapter_userdata {
Foreach_Function f;
void *f_userdata;
};
static void foreach_node_adapter(size_t index, Node *node, void *userdata) {
struct foreach_node_adapter_userdata *adapter_userdata = userdata;
(*adapter_userdata->f)(index, node->data, adapter_userdata->f_userdata);
}
void foreach(Linked_List* ll, Foreach_Function f, void *f_userdata) {
struct foreach_node_adapter_userdata adapter_userdata = {f, f_userdata};
foreach_node(ll, foreach_node_adapter, &adapter_userdata);
}
因为你标记了这个 C11,你可以使用 _Generic
来达到这个目的。 _Generic
很好,因为它是类型安全的,不像老派 void*
那样非常危险。它还解决了没有与函数指针类型一起使用的 void*
的泛型等价物的问题。
您可以有一个 "functor" 函数来为容器中的每个项目调用,例如:
typedef void node_operation_t (size_t index, node_t* node);
// functions following this form:
void node_add (size_t index, node_t* node);
void node_delete (size_t index, node_t* node);
...
那么你想这样称呼它
linked_list_t list;
traverse(&list, node_delete);
(命名:"traverse" 是 C 中容器的通用迭代的常用术语,而 "foreach" 在其他各种语言中更像是一个循环关键字。)
您现在可以为此创建一个 _Generic
宏,接受链表或其他容器:
#define traverse(list, func) \
_Generic((func), \
node_operation_t*: traverse_linked_list, \
foo_operation_t*: traverse_foo \
)(list, func)
这通过检查函数指针类型来选择合适的函数。仅当不同的函数属于不同类型时才需要这样做,否则您也可以将 traverse
设为一个函数。
同样,可以使用不同的参数输入对同一容器类型进行多个操作。很多可能性。
traverse_linked_list
这里会是这样的:
void traverse_linked_list (linked_list_t* ll, node_operation_t* op)
它只是循环遍历列表并为每个节点调用 op
。如果您想将参数从调用者一直传递到每个单独的操作(如 "set all items to value 5"),它可以扩展为接受更多参数。
我正在编写链表实现并想创建一个 foreach
函数。我想要这个函数的两个版本:一个供内部使用,迭代节点本身,另一个供最终用户迭代节点中保存的数据。由于每个代码几乎相同,我想根据第一个版本定义第二个版本。现在,我有两个 typedef
用于在每个版本中迭代的函数,以及 foreach 的内部版本:
// The internal-use version that takes a node
typedef void(*Foreach_Node_Function)(size_t index, Node* node);
// The end-user version that takes raw data
typedef void(*Foreach_Function)(size_t index, void* data);
void static foreach_node(Linked_List* ll, Foreach_Node_Function f) {
Node* current = ll->head;
for (size_t i = 0; i < length(ll); i++) {
(*f)(i, current);
current = current->next;
}
}
我需要能够编写一个适配器,将 Foreach_Node_Function
转换为 Foreach_Function
。如果我可以访问匿名函数,这将是微不足道的。我可以在 Foreach_Function
上关闭一个函数,并在传递给定数据之前修改它。像(假装 lambda
是一回事):
void foreach(Linked_List* ll, Foreach_Function f) {
Foreach_Node_Function adapted = lambda(size_t i, Node* node) {
// Only pass on the data to f
(*f)(i, node->data);
};
foreach_node(ll, adapted);
}
C 似乎不支持匿名函数,除非您愿意求助于 compiler specific hacks,我看不出没有它们如何实现。
这是否可以在 C 中实现,如果可以,如何实现?
通常当您有这样的回调函数时,您会添加一个额外的 void *
参数(通常称为 回调数据 或 用户数据) 让调用者将额外的数据传递给回调函数。所以你会:
typedef void(*Foreach_Node_Function)(size_t index, Node* node, void *userdata);
typedef void(*Foreach_Function)(size_t index, Node* node, void *userdata);
void static foreach_node(Linked_List* ll, Foreach_Node_Function f, void *f_userdata);
然后,你可以将正在适配的回调,以及它的 void *
参数传递给适配器回调:
struct foreach_node_adapter_userdata {
Foreach_Function f;
void *f_userdata;
};
static void foreach_node_adapter(size_t index, Node *node, void *userdata) {
struct foreach_node_adapter_userdata *adapter_userdata = userdata;
(*adapter_userdata->f)(index, node->data, adapter_userdata->f_userdata);
}
void foreach(Linked_List* ll, Foreach_Function f, void *f_userdata) {
struct foreach_node_adapter_userdata adapter_userdata = {f, f_userdata};
foreach_node(ll, foreach_node_adapter, &adapter_userdata);
}
因为你标记了这个 C11,你可以使用 _Generic
来达到这个目的。 _Generic
很好,因为它是类型安全的,不像老派 void*
那样非常危险。它还解决了没有与函数指针类型一起使用的 void*
的泛型等价物的问题。
您可以有一个 "functor" 函数来为容器中的每个项目调用,例如:
typedef void node_operation_t (size_t index, node_t* node);
// functions following this form:
void node_add (size_t index, node_t* node);
void node_delete (size_t index, node_t* node);
...
那么你想这样称呼它
linked_list_t list;
traverse(&list, node_delete);
(命名:"traverse" 是 C 中容器的通用迭代的常用术语,而 "foreach" 在其他各种语言中更像是一个循环关键字。)
您现在可以为此创建一个 _Generic
宏,接受链表或其他容器:
#define traverse(list, func) \
_Generic((func), \
node_operation_t*: traverse_linked_list, \
foo_operation_t*: traverse_foo \
)(list, func)
这通过检查函数指针类型来选择合适的函数。仅当不同的函数属于不同类型时才需要这样做,否则您也可以将 traverse
设为一个函数。
同样,可以使用不同的参数输入对同一容器类型进行多个操作。很多可能性。
traverse_linked_list
这里会是这样的:
void traverse_linked_list (linked_list_t* ll, node_operation_t* op)
它只是循环遍历列表并为每个节点调用 op
。如果您想将参数从调用者一直传递到每个单独的操作(如 "set all items to value 5"),它可以扩展为接受更多参数。