在 C 语言中的通用函数指针

12

我有一个函数,它接受一个数据块和块的大小以及一个函数指针作为参数。然后它迭代遍历数据并对数据块中的每个元素执行计算。 以下是我正在做的基本概述:

int myfunction(int* data, int size, int (*functionAsPointer)(int)){
    //walking through the data and calculating something
    for (int n = 0; n < size; n++){
        data[n] = (*function)(data[n]);
    }
}

我作为参数传递的函数看起来像这样:

int mycalculation(int input){
    //doing some math with input
    //...
    return input;
} 

这段代码运行正常,但现在我需要将一个额外的变量传递给我的函数指针。大致应该是这样的:

int mynewcalculation(int input, int someVariable){
    //e.g.
    input = input * someVariable;
    //...
    return input;
}

有没有一种优雅的方式来实现这个目标,同时保持我的整体设计思路?


2
你为什么不能直接更改函数声明以包含额外的整数参数呢? - Daniel Bingham
@dbingham:如果我没有弄错的话,我需要第二个“myfunction”,它将使用两个“int”函数指针。问题是,将会有更多具有不同类型和参数数量的函数。这样会有点混乱我的设计。 - Lucas
我不确定我一开始是否正确理解了问题。无论如何,我认为Jefromi已经很好地涵盖了基础知识点。 - Daniel Bingham
是的,这就是问题所在。@dbingham:您说得对,您可以使用va_args来完成它 - 这将使您避免制作额外的结构来处理可能性。我只是认为void*方法看起来更干净,并且容错性较低。 - Cascabel
1个回答

15

使其完全通用的常规方法是使用void*,这当然会导致各种类型安全性问题:

int map_function(int* data, int size, int (*f_ptr)(int, void*), void *userdata){
    //walking through the data and calculating something
    for (int n = 0; n < size; n++){
        data[n] = (*f_ptr)(data[n], userdata);
    }
}

int simple_calculation(int input, void *userdata) {
    // ignore userdata and do some math with input
    return input;
}

int calculation_with_single_extra_arg(int input, void *userdata) {
    int input2 = * (int*) userdata;
    // do some math with input and input2
    return input;
}

int calculation_with_many_extra_args(int input, void *userdata) {
    my_data_t data = (my_data_t *) userdata;
    return input * (input * data->a + data->b) + data->c;
}

int main(int argc, char **argv) {
    int array[100];
    my_data_t userdata = { 1, 2, 3 };

    populate(array, 100);

    map_function(data, calculation_with_many_extra_args, &userdata);
}
任何要传递的函数都必须具有该原型,但你可以通过 "userdata" 传递任何想要的数据。这里绝对没有类型检查。
您也可以像dbingham建议的那样使用 "va_args";不过这并没有太大区别,因为映射函数的原型仍必须是 "int(int, va_list)"。
编辑:我倾向于使用 "void *" 方法。 "va_list" 并没有添加任何类型安全性,并增加了更多用户误差的可能性(特别是两次调用 "va_end" 或未正确实现 "va_arg" 循环)。 "void *" 也不会增加任何额外的代码行;它被干净地传递,用户只需将其解引用为(希望是)正确的类型(在一般情况下可能是一个结构体)。

我想不出一个干净的方法来做这件事,但是这里描述的void*方法还不错。这就是系统函数(如ioctl())的方式。您可以通过指针传递任何需要的数据(整数、结构体、NULL),但是函数和调用者都必须小心,因为您将没有任何类型检查来保护您。 - bta
我觉得我已经把自己搞糊涂了,但是我认为你在这一点上都讲得很好了。 - Daniel Bingham
@Lucas:那些警告是针对指向函数的调用吗?我认为它需要期望 void*,然后将其转换为实际类型。 - Cascabel
1
我非常确定我的示例现在已经运行良好,您可以在http://git.gnome.org/browse/glib/tree/glib/ghash.c上看到`g_hash_table_foreach`的使用,而且它在(grel.c、gscanner.c、gcache.c)中也有广泛的应用 - 这些绝对可行! - Cascabel
1
@Jefromi:非常感谢!这个方法可行,我之前一直在做类型转换时出了问题,加上所有的警告和void*指针,感觉有点自杀。 - Lucas
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接