有可能" typedef"(各种)一个函数原型?

时间:2018-02-14 19:05:00

标签: c typedef function-prototypes

我有多个彼此相似的函数 - 它们采用相同的参数,并返回相同的类型:

double mathFunction_1(const double *values, const size_t array_length);

我已经使用了typedef指向这些函数的指针,因为我将它们存储为一个数组,以便在相同的数据上轻松使用它们,映射它们等等。:

typedef double (* MathFunction_ptr )(const double *, const size_t);

double proxy(MathFunction_ptr mathfun_ptr, const double *values, const size_t array_length);

我想要实现的是与声明和定义函数类似的易用性,就像我已经使用指针一样。

因此,我正在考虑使用类似的typedef来让我更容易编写实际的函数。我尝试这样做:

// declaration
typedef double MathFunction (const double *values, const size_t array_length);
MathFunction mathFunction_2;

以下方法部分有效。它让我可以节省一些按键"在声明中,但必须完全输入定义。

double mathFunction_2(const double *values, const size_t array_length)
{
    // ...
}

我通过搜索更多此问题找到的是:Can a function prototype typedef be used in function definitions?

然而,它并没有提供许多替代品,只是重申我根据标准禁止在其他实验中尝试做的事情。它提供的唯一选择是使用

#define FUNCTION(name) double name(const double* values, size_t array_length)

这听起来很笨(因为我对使用预处理器持谨慎态度和怀疑态度)。

我尝试做什么的替代方案是什么?

我试过的另外两种方法不起作用(而且,正如我刚才读到的那样,根据C标准6.9.1禁止和绝对错误):

1.这种方法不起作用,因为这意味着我告诉它定义变量mathFunction_2(我相信变量被视为指针,但我不明白这一点(好吧)就像一个功能:

MathFunction mathFunction_2
{
    // ...
}

2.这种方法不起作用,因为这意味着我告诉它创建一个返回函数的函数(在C语言中不可接受):

MathFunction mathFunction_2()
{
    // ...
}

3 个答案:

答案 0 :(得分:8)

您可以使用typedef作为签名(另请参阅this):

typedef double MathFunction_ty (const double *, const size_t);

然后声明相同签名的几个函数:

MathFunction_ty func1, func2;

或使用它声明一些函数指针

MathFunction_ty* funptr;

等...... C11中的所有内容,请阅读n1570

  

但是必须完全输入定义。

当然,因为你需要在函数的名称(并且这些名称​​不是函数类型的一部分) >定义。因此

double func1(const double*p, const size_t s) {
  return (double)s * p[0];
}

double func1(cont double*arr, const size_t ix) {
   return arr[ix];
}

具有相同的类型(上面用MathFunction_ty表示的那个),即使它们的形式参数(或形式参数)的命名方式不同。

您可能滥用预处理器并使用丑陋的宏来缩短此类函数的定义

// ugly code:
#define DEFINE_MATH_FUNCTION(Fname,Arg1,Arg2) \
   double Fname (const double Arg1, const size_t Arg2)
DEFINE_MATH_FUNCTION(func1,p,s) { return (double)s * p[0]; }

我发现这样的代码令人困惑且难以理解。我不建议这样编码,即使它确实可行。但有时我会编写类似的代码(其他原因)。

(顺便说一句,想象一下如果C 需要每个第一个正式参数都被命名为$1,每个第二个正式参数都被命名为$2等等...... ;恕我直言,这将使一个不太可读的编程语言;所以正式参数的名称对人类读者很重要,即使系统名称会使编译器的生活更简单)

另请阅读λ-calculusanonymous functions(C没有它们,但C ++有lambda expressions),closures(它们不是 C函数,因为它们具有封闭值,因此将代码与数据混合; C ++具有std::function - s),callbacks(必要的约定到“模仿”闭包)...阅读SICP,它将改善您对C或C ++的思考。另请参阅that回答。

答案 1 :(得分:3)

不幸的是,CI并不相信没有任何方法可以在不使用预处理器宏的情况下做你要求的事情,至少我个人同意你的评估他们很笨重而且要避免(尽管这个是一个意见问题,可以辩论。)

在C ++中,您可以利用auto parameters in lambdas

您在此处显示的示例功能签名确实不复杂,我不担心感知到的重复。如果签名要复杂得多,我会将其视为代码气味"你的设计可以改进,我会把注意力集中在那里,而不是用于缩短声明的句法方法。这不是这里的情况。

答案 2 :(得分:1)

是的,你可以。实际上,typedef声明的目的是使用类型标识符来声明一种变量。唯一的事情是当你在头文件中使用这样的声明时:

 typedef int (*callback_ptr)(int, double, char *);

然后你声明如下:

 callback_ptr function_to_callback;

不清楚你是在声明一个函数指针以及参数的数量和类型,但尽管如此,一切都是正确的。

最后,我想特别注意一些事情。当你处理这样的事情时,通常会更便宜,更快速地去编译器并尝试一些例子。如果编译器在没有任何投诉的情况下做了你想做的事情,最可能的事情就是你是对的。

#include <stdio.h>
#include <math.h>

typedef double (*ptr_to_mathematical_function)(double);

extern double find_zero(ptr_to_mathematical_function f, double aprox_a, double aprox_b, double epsilon);

int main()
{
#define P(exp) printf(#exp " ==> %lg\n", exp)

    P(find_zero(cos, 1.4, 1.6, 0.000001));
    P(find_zero(sin, 3.0, 3.2, 0.000001));
    P(find_zero(log, 0.9, 1.5, 0.000001));
}

double find_zero(
    ptr_to_mathematical_function f, 
    double a, double b, double eps)
{
    double f_a = f(a), f_b = f(b);
    double x = a, f_x = f_a;

    do {
        x = (a*f_b - b*f_a) / (f_b - f_a);
        f_x = f(x);

        if (fabs(x - a) < fabs(x - b)) {
           b = x; f_b = f_x;
        } else {
            a = x; f_a = f_x;
        }
    } while(fabs(a-b) >= eps);
    return x;
}

问题的第二个主要部分,如果你遇到这样的问题,唯一可以解决的问题就是使用宏(看看我是如何重复上述printf(3)函数调用的,但是不完全相同的参数列表,以及如何在下面解决问题):

#define MY_EXPECTED_PROTOTYPE(name) double name(double x)

然后,在定义中,只需使用:

MY_EXPECTED_PROTOTYPE(my_sin) {
    return sin(x);
}

MY_EXPECTED_PROTOTYPE(my_cos) {
    return cos(x);
}

MY_EXPECTED_PROTOTYPE(my_tan) {
    return tan(x);
}
...

将扩展为:

double my_sin(double x) {
...
double my_cos(double x) {
...
double my_tan(double x) {
...

您甚至可以在头文件中使用它,例如:

MY_EXPECTED_PROTOTYPE(my_sin);
MY_EXPECTED_PROTOTYPE(my_cos);
MY_EXPECTED_PROTOTYPE(my_tan);

正如其他答案所指出的那样,还有其他语言(C ++)可以为此提供支持等等,但我认为这超出了范围。