C++ and function pointers

Function pointers (sometimes known as delegates) are powefull tool in many languages. They offer the users the flexibility and allow you to call functions from almost everywhere. I have been using them for a trigger system in a state machine.

After C++11 has been released, there are several ways you can create your own delegate without the use or 3rd party libraries (like boost or FastDelegate). You can also bind lambda expressions as function pointers, but that is not our current target.

Possible solutions

Since I am using function pointers inside state machine, I have been using the same trigger function definition
void()
That is not very practical in some cases, where some arguments needs to be passed (return value is usually not important for me). I have been looking for a solution, that can store function pointer with any number of arguments inside one typed variable. I put the question on SO C++ std::function variable with varying arguments and wait. There were some solutions, including the accepted one with casting function pointer to
void(*)
It is a legal operation as long as you can cast it back to the correct form. I have used a wrapper around this function pointer and to know how to cast it back, templates have been used (in this case C++11 variadic templates to allow any number of arguments). This solution works, but has many limitations. Safety is probably the biggest one.

//========================================================================================


template <typename R>
class MultiFunc
{
	typedef void(*function_t)();

	function_t m_func;
     public:

	template <typename ...A1>
	MultiFunc<R>(R(*f)(A1...))
	{
		m_func = (void(*)())f;
	}

	template <typename ...A1>
	MultiFunc<R> operator =(R(*f)(A1...))
	{
		m_func = (void(*)())f;
		return *this;
	}


	template <typename ...A1>
	R operator()(A1... a1) const
	{
		R(*f)(A1...) = (R(*)(A1...))(m_func);
		return (*f)(a1...);

	}

};
//========================================================================================

Now, I can have pointer to any function inside the same class. The input arguments are deducted in compile time from template. This solution, however, has one PROBLEM. You MUST call the function with exact argument types, there is no auto-conversion. If your function expect double, you have to call it with double and not with int.


double test(double a, double b) {
	return a + b;
}

MultiFunc<double> fptr5 = &test;
fptr5(1.0, 1.0); //OK
fptr5(1, 1); //Compiles OK, but runtime horrible Error and crash - 1 is not double but int !!!!

It must be taken in mind, otherwise you will be really desperate.

Ok... this was the solution for a classic function pointers. But what about "class member function" pointers? They cannot be called the usuall way, we need the pointer to the parent objects and also for the function itself. Well, I have added second variable inside MultiFunc class and second template type to know the parent type.


//========================================================================================

//X = parent class
//R = ReturnType
template <typename X, typename R>
class MultiFunc2
{
	typedef void(X::*function_t)();

	function_t m_func;
	X * m_obj;
     public:

	template <typename ...A1>
	MultiFunc2<X, R>(X * obj, R(X::*f)(A1...))
	{
		m_func = (void(X::*)())(f);
		m_obj = obj;
	}


	template <typename ...A1>
	MultiFunc2<X, R> operator =(R(X::*f)(A1...))
	{
		m_func = (void(X::*)())(f);
		return *this;
	}


	template <typename ...A1>
	R operator()(A1... a1) const
	{
		R(X::*fn_ptr)(A1...) = (R(X::*)(A1...))(m_func);

		return ((*m_obj).*fn_ptr)(a1...);
	}


};//--------------------------------------------------

Now you can call member function pointers. This looks messed up and you have lost type-safety during compilation. You can add run time type-safety using

typeid
but it is now very convinient and it will be slow. This solution is not intended to be used in any production code!!! This is just one of the solutions, I came across the way during finding the "real" solution.

Final solution

At the end, I have came across using
std::tuple
and bind parametrs to function via this. Function is later called with parameters from tupple. It can even be unrolled at compile time. Solution of this problem has been presented in this SO question: “unpacking” a tuple to call a matching function pointer.
template<int... Is> struct seq{};

template<int N, int... Is>
struct gen_seq2 : gen_seq2 < N - 1, N - 1, Is... > {};

template<int ...S>
struct gen_seq2 < 0, S... > { typedef seq<S...> type; };

class IFunction
{
    public:
      virtual void call() = 0;
};


template <typename R, typename ...ARGS>
class MyFunction : public IFunction
{
    public:
      R retVal;
      std::tuple<ARGS...> args;

      std::function<R(ARGS...)> f;
	
      virtual void call()
      {		
         this->retVal = this->callFunc(typename gen_seq2<sizeof...(ARGS)>::type());
      };
	
      template<int ...S>
      R callFunc(seq<S...>)
      {
	 return f(std::get<S>(args) ...);
      }
};

Benchmark

I have created a simple benchamrk to compare various function calls using raw pointers, other presented solutions and also std::function and std::bind.

Sample code to show how function were created and called follows:

//Called functions
//--------------------------------------------------
class Foo
{
     public:
	double test(double x, double y)
	{
		return x + y;
	};
};

double test(double a, double b) {
	return a + b;
}
//--------------------------------------------------

//some defines and typedefs
typedef double(*function_t)(double, double);
typedef double(Foo::*Foo_function_t)(double, double);
#define CALL_MEMBER_FN(object, ptrToMember)  ((object).*(ptrToMember))
#define RND (static_cast <double> (rand()) / static_cast <double> (RAND_MAX))

Foo foo;

// code samples to call functions
//--- Test 0 ------------------------------------------------------------------------------------
test(RND, RND);

//--- Test 1 ------------------------------------------------------------------------------------
function_t fptr = &test;
fptr(RND, RND);

//--- Test 2 ------------------------------------------------------------------------------------
std::function<double(double, double)> fptr2 = &test;
fptr2(RND, RND);

//--- Test 3 ------------------------------------------------------------------------------------
std::function<double(double, double)> fptr3 = std::bind(&test, placeholders::_1, placeholders::_2);
fptr3(RND, RND);

//--- Test 4 ------------------------------------------------------------------------------------
double x = 0;
double y = 0;
std::function<double()> fptr4 = std::bind(&test, cref(x), cref(y));

x = RND;
y = RND;
fptr4();

//--- Test 5 ------------------------------------------------------------------------------------
MultiFunc<double> fptr5 = &test;
fptr5(RND, RND);


//--- Test 6 ------------------------------------------------------------------------------------
foo.test(RND, RND);

//--- Test 7 ------------------------------------------------------------------------------------
Foo_function_t fptr6 = &Foo::test;
CALL_MEMBER_FN(foo, fptr6)(RND, RND);
	
//--- Test 8 ------------------------------------------------------------------------------------
std::function<double(double, double)> fptr7 = std::bind(&Foo::test, foo, placeholders::_1, placeholders::_2);
fptr7(RND, RND);

//--- Test 9 ------------------------------------------------------------------------------------
x = 0;
y = 0;
std::function<double()> fptr8 = std::bind(&Foo::test, foo, cref(x), cref(y));
fptr8();

//--- Test 10 ------------------------------------------------------------------------------------
MultiFunc2<Foo, double> fptr9(&foo, &Foo::test);
fptr9(RND, RND);

//--- Test 11 ------------------------------------------------------------------------------------
MultiFunc2<Foo, double> fptr10(&foo, &Foo::test);
std::function<double()> tmp = std::bind(fptr10, RND, RND);
tmp();


//--- Test 12 ------------------------------------------------------------------------------------
MyFunction<double, double, double> * fptr12 = new MyFunction<double, double, double>();
fptr12->f = &test;
	
fptr12->args = std::make_tuple(RND, RND); //set arguments via tupple
fptr12->call(); //call the function with binded args
fptr12->retVal; //return computed value



Every test has been iterated 10,000,000 times in Visual Studio 2013 (Microsoft C++ compiler), Release Mode (without debugger). Every test looks like following pseudo code:

RESET_ARRAY;
START;
for (int i = 0; i < COUNT; i++)
{		
  res[i % ARR_SIZE] += call_function_with_result;
}
END;
PRINT_TIME;

ANTI_OPTIMALIZATION; //iterate array and print sum - this is here to prevent compiler to "remove" function calls because they are unused

Results

test #Time [ms]
0374
1405
2436
3436
4421
5421
6374
7390
8452
9436
10421
111294
12445

As you can see from the result, there is an overhead in using function pointers. But on the other hand, overhead between std::function and casting of the raw pointer is negligible.

Special case in test #11. This is solution, where you can bind parametrs outside the function and inside you will always have the same call. This is solution, that can be used inside universal state machine. Inside, you have no knowledge about the function, parameters are bind outside. On the other hand, the overhead is big and it depends on situation, whether to use this soution or not. Solution similar to #11 is #12. That is our winner and our target solution. It has the speed of other methods (well, almost) and availability to set arguments before the actuall call.

Martin Prantl 2015