11

So I've got the following expression:

int (*f1(int(*a)(int, int))) (int, int);

and I'm trying to make sense out of it, but it's confusing. I figured out that "a" is a pointer to a function which takes 2 arguments(int, int). Then f1 seems to be a pointer to another function that takes 2 int arguments. But what's confusing me is how f1 relates to a.

Can someone give me some hints or properly explain what the above expression is doing?

Diez
  • 111
  • 2
  • 3
    Please tell me why are people writing C++ code using function pointers. std::function is available – Ed Heal Jan 13 '18 at 12:58
  • That's not the point of the question. I've got this line, I'm trying to figure out what it means and I'm stuck. – Diez Jan 13 '18 at 13:01
  • That is why it is a comment? But one should think seriously why is one doing this – Ed Heal Jan 13 '18 at 13:05
  • 6
    https://cdecl.org/ says: "declare f1 as function (pointer to function (int, int) returning int) returning pointer to function (int, int) returning int" – kfx Jan 13 '18 at 13:08
  • 4
    See also: http://c-faq.com/decl/spiral.anderson.html – kfx Jan 13 '18 at 13:08
  • 1
    Possible duplicate of [How do I understand complicated function declarations?](https://stackoverflow.com/questions/1448849/how-do-i-understand-complicated-function-declarations) I do see that that question is about C, while this one is about C++, but this one doesn't have anything C++-specific. – Ruslan Jan 13 '18 at 19:07

4 Answers4

10

It declares f1 as a function with a single parameter called a. Both type of the parameter and return type are "pointer to function with two int parameters returning int".


Here's how you parse it:

// f1 is...
      f1
// ...a function...
      f1(                 )
// ...with a single parameter called `a`, which is...
      f1(     a           )
// ...a pointer to...
      f1(    *a           )
// (skip parentheses)
      f1(   (*a)          )
// ...a function...
      f1(   (*a)(        ))
// ...with two `int` parameters...
      f1(   (*a)(int, int))
// ...returning an `int`. The `f1` itself returns...
      f1(int(*a)(int, int))
// ...a pointer to...
     *f1(int(*a)(int, int))
// (skip parentheses)
    (*f1(int(*a)(int, int)))
// ...a function...
    (*f1(int(*a)(int, int))) (        )
// ...with two int parameters...
    (*f1(int(*a)(int, int))) (int, int)
// ...returning an `int`.
int (*f1(int(*a)(int, int))) (int, int)
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
6

This is a declaration of the function f1 which takes a parameter a- a pointer to function which takes 2 ints as argument and returns an int - and returns a pointer to function of the same type.

Breaking it down with a typedef:

typedef int(*t)(int, int);

t f1(t a); //this is your declaration
mdatsev
  • 3,054
  • 13
  • 28
5

The tip in C is to read a declaration as it were an expression. This is this famous symmetrie that make C elegant.

How to read it? following the operators precedence rules:

  1. *a: if I dereference variable a;
  2. (*a)(int,int): and then call it with two integers;
  3. int (*a)(int,int): then I get an integer;

So a is a pointer to a function taking two ints as parameter and returning an int.

Then:

  1. f( int(*a)(int,int) ) if I call f with the argument a;
  2. *f( int(*a)(int,int) ) and then I dereference the result;
  3. (*f( int(*a)(int,int) )(int,int) and then call this result with 2 int as argument
  4. int (*f( int(*a)(int,int) )(int,int) I get an int

So f is a function taking an a as argument and returning a pointer to a function that take two ints as argument and returning an int. So f return type is the same as its argument return type. It could have been simpler:

using ftype = int(*)(int,int);
ftype f( ftype a);
Oliv
  • 17,610
  • 1
  • 29
  • 72
  • 2
    Note that the way C++ extends this C syntax with references does not appear to follow this pattern. E.g. `int &x;` declares `x` as a reference to `int`, but it doesn't mean that `&x` has type `int` (it actually has type `int*`). So this is all true only if your declaration doesn't have references in it. – Ruslan Jan 13 '18 at 19:04
  • Arhhh reference are monsters: const pointers with pointed object semantic. – Oliv Jan 13 '18 at 19:10
4

a is the name of f1's only parameter; when you remove it, then you can use https://cdecl.org/ to decipher the entire declaration:

declare f1 as function (pointer to function (int, int) returning int) returning pointer to function (int, int) returning int

So f1 is a function. It takes a function pointer (called a) and it returns a function pointer.

Both of those function pointers are for functions which take two ints and return an int.

Here is an example to see it all in action:

#include <iostream>

int passed(int x, int y) { std::cout << "passed\n"; return x * y; }
int returned(int x, int y) { std::cout << "returned\n"; return x + y; }

// a is redundant here, where we just declare f1:
int (*f1(int(*a)(int, int))) (int, int);

// but not here, where we define f1:
int (*f1(int(*a)(int, int))) (int, int)
{
    std::cout << "f1\n";
    int result_of_passed = a(10, 10);
    std::cout << result_of_passed << '\n';
    return returned;
}

int main()
{
    int x = f1(passed)(10, 10);
    std::cout << x << '\n';
}

Output:

f1
passed
100
returned
20
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62