The C++ logo, by Jeremy Kratz, licensed under CC0 1.0 Universal Public Domain Dedication

How to declare function pointers

Notes published the
3 - 4 minutes to read, 762 words

Function pointers are a very useful feature, but the syntax for writing the type looks a little strange and makes them harder to read than necessary.

Consider

int foo(double, short);
int bar(double, short);


int baz(bool b){
  int (*f)(double, short) = b ? foo : bar;
  return f(0,0);
}

The variable f is a little bit hidden in int (*f)(double, short). Normally variable names come after the type, like in int value = 42.

Note that also with an array this is not completely true. The (sometimes optional) size of the array and the brackets come after the name (int arr[]) and are not written together with the rest of the type (int[] arr) as it can be done in Java.

Nevertheless, the syntax of the array, albeit somewhat inconsistent with other types, is more readable compared to the one for function pointers.

Improve readability by not writing the type

Type deduction

A first possibility to improve the readability is to create to avoid spelling the type out.

In this case, it is possible to deduce the type with auto:

int foo(double, short);
int bar(double, short);

int baz(bool b){
  auto f = b ? foo : bar;
  return f(0,0);
}

No temporary variables

Another possibility is to avoid creating temporaries:

int foo(double, short);
int bar(double, short);

int baz(bool b){
  return (b ? foo : bar)(0,0);
}

Create an alias for a function pointer

typedef

But when we need to use a variable (or in case of function overloads), and can’t deduce the type, it is still possible to improve readability by creating an alias with typedef or using.

The common approach is to take the type as-is, and put it behind a typedef:

int foo(double, short);
int bar(double, short);

typedef int (*fun)(double, short);


int baz(bool b){
  fun f = b ? foo : bar;
  return f(0,0);
}

While the code inside baz is more readable, the typedef is as hard to read as the first snippet of those notes.

using

Since C++11, there is a tendency in some projects to prefer using using

int foo(double, short);
int bar(double, short);

using fun = int (*)(double, short);

int baz(bool b){
  fun f = b ? foo : bar;
  return f(0,0);
}

While it is true that compared to the typedef the name of the type stands out in all places, how to write the declaration itself is not as easy to remember, in particular, because of (*).

using and decltype

An easy way to overcome the syntax issue is to get the type from an existing function with decltype, for example

int foo(double, short);
int bar(double, short);

using fun = decltype(&foo);

int baz(bool b){
  fun f = b ? foo : bar;
  return f(0,0);
}

But, for example, when defining an API, a function with the desired signature might not be available.

Alternate approach

What I believe is the most readable way to declare a function pointer is …​ not to alias a function pointer, but just a function type. Especially with typedef, as the syntax is the same for declaring a function, with typedef prepended, the syntax is easy to understand:

int foo(double, short);
int bar(double, short);

typedef int fun(double, short);


int baz(bool b){
  fun* f = b ? foo : bar;

  // a reference instead of a pointer would also be valid:
  //fun& f = b ? foo : bar;

  return f(0,0);
}

As fun is not a function pointer type, but a function type, we need to write fun* or fun& in baz. Especially when looking at API, seeing if a parameter (or return value) is a pointer or not, is indeed a nice feature. void bar(f*) gives the reader immediately some information compared to void bar(f).

Hiding pointers in a typedef does add very little in readability and makes the code harder to maintain. It forces the user to see what the typedef is, just to have an idea of what it is dealing with.

Writing

void bar(f*);

shows at least immediately the parameter is a pointer, even without looking at what f actually is.

Similarly one can use using instead of typedef, but as the syntax for creating such aliases does not match the declaration of a function, I’m not convinced that in this case using is better than typedef:

int foo(double, short);
int bar(double, short);

using fun = int(double, short);


int baz(bool b){
  fun* f = b ? foo : bar;

  return f(0,0);
}

Do you want to share your opinion? Or is there an error, some parts that are not clear enough?

You can contact me anytime.