Cleaner C++: Wrapping Function Pointers

Hey everyone. This post is less about a specific part of Halogen, and more about C++ in general. I aim to cover what a function pointer is, how to use them syntactically in C++, and finally I’ll provide and explain a useful class for wrapping their functionality into a cleaner API.

So, to start, here’s a  basic function in C++:

void PrintNum(int num)
{
    std::cout << "Number: " << num << std::endl;
}

As you can see, this function simply prints a number to the standard output. Calling this function directly looks something like PrintNum(15);.

C++ has a feature called function pointers, which allows us to hold functions as variables. This can come in handy when you want modular functionality without writing lots of different classes. For example, Halogen uses function pointers in its task manager, where each Task class holds a pointer to the function it should execute asynchronously. Another great advantage is that generally, a function pointer is only sizeof(void*);, which makes them pretty lightweight. Here’s what a pointer to the above function would look like:

void(*myFunctionPointer)(int) = PrintNum;
...
myFunctionPointer(5);

Yikes. Though useful, function pointers are definitely not pretty, syntactically speaking. The identifier is buried in the middle, which can make it a bit hard to notice sometimes. Most of the time you can circumvent this with a typedef:

typedef void(*PrintFunc)(int);
...
PrintFunc myFunctionPointer = PrintNum;

This is much cleaner, but still requires an ugly typedef every time you want to point to a function. What if we could do away with that typedef, but still have readable function pointers in our code? Sounds like a job for templates! C++11 provides std::function, a heavyweight implementation of a function pointer wrapper with tons of functionality, but it’s not particularly fast and takes up considerably more space than necessary, based on my testing. So, I decided to write my own simplified version that provides only what’s necessary for Halogen.

One naive approach to this might look something like this:

template <class ReturnType, class... Args>
class Function
{
//impl goes here
};

...

Function<void, int> myFunction = PrintNum;
myFunction.execute(5);

Already, we’ve come a long way. But this is only scratching the surface! One of the first and easiest things we can do to clean up the code even further is turn Function into a functor (go figure) by overloading the () operator:

ReturnType operator() (Args... argv)
{
    m_InternalPointer(argv);
}

...

Function<void, int> myFunction = PrintNum;
myFunction(5);

This cleans up the call-site of the function pointer, but initializing the class is still a bit ambiguous. Does it take int as an argument, or is int the return type? If int is the return type, what is the void for? Most people would probably quickly figure these questions out, but what if we could make our template parameters look more like a function declaration in the first place? Partial template specialization can help us.

template <class>
class Function;

template <class ReturnType, class... Args>
class Function<ReturnType(Args...)>
{
// impl here
}

Knowing how to properly use templates and partial template specialization is key here. Here’s what’s happening: first, we declare (but do not define) a class called Function, which takes 1 template parameter – which we’ll explain shortly. Next, we specialize by defining a version of Function that takes a ReturnType template, and a list of arguments in a variadic template. Then, we define how those 2 template arguments combine into the 1 function argument of the original Function declaration: <ReturnType(Args...)> . What this means is that in the first declaration of Function, the unknown template is actually a function pointer defined by the second Function definition! With this, we now have clean initialization and call sites:

Function<void(int)> myFunction = PrintNum;
myFunction(5);

And for completeness, here’s the full code:

template <class>
class Function;

template <classReturnType, class... FunctionArgs>
class Function<ReturnType(FunctionArgs...)>
{
public:

    typedef ReturnType(*Pointer)(FunctionArgs...);

    // Non-explicit means we can build straight from a compatible function
    Function(Pointer func)
        : m_InternalPointer(func) {}

    // Override the () operator to turn it into a functor
    ReturnType operator() (FunctionArgs... argv)
    {
        returnm_InternalPointer(argv...);
    }

private:

    Pointer m_InternalPointer;
};

Member Function Pointers

Did I hear someone say member functions? Alright, you talked me into it. For my implementation, I decided that the member function wrapper would have to be its own separate class, since pointers to member functions are rather different, and bring along their own complications. First, let’s see what a member function pointer looks like in its barest form:

struct Obj
{
    void Function(int arg);
}
...
Obj myObj;
void(Obj::*myFunctionPtr)(int) = &Obj::Function;
...
(myObj.*myFunctionPointer)(5);

Not even the call sites are pretty with member function pointers. Note that in the type, we have to prefix the regular function pointer identifier with Obj:: in order to tell the compiler what class of which the function is a member. We also have to dereference the function before we can call it, and we have to call it on an instance of the class where the function came from. Since most of these are fairly trivial additions to our existing class, there’s not much to explain, so here’s the code:

template<class Obj, class>
class MemberFunction;

template <class Obj, class ReturnType, class... FunctionArgs>
class MemberFunction<Obj, ReturnType(FunctionArgs...)>
{
public:

    typedef ReturnType(Obj::*Pointer)(FunctionArgs...);

    MemberFunction() = delete;

    MemberFunction(Obj* obj, Pointer func)
        : m_ObjHandle(obj), m_InternalPointer(func)
    {}

    ReturnType operator() (FunctionArgs... argv)
    {
        return (m_ObjHandle->*m_InternalPointer)(argv...);
    }
private:

    Obj* m_ObjHandle;
    Pointer m_InternalPointer;
};

I hope this helped you understand function pointers better, and with any luck these 2 classes can provide some help to someone. Thanks for reading!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s