Lei Mao bio photo

Lei Mao

Machine Learning, Artificial Intelligence, Computer Science.

Twitter Facebook LinkedIn GitHub   G. Scholar E-Mail RSS

Introduction

Function pointers are used to pass functions as arguments to other functions in C and C++. The usages are universal in C and C++.


In this blog post, I would like to briefly cover the usages of function pointers.

Examples

To define a function pointer, we have to declare its type. The type of function pointer has the following signature.

T0 (*f)(T1, T2, T3, ...)

where f is the function pointer, T0 is the function output type, T1, T2, T3, etc, represents the type for argument 1, argument 2, and argument 3, etc.


When calling the function using function pointer, (*f) is just equivalent to f because the function name is just the function address.

/* 
 * function_ptr.cpp
 */
#include <iostream>

template <typename T>
T add(T x, T y)
{
    return x + y;
}

template <typename T>
T minus(T x, T y)
{
    return x - y;
}

template <typename T>
T runBinaryFunction1(T x, T y, T (*f)(T, T))
{
    return (*f)(x, y);
}

template <typename T>
T runBinaryFunction2(T x, T y, T (*f)(T, T))
{
    return f(x, y);
}

int main()
{
    int x = 1;
    int y = 2;

    // fpAdd1 is a pointer for function which takes two inputs of type int and generates a output of type int
    int (*fpAdd1)(int, int) = &add<int>;
    // fpAdd1 is both function name and function address
    // The following two are equivalent
    std::cout << fpAdd1(x, y) << std::endl;
    std::cout << (*fpAdd1)(x, y) << std::endl;

    // add<int> is both function name and function address
    // So assigning &add<int> and add<int> are equivalent
    int (*fpAdd2)(int, int) = add<int>;
    std::cout << fpAdd2(x, y) << std::endl;
    std::cout << (*fpAdd2)(x, y) << std::endl;

    // Lambda expression also works
    // C does not have lambda expression
    int (*fpAdd3)(int, int) = [](int x, int y){return x + y;};
    std::cout << fpAdd3(x, 2) << std::endl;
    std::cout << (*fpAdd3)(x, 2) << std::endl;

    std::cout << runBinaryFunction1(x, y, fpAdd1) << std::endl;
    std::cout << runBinaryFunction1(x, y, *fpAdd1) << std::endl;
    std::cout << runBinaryFunction1(x, y, fpAdd2) << std::endl;
    std::cout << runBinaryFunction1(x, y, *fpAdd2) << std::endl;
    std::cout << runBinaryFunction1(x, y, fpAdd3) << std::endl;
    std::cout << runBinaryFunction1(x, y, *fpAdd3) << std::endl;

    std::cout << runBinaryFunction2(x, y, fpAdd1) << std::endl;
    std::cout << runBinaryFunction2(x, y, *fpAdd1) << std::endl;
    std::cout << runBinaryFunction2(x, y, fpAdd2) << std::endl;
    std::cout << runBinaryFunction2(x, y, *fpAdd2) << std::endl;
    std::cout << runBinaryFunction2(x, y, fpAdd3) << std::endl;
    std::cout << runBinaryFunction2(x, y, *fpAdd3) << std::endl;
}

To compile the program, please run the following command in the terminal.

$ g++ function_ptr.cpp -o function_ptr --std=c++11

Conclusions

Just memorize the function pointer signature and in most of the scenarios we would not make mistake.