-1

I have a question continuing the post Function passed as template argument. In the provided code:

#include <iostream>

void add1(int &v)
{
  v+=1;
}

void add2(int &v)
{
  v+=2;
}

template <void (*T)(int &)>
void doOperation()
{
  int temp=0;
  T(temp);
  std::cout << "Result is " << temp << std::endl;
}

int main()
{
  doOperation<add1>();
  doOperation<add2>();
}

what about a third function which has a different parameter set layout, e.g.

double add3(double v1, double v2)
{
return v1+v2;
}

If this is not achievable using template at all, how do we pass an arbitrary function to another function? And how do we handle the parameter set with all kinds of possibilities? I know python may be able to do it by passing a tuple (kwargs**), but not sure about C/C++.

Community
  • 1
  • 1
Hailiang Zhang
  • 17,604
  • 23
  • 71
  • 117

2 Answers2

1

One form of passing a generic function to be called is a callable templated type:

#include <functional>
#include <iostream>

template<typename F>
void callFoo(F f) {
   f();
}

int main() {
   callFoo(std::bind([](int a, int b) {std::cout << a << ' ' << b;}, 5, 6));
}

callFoo takes a callable type, F, and calls it. Around this call, you can, for example, do timer work to time the function. In main, it's called with a lambda that has two parameters and the values given to those parameters bound to it. callFoo can then call it without storing the arguments. This is very similar to taking a parameter with the type std::function<void()>.

If, however, you don't want to use std::bind, you can pass in the arguments separately with a couple changes:

template<typename F, typename... Args>
void callFoo(F f, Args... args) { //ignoring perfect forwarding
    f(args...);
}

int main() {
    callFoo(/*lambda*/, 5, 6);
}

In these cases, passing void functions makes sense. Indeed, return values can be used as parameters and passed in with std::ref. If you plan on returning what the function returns, you'll have to handle the special case of the return type being void, as you can't assign to a void variable and return that. At this point, it's easier to direct you to my previous question on the matter. My use case for it turned out to be moot, but the solution works great for other uses.

Community
  • 1
  • 1
chris
  • 60,560
  • 13
  • 143
  • 205
  • Great answer! One more question is that "callFoo(std::bind([](int a, int b) {std::cout << a << ' ' << b;}, 5, 6));" has a very complicated form, where the parameter list (or definitions) need to be provided 3 times. I have a very very long parameter list, and not sure whether it has a more concise format. – Hailiang Zhang Feb 06 '13 at 19:33
  • My mistake. The second code box answered my question in the above comment. Howvever, it seems that it only works for gcc/4.5 and above, and do you think there is a solution in gcc/4.1? – Hailiang Zhang Feb 06 '13 at 20:04
  • @HailiangZhang, Yes, both lambdas and variadic templates are C++11. In 4.1, you might be able to use Boost to achieve the same effect. Phoenix for lambdas, but I'm not sure which library has variadic templates. – chris Feb 06 '13 at 20:38
0

This could possibly lead you closer to what you want:

#include <iostream>

void add1(int &v)
{
    v+=1;
}

double add2(double v1, double v2)
{
    return v1 + v2;
}

// 1 param version
template< class aRetType, class tP1 >
aRetType doOperation( aRetType (*aFunction)( tP1 aP1 ), tP1 valP1  )
{
    return aFunction( valP1 );
}

// 2 param version
template< class aRetType, class tP1, class tP2 >
aRetType doOperation( aRetType (*aFunction)( tP1 aP1, tP2 aP2 ), tP1 valP1, tP2 valP2 )
{
    return aFunction( valP1, valP2 );
}

// 3 param version and up is not given, but you get the trick.

int main()
{
    int iTemp = 8;
    doOperation< void, int& >( add1, iTemp );
    std::cout << "Result is " << iTemp << std::endl;

    double iResult;
    iResult = doOperation< double, double, double >( add2, 2.2, 8.8);
    std::cout << "Result is " << iResult << std::endl;
}
Izhaki
  • 23,372
  • 9
  • 69
  • 107