[英]Is there a way to pass a function pointer with generic arguments?
我正在实现一个通用的单链列表,其中列表节点存储指向其数据的指针。
typedef struct sll_node
{
void *data;
struct sll_node *next;
} sll_node;
为了实现适用于任何类型数据的通用find子例程,我编写了它,以使其将指向比较函数的函数指针作为参数,如下所示:
/* eq() must take 2 arguments. ex: strcmp(char *, char *) */
sll_node *sll_find(void *data, int (*eq)(), sll_node *root);
您可以传递适合于手头数据类型的函数指针。因此,如果将字符串存储在列表节点中,则可以将strcmp作为eq()函数传递,依此类推。 它有效,但我仍然不满意。
有没有一种方法可以明确指定比较函数参数的数量而又不放弃其通用性?
我首先尝试了这个:
sll_node *sll_find(void *data, int (*eq)(void *, void *), sll_node *root);
我希望它能工作。 但是不行(编辑:它编译时带有警告,但我打开了-Werror!),我不得不围绕strcmp编写包装函数,使其符合eq原型。
然后,我尝试:
sll_node *sll_find(void *data, int (*eq)(a, b), sll_node *root);
要么:
typedef int (*equality_fn)(a, b);
sll_node *sll_find(void *data, equality_fn eq, sll_node *root);
由于“仅在函数定义中允许使用不带类型的参数列表”,因此两者均无法编译
要在没有包装或strcmp
情况下使用strcmp
,声明必须为
sll_node *findNode(void *data, int (*eq)(const char *, const char *), sll_node *root);
另一方面,如果将args声明为const void *
,则可以通过将strcmp
转换为适当的类型来避免包装器。
方法1:直接投射,凌乱但有效
result = findNode( "hello", (int(*)(const void *, const void *))strcmp, root );
方法2:比较函数,然后使用typedef进行强制转换
typedef int (*cmpfunc)(const void *, const void *);
result = findNode( "world", (cmpfunc)strcmp, root );
编辑:阅读完@WilburVandrsmith链接的帖子后 ,我决定保留此答案。 我让读者自己决定提议的演员表是否违反了规范中的以下段落:
如果使用转换后的指针来调用其类型与指向的类型不兼容的函数,则该行为是不确定的。
兼容还是不兼容,这是您决定的问题。
您最后一次尝试的解决方案是最接近正确的解决方案。 定义类型的函数指针中的参数需要使用其数据类型进行声明,就像使用常规函数声明一样,如下所示:
typedef int (*equality_fn)(char *a, char *b);
sll_node *sll_find(void *data, equality_fn eq, sll_node *root);
更新
为了使其更通用,请使用void指针,然后将传递的void指针类型转换为equality_fn
的匹配函数定义中的所需数据类型:
typedef int (*equality_fn)(void *a, void *b);
sll_node *sll_find(void *data, equality_fn eq, sll_node *root);
还有一点要记住的重要一点是 ,无论指针指向什么或最初是如何定义的,指针就是指针 就是指针 。 因此,只要您在代码中正确处理并将其转换回有效类型之前,就可以使用一些函数指针,void指针或指向字节,char,int的指针(任何东西)尝试使用它。
大多数编码人员在C语言中没有充分利用的其他优点是,函数名称本身实际上只是在运行时调用的地址,因此它们也是指针。 ;)
我对这个难题的解决方案是(顺便提一下,避免使用指针typedef):
typedef int equality_fn(const void *a, const void *b);
sll_node *sll_find(void *data, equality_fn *eq, sll_node *root);
然后,使所有比较器的类型都为equality_fn
。 如果您实际上需要一个功能,那就去吧:
equality_fn eq_strcmp; // a prototype
// ...
int eq_strcmp(const void *a, const void *b) { return strcmp(a, b); }
获得大量类型安全性,以换取潜在的picosocopic运行时惩罚-您要从事的这笔交易的哪一端取决于您的应用程序。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.