Typedef does not work like typedef [type] [new name]
. The [new name]
part does not always come at the end.
You should look at it this way: if [some declaration]
declares a variable, typedef [same declaration]
would define a type.
E.g.:
int x;
declares a variable named x of type int -> typedef int x;
defines a type x as int.
struct { char c; } s;
defines a variable named s of some struct type -> typedef struct { char c; } s;
defines type s to be some struct type.
int *p;
declares a variable named p of type pointer to int ->
typedef int *p;
defines a type p as pointer to int.
And also:
int A[];
declares an array of ints named A -> typedef int A[];
declares a type A as an array of ints.
int f();
declares a function named f -> typedef int f();
declares a function type f as returning an int and taking no arguments.
int g(int);
declares a function name g -> typedef int g(int);
declares a function type g as returning an int and taking one int.
As an aside: Note that all function arguments come after the new name! As those types could be complicated as well, there can be a lot of text after the [new name]. Sadly, but true.
But those are not proper function pointers yet, just function types. I'm not sure a function type exists in C or C++, but it is useful as an intermediate step in my explanation.
To create a real function pointer, we have to add '*' to the name. Which, sadly, has wrong precedence:
typedef int *pf();
declares a function type pf as return a int*. Oops, that's not what was intended.
So use () to group:
typedef int (*pf)();
declares a function pointer type pf as returning an int and taking no arguments.
typedef int (&rf)();
declares a function reference type rf as returning an int and taking no arguments.
Let's now look at your examples and answer your questions:
typedef int (&rifii) (int, int);
declares a function reference type rifii as returning an int and taking two int arguments.
And obviously (?) button2[2]( );
will call copy();
.
Proper syntax without typedefs is hard to write correctly without a compiler, and hard to read even with a compiler:
void (*edit_ops[])() = { &cut, &paste, ©, &search };
void (*file_ops[])() = { &open, &append, & close, &write };
void (**button2)() = edit_ops;
void (**button3)() = file_ops;
button2[2]( );
Which is why everyone prefers typedefs when using function pointers.
When reading, find the place to start reading. Read as much to the right as you can, but observe the grouping by (). Then read to the left as much as you can, again limited by grouping (). After finishing everything inside the (), start with reading to the right, then to the left.
Applied to void (*edit_ops[])()
, this means that
- edit_ops is
(go to the right)
- an array
(hit the end of the group, so turn to the left)
- of pointer
(end of grouping)
- to a function taking
(parse the () to the right)
- no arguments
(go to the left)
- returning a void
For the experts:
To make it even more complicated, arguments can have names (which will be ignored), so it might even be hard to find where to start parsing! E.g. typedef int (*fp)(int x);
is valid and the same as typedef int (*fp)(int);
Names can even have () around them: typedef int (*fp)(int (x));
But as we have seen, argument names can be left out, so even the following is allowed: typedef int (*fp)(int ());
. This is still a function pointer taking a single int and return an int. In case you would like to make your code really hard to read...