简体   繁体   English

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

[英]Is there a way to pass a function pointer with generic arguments?

I am implementing a generic singly linked list where list nodes store a pointer to their data. 我正在实现一个通用的单链列表,其中列表节点存储指向其数据的指针。

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

To implement a generic find subroutine that works with any kind of data, I wrote it so that it takes as an argument a function pointer to the comparison function as follows: 为了实现适用于任何类型数据的通用find子例程,我编写了它,以使其将指向比较函数的函数指针作为参数,如下所示:

/* eq() must take 2 arguments. ex: strcmp(char *, char *) */
sll_node *sll_find(void *data, int (*eq)(), sll_node *root);

You can pass the appropriate function pointer that works with the data type at hand.. So if you store strings in the list nodes, you can pass strcmp as the eq() function, and so on. 您可以传递适合于手头数据类型的函数指针。因此,如果将字符串存储在列表节点中,则可以将strcmp作为eq()函数传递,依此类推。 It works but I'm still not satisfied.. 它有效,但我仍然不满意。

Is there a way to explicitly specify the number of comparison function parameters without giving up its generality? 有没有一种方法可以明确指定比较函数参数的数量而又不放弃其通用性?

I tried this at first: 我首先尝试了这个:

sll_node *sll_find(void *data, int (*eq)(void *, void *), sll_node *root);

I expected it to work. 我希望它能工作。 But no (edit: it compiles with a warning but I have -Werror on!), I had to write a wrapper function around strcmp to make it conform to the eq prototype. 但是不行(编辑:它编译时带有警告,但我打开了-Werror!),我不得不围绕strcmp编写包装函数,使其符合eq原型。

I then tried: 然后,我尝试:

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

or: 要么:

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

which both wouldn't compile since: "a parameter list without types is only allowed in a function definition" 由于“仅在函数定义中允许使用不带类型的参数列表”,因此两者均无法编译

To use strcmp without a wrapper or a cast, the declaration needs to be 要在没有包装或strcmp情况下使用strcmp ,声明必须为

sll_node *findNode(void *data, int (*eq)(const char *, const char *), sll_node *root);

On the other hand, if you declare the args as const void * , then you can avoid the wrapper by casting strcmp to the appropriate type. 另一方面,如果将args声明为const void * ,则可以通过将strcmp转换为适当的类型来避免包装器。

Method 1: direct cast, messy but effective 方法1:直接投射,凌乱但有效

    result = findNode( "hello", (int(*)(const void *, const void *))strcmp, root );

Method 2: typedef the comparison function, and then use it to cast 方法2:比较函数,然后使用typedef进行强制转换

typedef int (*cmpfunc)(const void *, const void *);
result = findNode( "world", (cmpfunc)strcmp, root );

Edit: After reading this post that @WilburVandrsmith linked, I've decided to leave this answer as is. 编辑:阅读完@WilburVandrsmith链接的帖子后 ,我决定保留此答案。 I leave it up to the reader to decide whether the proposed cast violates the following paragraph from the specification: 我让读者自己决定提议的演员表是否违反了规范中的以下段落:

If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined. 如果使用转换后的指针来调用其类型与指向的类型不兼容的函数,则该行为是不确定的。

Compatible or not compatible, that is the question, you decide. 兼容还是不兼容,这是您决定的问题。

Your last attempted solution is the closest to being correct. 您最后一次尝试的解决方案是最接近正确的解决方案。 The parameters in your defined-type function pointer need to be declared with their data types, just like you would with a regular function declaration, like so: 定义类型的函数指针中的参数需要使用其数据类型进行声明,就像使用常规函数声明一样,如下所示:

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

UPDATE 更新

To make it more generic use void pointers, and then type cast the passed void pointers to the needed data type in the matching function definition for equality_fn : 为了使其更通用,请使用void指针,然后将传递的void指针类型转换为equality_fn的匹配函数定义中的所需数据类型:

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

Something else important to remember is that a pointer is a pointer is a pointer , regardless of what it's pointing at or how it was originally defined. 还有一点要记住的重要一点 ,无论指针指向什么或最初是如何定义的,指针就是指针 就是指针 So, you can have some function pointer, or a void pointer, or a pointer to a byte, a char, an int--anything--as long as you handle it properly in your code and cast it back to a valid type before attempting to use it. 因此,只要您在代码中正确处理并将其转换回有效类型之前,就可以使用一些函数指针,void指针或指向字节,char,int的指针(任何东西)尝试使用它。

Something else that most coders don't take much advantage of in C is that function names themselves are really just addresses that are called at run-time, and so they are also pointers. 大多数编码人员在C语言中没有充分利用的其他优点是,函数名称本身实际上只是在运行时调用的地址,因此它们也是指针。 ;) ;)

My solution to this conundrum would be (avoiding pointer typedefs, incidentally): 我对这个难题的解决方案是(顺便提一下,避免使用指针typedef):

typedef int equality_fn(const void *a, const void *b);

sll_node *sll_find(void *data, equality_fn *eq, sll_node *root);

Then make all your comparators be of type equality_fn . 然后,使所有比较器的类型都为equality_fn If you need to actually have a function then so be it: 如果您实际上需要一个功能,那就去吧:

equality_fn eq_strcmp;  // a prototype

// ...

int eq_strcmp(const void *a, const void *b) { return strcmp(a, b); }

Gain lots of type safety in exchange for a potential picosocopic runtime penalty - which end of this trade you want to be on depends on your application. 获得大量类型安全性,以换取潜在的picosocopic运行时惩罚-您要从事的这笔交易的哪一端取决于您的应用程序。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM