有没有办法传递带有通用参数的函数指针?
Is there a way to pass a function pointer with generic arguments?
我正在实现一个通用的单链表,其中列表节点存储指向其数据的指针。
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);
我希望它能工作。但是没有(编辑:它编译时带有警告,但我有 -Werror on!),我不得不围绕 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);
两者都无法编译,因为:"a parameter list without types is only allowed in a function definition"
要在没有包装器或强制转换的情况下使用 strcmp
,声明需要
sll_node *findNode(void *data, int (*eq)(const char *, const char *), sll_node *root);
另一方面,如果将 args 声明为 const void *
,则可以通过将 strcmp
转换为适当的类型来避免包装。
方法一:直接施法,乱七八糟但有效
result = findNode( "hello", (int(*)(const void *, const void *))strcmp, root );
方法二:typedef比较函数,然后用它来cast
typedef int (*cmpfunc)(const void *, const void *);
result = findNode( "world", (cmpfunc)strcmp, root );
编辑:在阅读了@WilburVandrsmith 链接的 this post 之后,我决定保留这个答案。我将其留给 reader 来决定提议的转换是否违反了规范中的以下段落:
If a converted pointer is used to call a function whose type is not
compatible with the pointed-to type, the behavior is undefined.
兼容不兼容,这是你的问题。
您最后一次尝试的解决方案是最接近正确的。定义类型函数指针中的参数需要使用它们的数据类型进行声明,就像使用常规函数声明一样,如下所示:
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 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 运行时惩罚 - 您希望在这个交易的哪一端取决于您的应用程序。
我正在实现一个通用的单链表,其中列表节点存储指向其数据的指针。
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);
我希望它能工作。但是没有(编辑:它编译时带有警告,但我有 -Werror on!),我不得不围绕 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);
两者都无法编译,因为:"a parameter list without types is only allowed in a function definition"
要在没有包装器或强制转换的情况下使用 strcmp
,声明需要
sll_node *findNode(void *data, int (*eq)(const char *, const char *), sll_node *root);
另一方面,如果将 args 声明为 const void *
,则可以通过将 strcmp
转换为适当的类型来避免包装。
方法一:直接施法,乱七八糟但有效
result = findNode( "hello", (int(*)(const void *, const void *))strcmp, root );
方法二:typedef比较函数,然后用它来cast
typedef int (*cmpfunc)(const void *, const void *);
result = findNode( "world", (cmpfunc)strcmp, root );
编辑:在阅读了@WilburVandrsmith 链接的 this post 之后,我决定保留这个答案。我将其留给 reader 来决定提议的转换是否违反了规范中的以下段落:
If a converted pointer is used to call a function whose type is not compatible with the pointed-to type, the behavior is undefined.
兼容不兼容,这是你的问题。
您最后一次尝试的解决方案是最接近正确的。定义类型函数指针中的参数需要使用它们的数据类型进行声明,就像使用常规函数声明一样,如下所示:
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 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 运行时惩罚 - 您希望在这个交易的哪一端取决于您的应用程序。