Martin Copes
Martin Copes

Reputation: 981

Pass a function with the arguments in C++

Is it possible in C++ to pass a function WITH it's parameters as an argument? I don't want to just pass the function name. I want to pass the function name with the parameters included. Note that I don't know how many parameters the function recieves nor the types. That is why I am not adding them as extra parameters. I just want a reference to a function call that I can execute on demand. For example:

CallFunction(f(param1, param2));

But f is not evaluated, I just get a reference to the function call and I can evaluate it when I want, for example:

CallFunction(f){
 //do something and then in one moment do:
 f; // and this calls f(param1, param2)
}

Any way in which this can be achieved in C++? Thank you in advance.

Upvotes: 1

Views: 98

Answers (3)

Jts
Jts

Reputation: 3527

  1. Accept any callable function as the first argument
  2. Use variadic templates and accept any number of arguments of any type.
  3. Use perfect forwarding.

http://ideone.com/z2kLUx

int HelloWorld(string var1, const char* var2)
{
    cout << var1 << " " << var2 << endl;
    return 5;
}

template<typename Fn, typename... Args>
auto CallFunction(Fn fn, Args&&... args)
{
     return fn(forward<Args>(args)...);
}

int main()
{
    cout << "Value returned: " << CallFunction(HelloWorld,"Hello","World");
    return 0;
}

This is the simple version, which has a drawback: you have to specify a function that returns non-void, but this can be fixed easily. The easy fix is create another CallFunction that returns void with a different name, or use std::enable_if.

http://ideone.com/El3WUi

template<typename Fn, typename... Args>
auto CallFunction(Fn fn, Args&&... args) -> typename enable_if<is_same<void, decay_t<decltype(fn(forward<Args>(args)...))>>::value, decltype(fn(forward<Args>(args)...))>::type
{
    fn(forward<Args>(args)...);
}

template<typename Fn, typename... Args>
auto CallFunction(Fn fn, Args&&... args) -> typename enable_if<!is_same<void, decay_t<decltype(fn(forward<Args>(args)...))>>::value, decltype(fn(forward<Args>(args)...))>::type
{
    return fn(forward<Args>(args)...);
}

Upvotes: 0

Jozef Leg&#233;ny
Jozef Leg&#233;ny

Reputation: 1167

You have several options.

One is to create a wrapper closure that will encapsulate the call to your function. This has the advantage that you can transform the output of your function if necessary.

Doing a simple wrapper is identical to using the std::bind as others have mentioned before.

#include <functional>
#include <iostream>

void CallFunction(std::function<void(void)> f) {
    f();
}

int main() {
    auto closure = [](int a, int b) {
        std::cout << a << ", " << b << std::endl;
    };

    auto wrapper = [closure]() {
        closure(1, 2);
    };

    CallFunction(wrapper);

    CallFunction(std::bind(closure, 3, 4));

    return 0;
}

Upvotes: 0

aschepler
aschepler

Reputation: 72463

A complete example of using std::bind:

#include <iostream>
#include <functional>

void f(int p1, int p2) {
    std::cout << "In f(" << p1 << "," << p2 << ")\n";
}

template <typename F>
void CallFunction(F f)
{
    std::cout << "Start CallFunction\n";
    f();
    std::cout << "End CallFunction\n";
}

int main()
{
    CallFunction(std::bind(f, 2, 3));
}

Upvotes: 3

Related Questions