Function pointers in C/C++

Using pointers to functions is an advanced technique that can really simplify code. Indeed, C++ uses them to implement class polymorphism.

But one of the reasons that they’re seen as being so esoteric is that their syntax in C and C++ is… opaque. But it doesn’t have to be!

Extracted from my edit of the Wikipedia article

Introduction

The C and C++ syntax for pointers to functions used in all the textbooks is difficult to read and explain, even when typedefs are used to help make the definitions more readable. However, every C and C++ compiler supports a more clear and concise mechanism to declare function pointers: use typedef, but don’t store the pointer as part of the definition. Note that the only way this kind of typedef can actually be used is with a pointer – but that highlights the pointer-ness of it.

C and C++ syntax

// This declares 'F':
//    a function that accepts a 'char' and returns an 'int'.
// Note that the definition is elsewhere.
int F(char c);

// This defines 'Fn':
//    a type of function that accepts a 'char' and returns an 'int'.
typedef int Fn(char c);

// This defines 'fn':
//    a variable of type pointer-to-'Fn';
// and then assigns the address of 'F' to it.
Fn *fn = &F; // '&' is not required - but it highlights what is being done.

// This calls 'F' using 'fn';
// and then assigns the result to the variable 'a'.
int a = fn('A');

// This defines 'Call':
//    a function that accepts a pointer-to-'Fn';
//    calls that function;
//    and returns the result.
int Call(Fn *fn, char c) {
   return fn(c);
} // Call(fn, c)

// This calls function 'Call()', passing in 'F'.
// It then assigns the result to the variable 'call'.
int call = Call(&F, 'A'); // Again, '&' is not required

// LEGACY: Note that to maintain existing code bases,
// the above definition style can still be used first;
// then the original type can be defined in terms of it
// using the new style.

// This defines 'PFn':
//    a type of pointer-to-type-Fn.
typedef Fn *PFn;

// 'PFn' can be used wherever 'Fn *' can
PFn pfn = F;
int CallP(PFn fn, char c);

C++ specific syntax

These examples use the above definitions. In particular, note that the above definition for Fn can be used in pointer-to-member-function definitions. Also note that C++ defines the precedence of the two “pointer to member” operators (.* and ->*) as lower than ordinary member access operators (. and *) and the function call operator (()), thus to use them requires extra parentheses:

// This defines 'C':
//    a class with similar static and member functions;
// and then creates an instance called 'c'.
class C {
public:
   static int Static(char c);
   int Member(char c);
} c; // C

// This defines 'p':
//    a pointer to 'C';
// and assigns the address of 'c' to it
C *p = &c;

// This assigns a pointer-to-'Static' to 'fn'.
// Since there is no 'this', 'Fn' is the correct type;
// and 'fn' can be used as above.
fn = &C::Static;

// This defines 'm':
//    a pointer-to-member-of-'C' with type 'Fn';
// and assigns the address of 'C::Member' to it.
// You can read it right-to-left like all pointers:
// "'m' is a pointer to member of class 'C' of type 'Fn'."
Fn C::*m = &C::Member;

// This uses 'm' to call 'Member' in 'c';
// assigning the result to 'cA'.
int cA = (c.*m)('A');

// This uses 'm' to call 'Member' in 'p';
// assigning the result to 'pA'.
int pA = (p->*m)('A');

// This defines 'Ref':
//    a function that accepts a reference-to-'C';
//    a pointer-to-member-of-'C' of type 'Fn';
//    and a 'char';
// calls the function, and returns the result.
int Ref(C &r, Fn C::*m, char c) {
   return (r.*m)(c);
} // Ref(r, m, c)

// This defines 'Ptr':
//    a function that accepts a pointer-to-'C';
//    a pointer-to-member-of-'C' of type 'Fn';
//    and a 'char';
// calls the function and returns the result
int Ptr(C *p, Fn C::*m, char c) {
   return (p->*m)(c);
} // Ptr(p, m, c)

// LEGACY: Note that to maintain existing code bases, the above definition
// style can still be used first;
// then the original type can be defined in terms of it using the new style.

// This defines 'FnC':
//    a type of pointer-to-member-of-class-'C' of type 'Fn'.
typedef Fn C::*FnC;

// 'FnC' can be used wherever 'Fn C::*' can.
FnC fnC = &C::Member;
int RefP(C &p, FnC m, char c);

Leave a comment