If you have read Function pointers, you may be wondering about how you might use function pointers to call the member functions of structs and classes. And you would be right to wonder. Regular function pointers will not work. What we need for this task is member function pointers, aka pointers to members functions. Strictly speaking, this only relates to non-static member functions, since static member functions do work with regular function pointers.
Pointers to member functions are quite strange beasts which are only superficially similar to regular function pointers. I think they are one of the nifty cool features of C++, but sadly they seem to be not well understood by a lot of C++ developers.
This article builds on the previous article on function pointers, and aims to explain member function pointers a little, and how they can be used to integrate C++ code with C callback functions.
How member functions are implemented
To understand member function pointers and why they are so different from regular function pointers, it may help to understand a little bit about how classes are actually implemented by the compiler. Suppose we have some code that looks like this:
// Simple class with a few member functions.
class Thing
{
public:
// Constructor.
Thing(int offset); // : m_offset{offset} {}
// Non-static member functions.
int add(int a, int b); // { return a + b + m_offset; }
int mul(int a, int b); // { return a * b + m_offset; }
// Static member function.
static int div(int a, int b); // { return a / b; }
private:
int m_offset;
};
Thing::Thing(int offset)
: m_offset{offset}
{
}
int Thing::add(int a, int b)
{
return a + b + m_offset;
}
int Thing::mul(int a, int b)
{
return a * b + m_offset;
}
int Thing::div(int a, int b)
{
return a / b;
}
// Using the class.
int test()
{
Thing t{12};
return t.add(13, 14);
}
I could have inlined the member functions inside the class to make the code shorter (see comments), but wanted it to closely resemble the C that follows. The code is implemented (more or less) by the compiler as if you had written C something like this:
// Simple class with a few associated functions.
// Not official C as no typedef 'tag' malarky here.
struct Thing
{
// This throws away the notion of 'private'
int m_offset;
};
void Thing_Thing(Thing* this, int offset)
{
this->m_offset = offset;
}
int Thing_add(Thing* this, int a, int b)
{
return a + b + this->m_offset;
}
int Thing_mul(Thing* this, int a, int b)
{
return a * b + this->m_offset;
}
int Thing_div(int a, int b)
{
return a / b;
}
int test()
{
Thing t;
Thing_Thing(&t, 12);
return Thing_add(&t, 13, 14);
}
The compiler automatically inserts an additional parameter called this which holds a pointer to the instance of the object on which the function is called. That is to say, my_thing.add(13, 19) would effectively be implemented as Thing_add(&my_thing, 13, 19). It’s more complicated when there are virtual functions or multiple inheritance, but that’s the basic idea. [Interestingly, this means you can call a member function without an actual object: a null pointer is sufficient – the compiler doesn’t care as long as the pointer has the correct type. this would be nullptr or, worse, uninitialised. Don’t do this on purpose. :)]. this is a keyword in C++, so the “as if” code won’t actually compile unless you replace it with another name.
The made up “as if” function names are not that far from reality. The compiler will invent a name each the function based on the namespace, the class name, the parameter types, and other factors. This is what we know as name-mangling. To you it looks like Thing::add; to the linker it looks like _ZN5Thing3addEii (from GCC), or something equally ugly. There is no ambiguity. Incidentally, this is also how function overloading works: different signatures lead to different names. Debuggers usually do a good job of de-mangling the names so you don’t have to scratch your head too much.
So, in principle, a member function pointer is just like a regular function pointer. The member function pointer for Thing::add() can be imagined to hold the address of Thing_add(). But unfortunately this is not enough information to make the call work.
- The key thing to note is that when we use the member function pointer we will need somehow to supply a value for the this parameter in addition to values for the other parameters to the function. This is very different from plain function pointers.
- And it gets more complicated when virtual functions and multiple inheritance are involved. I thought it was quite something when I discovered that a pointer to a virtual member function will do the right thing.
- I don’t know this, but the compiler will presumably need to know which class the function pointer relates to, Thing in this case, so that functions of other classes with similar signatures can be rejected as being of different types (I’m glossing over the type of this here). I captured the class name in the “as if” function names, but I imagine the compiler wants a link to some detailed type info or something.
- Note that a pointer to a member function of some class is also usable with matching member functions of derived classes.
In truth, the way member function pointers are actually represented internally is implementation defined. That means each compiler can do whatever it wants, so long as it works. In many cases, they may actually be simple function pointers as suggested above. Or they might be structs with several data members containing all the necessary information, or something completely different. They are black boxes that we will soon learn how to use, but shouldn’t look inside. The sizes of member function pointers can even be different for different classes, which was an unpleasant surprise when I found out. It’s really best not to think of them as holding meaningful information or function addresses at all. Just treat them as opaque gizmos that you can somehow use to invoke a member function of a particular object.
Note that the static member function Thing::div() does not have a this parameter. It doesn’t need one because static functions aren’t tied to any particular instance of a class, but can be called on any object, or even without any object. If you did want to give a static function access to a particular object, you’d have to add a Thing* parameter of your own. For this reason, static member functions can be used with regular function pointers.
Aliases for member function pointer types
Following on from the discussion of signatures for regular function pointers here, the signature of a member function pointer is very similar, but includes the type of the class or struct as well. Suppose we have the following class:
class Gadget
{
public:
void foo(int, double);
void bar(int, double);
};
In this code Gadget::foo() and Gadget::bar() have the same signature, and it is void (Gadget::*)(int, double). This is basically identical to the signature for a free function which takes int and double arguments and returns void, but also includes the name of the class and the scope resolution operator before the star. Declaring variables of member function pointers types is straightforward, but ugly. Invoking them to call functions is worse:
// Gadget::foo() is a function.
void Gadget::foo(int arg1, double arg2)
{
// ...
}
// mem_fun_ptr is a member function pointer.
void (Gadget::*mem_fun_ptr)(int, double);
// mem_fun_ptr now hold the "address" of Gadget::foo().
mem_fun_ptr = &Gadget::foo;
Gadget gadget;
// Calls Gadget::foo(). See below.
(gadget.*mem_fun_ptr)(1, 2);
We can declare member function pointer types (that is, aliases for the function signature) in much the same ways as for regular function pointers.
Typedef
// Direct variable declaration - avoid!!
// mem_fun_ptr is a variable: it is a
// pointer to member function of type
// void (Gadget::*)(int, double).
void (Gadget::*mem_fun_ptr)(int, double);
// C style type alias - avoid.
// OldMemFunPtr is an alias for the pointer to
// member function type void (Gadget::*)(int, double).
typedef void (Gadget::*OldMemFunPtr)(int, double);
Type alias (C++11)
// C++11 style type alias - preferred.
// NewMemFunPtr is equivalent to OldMemFunPtr.
using NewMemFunPtr = void (Gadget::*)(int, double);
Trailing return type (C++11)
// C++11 style type alias - trailing return type.
// NewMemFunPtr is equivalent to OldMemFunPtr.
using TrailingMemFunPtr = auto (Gadget::*)(int, double) -> void;
As with regular function pointers, member function pointers can appear in the same places as other data types: as local variables, member variables, in containers, and so on. Aside from calling functions, compared for equality (or non-equality), but few other operators make any sense with them, including other relational operators, and probably won’t compile.
Using a member function pointer variable
So far we have covered what member function pointers are and how to alias them with simple names to hide the rather unpleasant syntax, but we haven’t actually used them for much function pointing. This is very simple: you just assign the function pointer with address of a function, or the value of another function pointer, and then you use the function pointer to call the function of whose address it currently holds:
class Maths
{
void Maths::square(int x)
{ std::cout << x << " squared is " << (x*x); }
void Maths::cube(int x)
{ std::cout << x << " cubed is " << (x*x*x); }
};
// Create an alias for the function pointer.
using MemFuncPtr = void (Maths::*)(int);
int test()
{
Maths maths;
MemFuncPtr ptr = &Maths::cube;
// Read as maths.function(3) where
// "function" is "deference ptr".
(maths.*ptr)(3); // Calls cube(3)
// Call with a Maths object
ptr = &Maths::square; // & mandatory
(maths.*ptr)(5); // Calls square(5)
MemFuncPtr ptr2 = ptr;
(maths.*ptr2)(5); // Calls square(6)
// Call with a pointer to a Maths object
Maths* maths_ptr = &maths;
// Read as maths_ptr->function(6) where
// "function" is "deference ptr".
(maths_ptr->*ptr2)(6); // Calls square(6)
}
Going through some of this in order:
- MemFuncPtr is an alias for member function pointers with the signature void (Maths::*)(int).
- We create an instance maths of the class Maths and take its address in a pointer.
- We declare a member function pointer as take the address of Maths::cube. Note that the & is always required when taking the address of member functions, which is different from non-member functions.
- Next comes one of the two least well understood of all C++ operators: .*. It really isn’t all that bad, but could stand some explanation:
- Remember when I said earlier that we need a way to pass the this pointer to the imaginary function implementation we are calling? That is precisely what this operator does.
- I find it helpful to split the operator into two parts in my mind: the star just means “dereference the function pointer to give us a function”. This is analogous to the way dereferencing a data pointer gives us data. [Actually a reference to data, but ignore that.]
- And then the other part of the operator, the dot, has exactly the same meaning it would have if we were calling a member function directly rather than through a function pointer.
- The parentheses around (maths.*ptr) are required for reasons of operator precedence. This is unfortunate, as it makes the function call look weird and ugly. Shame, but that’s how it is.
- Later we take the address of the object maths and then use the data pointer maths_ptr to invoke a member function pointer. This uses the other scary member access operator: ->*. As before, it may be simpler to interpret this in two parts. As before, the parentheses are required.
Note that like regular function pointers, member function pointers have distinct types (including the name of the class), and cannot to assigned to pointers of other types (except that member of derived classes can be used with pointers to members of base classes). I’m not sure I would even cast them: what meaning would the result have?
And that’s it. Member function pointers are actually kind of cool, and not nearly as arcane as is commonly believed. There are some complexities in their implementation for virtual functions and in the presence of multiple inheritance, but this has no impact on how they are used in your code.
Interfacing member functions with C
Sadly it is not possible to pass member function pointers to C APIs. C simply has no concept of what they are, and can only handle pointers to free functions (and static member functions). There are a few simple things we can do interface our C++ with C.
C to member function callbacks – version 1
So… you are writing your application in C++ but have to pass a callback to some C API. You would really like it if it would call a member function of a particular object. There is a simple way to do this: create an intermediate function to act as a trampoline.
using Callback = void (*)(int x, void* context);
void c_api_set_callback(Callback cb, void* context);
struct Foo
{
Foo();
void alpha(int x);
void beta(int x);
void gamma(int x);
void delta(int x);
};
// Trampoline signature matches what the C expects.
void call_foo_alpha(int x, void* context)
{
// We know this is a safe cast.
// Translate into a member function call.
auto foo = static_cast<Foo*>(context);
foo->alpha(x);
}
// Constructor sets up the callback.
Foo::Foo()
{
// Pass pointer to this object as context.
c_api_set_callback(call_foo_alpha, this);
}
It is very simple and it works very well indeed. A problem with this approach is that we would need to write individual trampoline functions for each member we wanted to use a callback function, even though they all have the same signature. This is not ideal.
Note also that the “trampoline” function needs to be able to see the members of the class it needs to use. They must be public, or it must be a friend. Alternatively, the trampoline could be a static member function of the class.
C to member function callbacks – version 2
There is a slightly better way, which makes use of a member function pointer (that’s why we’re here, after all) and allows us to write only one trampoline function for all the matching members.
The thing about member function pointers is that they are known at compile time. The type is known at compile time, obviously, but so are the specific values (however they might be implemented) corresponding to each particular member function. This means that we can use a member function pointer as a non-type template parameter. Like this:
// Alias for a member function pointer.
using MemFuncPtr = void (Foo::*)(int);
// The trampoline is now a template parameterised on
// a particular member function pointer.
template <MemFuncPtr mem_func_ptr>
void call_foo_member(int x, void* context)
{
// And use the member function pointer.
auto foo = static_cast<Foo*>(context);
(foo->*mem_func_ptr)(x);
}
Foo::Foo()
{
// Instantiate the template for several different members.
c_api_set_callback(call_foo_member<&Foo::alpha>, this);
c_api_set_callback(call_foo_member<&Foo::beta>, this);
c_api_set_callback(call_foo_member<&Foo::gamma>, this);
c_api_set_callback(call_foo_member<&Foo::delta>, this);
}
This is great, but remember that templates are code generators. By instantiating the template four times in Foo::Foo(), we generated four different versions of the code, which all differ just by which member function they call (you can verify this by copying the code into Compiler Explorer). Four virtually identical functions! Is it bloat? Well, the functions are very small. Is it justified? There isn’t really another way, and the template saved us from some repetitive and error prone typing. As always with templates, if you actually need the code they generate, you need it. You would have written it by hand anyway. Or you might find another way to get the same result.
C to member function callbacks – version 3
The little template trampoline is nice, but we can do a little better still. At the moment the trampoline is only useful for members of Foo. It would be really useful if we had a single implementation which could always be used, regardless of which class the target member function belonged to. I suspect you know what’s coming: we can convert Foo itself into a template parameter. Like this:
// Template the member function pointer type alias.
template <typename Class>
using MemFuncPtr = void (Class::*)(int);
// Add Class to the template trampoline.
template <typename Class, MemFuncPtr<Class> mem_func_ptr>
void call_member(int x, void* context)
{
auto obj = static_cast<Class*>(context);
(obj->*mem_func_ptr)(x);
}
Foo::Foo()
{
// Pass pointer to this object as context.
c_api_set_callback(call_member<Foo, &Foo::alpha>, this);
}
struct Bar
{
Bar();
void something(int x);
};
Bar::Bar()
{
// Assume Bar::something has the same signature.
c_api_set_callback(call_member<Bar, &Bar::something>, this);
}
I’m quite pleased with that. Now we have a simple bit of template goodness which we can use every time we need to convert some member function with signature void (Class::*)(int) into a free function with signature void (*)(int, void*) which can be used as a callback with C libraries. It’s just a few lines of code but can do so much for us. We could however get it to do even more for us… We could generalise the return type and the number and types of the arguments. I’ll leave these for another day.
C to member function callbacks – version 4
Finally, the invocation of call_member<> is now quite long, and includes the name of the class twice: once by itself, and once as part of the member function pointer used as a non-type template parameter. We can shorten this a little by using an auto template parameter for the member function pointer. The following code adds a thin wrapper for the c_api_set_callback() function.
// C-style API taking a plain function pointer and
// void pointer context
using Callback = void (*)(int x, void* context);
void c_api_set_callback(Callback cb, void* context);
// Template the member function pointer type alias.
template <typename Class>
using MemFuncPtr = void (Class::*)(int);
// Trampoline to redirect C callback to a member function
template <typename Class, MemFuncPtr<Class> mem_func_ptr>
void call_member(int x, void* context)
{
auto obj = static_cast<Class*>(context);
(obj->*mem_func_ptr)(x);
}
// Wrapper for c_api_set_callback which means we don't have
// to pass the name of the class twice.
template <auto MemFuncPtr, typename Class>
void new_c_api_set_callback(Class* context)
{
// This is the code we called directly in Foo::Foo() before.
c_api_set_callback(call_member<Class, MemFuncPtr>, context);
}
struct Foo
{
Foo();
void alpha(int x);
void beta(int x);
void gamma(int x);
void delta(int x);
};
Foo::Foo()
{
// Now we just pass the the member function pointer and
// write the name of the class only once.
new_c_api_set_callback<&Foo::alpha>(this);
new_c_api_set_callback<&Foo::beta>(this);
new_c_api_set_callback<&Foo::gamma>(this);
}
The trick here is that the new template function takes a Class* argument instead of a void* argument. This is sufficient for the compiler to work out the type of class for which MemFuncPtr is a member function pointer. This happens at the point where we actually call the template function in Foo::Foo(). This all adds up to needing to pass one less template argument when setting the callback function. We don’t have to duplicate the name of the class, which is convenient. A call to new_c_api_set_callback() is now basically the same amount of typing as a call to c_api_set_callback() would be with a free function. I don’t regard brevity as a particularly important goal, but do value clarity. It’s hard not to like the simplicity of using this template.
Of course, using this method also adds up to generating a distinct version of new_c_api_set_callback() each time the function template is used. Is it a price worth paying? Once again, they are very small functions. In fact, in this example they optimised away completely. The compiler can do this easily because there are no tricksy function pointers which can’t be resolved. The template function is little more than a typesafe macro.
Conclusion
Pointers to member functions are a fascinating topic, and they are quite useful in some situations. I hope that this article managed to shed a little light on them and maybe demonstrate that they are not one of the dark arts, but actually relatively simple to understand.
It is common, especially in embedded code, to set callback functions using existing C APIs. I showed a simple way to interface your C++ classes with C callbacks, and then progressively generalised that method to make it more useful while avoiding the need to write a lot of repeated code.
In the next article, I will take some of these ideas and generalise the concept of callback functions to C++.


