Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to "typedef"(of sorts) a function prototype?

I have multiple functions that are similar to each other - they take in the same arguments, and return the same type:

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

I already use typedef'd pointers to those functions, as I store them as an array to easily use any number of them on the same data, map them etc.:

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

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

What I want to achieve, is a similar ease-of-use with declaring and defining the functions, as I already have with using pointers to them.


Thus, I was thinking about using a similar typedef to make it easier for me to write the actual functions. I tried doing it like this:

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

The following approach works partially. It lets me "save a few keystrokes" in the declaration, however the definition has to be fully typed out.

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

What I found by searching more for this issue is this: Can a function prototype typedef be used in function definitions?

However it doesn't provide many alternatives, and only reaffirms that what I tried to do in my other experiments is forbidden according to the Standard. The only alternative it provides is using

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

which sounds clunky to me(as I'm wary and skeptical of using the preprocessor).

What are the alternatives to what I'm trying to do?


Two other approaches I tried that don't work(and, as I just read, are forbidden and absolutely wrong according to the C standard 6.9.1):

1.This approach doesn't work, as it means that I'm telling it to define a variable mathFunction_2(I believe that variable is treated as a pointer, though I don't understand this well enough yet) like a function:

MathFunction mathFunction_2
{
    // ...
}

2.This approach doesn't work, as it means I'm telling it to create a function which returns a function(unacceptable in the C language):

MathFunction mathFunction_2()
{
    // ...
}
like image 434
tehftw Avatar asked Feb 14 '18 19:02

tehftw


People also ask

Can typedef be used for functions?

A typedef, or a function-type alias, helps to define pointers to executable code within memory. Simply put, a typedef can be used as a pointer that references a function.

Which is the correct method to typedef a function pointer?

h> void upton(int n) { for (int i = 1; i <= n; ++i) printf("%d\n", i); } void nth(int n) { printf("%d\n, n); } Notice they both are having similar signature. So we can create function pointer which would be able to point both the functions . It can be done by the method given below.

How do you describe a function prototype?

A function prototype is a definition that is used to perform type checking on function calls when the EGL system code does not have access to the function itself. A function prototype begins with the keyword function, then lists the function name, its parameters (if any), and return value (if any).

Is it necessary to write function prototype?

No, it is not required to write prototypes if the called function is written before the calling function. Prototypes are written so that the compiler can understand that the declared function is defined later. So in that case, even if the called function is written after than calling function, it won't raise any error.


3 Answers

Unfortunately in C I don't believe there is any way to do what you're asking without using preprocessor macros, and personally at least I agree with your assessment that they are clunky and to be avoided (though this is a matter of opinion and open to debate).

In C++ you could potentially take advantage of auto parameters in lambdas.

The example function signatures you show here really aren't complicated and I wouldn't worry about the perceived duplication. If the signatures were much more complicated, I would view this as a "code smell" that your design could be improved, and I'd focus my efforts there rather than on syntactic methods to shorten the declaration. That just isn't the case here.

like image 33
TypeIA Avatar answered Sep 23 '22 22:09

TypeIA


Yes, you can. Indeed, that's the purpose of the typedef declaration, to use a type identifier to declare a type of variable. The only thing is that when you use such a declaration in a header file:

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

and then you declare something like:

 callback_ptr function_to_callback;

it's not clear that you are declaring a function pointer and the number and type of the parameters, but despite of this, everything is correct.

Finally, I want to note you something particularly special. When you deal with something like this, it is normally far cheaper and quick to go to the compiler and try some example. If the compiler does what you want without any complaint, the most probable thing is that you are correct.

#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;
}

The second, and main part of your question, if you are having such a problem, the only way you can solve it is via using macros (see how I repeated the above printf(3) function calls with similar, but not identical parameter lists, and how the problem is solved below):

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

and then, in the definitions, just use:

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

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

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

that will expand to:

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

you can even use it in the header file, like:

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

As it has been pointed in other answers, there are other languages (C++) that give support for this and much more, but I think this is out of scope here.

like image 38
Luis Colorado Avatar answered Sep 25 '22 22:09

Luis Colorado


You could use a typedef for the signature (see also this):

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

and then declare several functions of the same signature:

MathFunction_ty func1, func2;

or declare some function pointer using that:

MathFunction_ty* funptr;

etc... All this in C11, read n1570.

however the definition has to be fully typed out.

Of course, since you need to give a name to each formal parameter (and such names are not part of the type of the function) in the function's definition. Therefore

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

and

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

have the same type (the one denoted by MathFunction_ty above), even if their formal parameters (or formal arguments) are named differently.

You might abuse of the preprocessor and have an ugly macro to shorten the definition of such functions:

// 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]; }

I find such code confusing and unreadable. I don't recommend coding like that, even if it is certainly possible. But sometimes I do code something similiar (for other reasons).

(BTW, imagine if C required every first formal argument to be named $1, every second formal argument to be named $2, etc...; IMHO that would make a much less readable programming langage; so formal parameter's name matters to the human reader, even if systematic names would make the compiler's life simpler)

Read also about λ-calculus, anonymous functions (C don't have them but C++ has lambda expressions), closures (they are not C functions, because they have closed values so mix code with data; C++ has std::function-s), callbacks (a necessary convention to "mimick" closures)... Read SICP, it will improve your thinking about C or C++. Look also into that answer.

like image 84
Basile Starynkevitch Avatar answered Sep 26 '22 22:09

Basile Starynkevitch