有没有办法传递带泛型参数的函数指针?

时间:2015-02-12 23:35:31

标签: c function-pointers

我正在实现一个通用的单链表,其中列表节点存储指向其数据的指针。

typedef struct sll_node
{   
    void *data;
    struct sll_node *next;
} sll_node;

为了实现一个适用于任何类型数据的通用查找子例程,我编写了它,以便它将一个函数指针作为参数作为参数,如下所示:

/* 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);

我预计它会起作用。但是没有(编辑:它编译了一个警告,但我有 - 错误!),我不得不在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);

这两个都不会编译,因为:“只有函数定义才允许没有类型的参数列表”

3 个答案:

答案 0 :(得分:2)

要在没有包装器或强制转换的情况下使用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 int (*cmpfunc)(const void *, const void *);
result = findNode( "world", (cmpfunc)strcmp, root );

编辑:在阅读@WilburVandrsmith链接的this post之后,我决定按原样保留此答案。我将其留给读者来决定提议的演员是否违反了规范中的以下段落:

  

如果转换的指针用于调用其类型不是的函数   与指向类型兼容,行为未定义。

兼容或不兼容,这是你决定的问题。

答案 1 :(得分:0)

您上一次尝试的解决方案最接近正确。定义类型函数指针中的参数需要使用它们的数据类型声明,就像使用常规函数声明一样,如下所示:

typedef int (*equality_fn)(char *a, char *b);
sll_node *sll_find(void *data, equality_fn eq, sll_node *root);

<强>更新

为了使它更通用,使用void指针,然后在equality_fn的匹配函数定义中将传递的void指针类型转换为所需的数据类型:

typedef int (*equality_fn)(void *a, void *b);
sll_node *sll_find(void *data, equality_fn eq, sll_node *root);

要记住的其他重要事项是指针是一个指针 是一个指针 ,无论它指向什么或它是怎样的最初定义。所以,你可以有一些函数指针,一个void指针,或一个指向byte,char,int的指针 - 任何东西 - 只要你在代码中正确处理它并将其强制转换回有效类型之前试图使用它。

大多数编码人员在C中没有充分利用的其他东西是函数名称本身实际上只是在运行时调用的地址,因此它们也是指针。 ;)

答案 2 :(得分:0)

我对这个难题的解决方案是(顺便避免指针类型定义):

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运行时间惩罚 - 您希望在此交易结束时取决于您的应用程序。