2

I'm trying to pass a function, with a variable amount of parameters, as an argument to my function baz(). I'm not really sure how to go about doing this, but hopefully the example I've provided below shows my current thought-process.

#include <iostream>

template<typename... Ts>
void foo(void (*func)(Ts...)){
    func(Ts...);
}

void bar(int a, int b, std::string c){
    std::cout << "Test a: " << a << " Test b: " << b << " Test c: " << c << "\n";
}

void baz(int x, int y){
    std::cout << x+y << "\n";
}

int main()
{
    foo(&bar, 10, 20, "Hello!"); // Expected: "Test a: 10 Test b: 20 Test c: Hello!"
    foo(&baz, 2, 2);             // Expected: "4"
    // main.cpp:13:12: error: expected primary-expression before ‘...’ token

    return 0;
}

Any help is greatly appreciated.

2 Answers 2

2

You can pass a function type

#include <iostream>

template<typename Fn, typename ... Ts>
void foo( Fn&& func, Ts...ts ){
    func(ts...);
}

void bar(int a, int b, std::string c){
    std::cout << "Test a: " << a << " Test b: " << b << " Test c: " << c << "\n";
}

void baz(int x, int y){
    std::cout << x+y << "\n";
}

int main()
{
    foo(&bar, 10, 20, "Hello!"); // Expected: "Test a: 10 Test b: 20 Test c: Hello!"
    foo(&baz, 2, 2);             // Expected: "4"
    return 0;
}

Produces

Program stdout
Test a: 10 Test b: 20 Test c: Hello!
4

Godbolt: https://godbolt.org/z/beMv6a9a4

Sign up to request clarification or add additional context in comments.

Comments

2

Or you can slightly alter your approach and instead of passing the args yourself you can let a lambda capture them.

#include <iostream>
#include <type_traits>

// you can call foo with any invocable
// and even let it return the return value of that invocable.

template<typename fn_t>
auto foo(fn_t&& fn ) -> std::enable_if_t<std::is_invocable_v<fn_t>, decltype(fn())>
{
    std::cout << "foo begin\n";

    // slightly different handling for functions returning a void
    if constexpr (std::is_same_v<decltype(fn()), void>)
    {
        fn();
        std::cout << "foo end\n";
    }
    else
    {
        auto retval = fn();
        std::cout << "foo end\n";
        return retval;
    }
}

void bar(int a, int b, std::string c) 
{
    std::cout << "Test a: " << a << " Test b: " << b << " Test c: " << c << "\n";
}

int baz(int x, int y) 
{
    std::cout << x + y << "\n";
    return x + y;
}

int main()
{
    int x{ 2 };
    int y{ 3 };

    foo([&] { bar(x, y, "Hello!"); } );
    int sum = foo([&] { return baz(x,y); });

    return sum;
}

2 Comments

Very clever! Never thought of doing it that way.
I do more things like this, queing functions for an asynchronous executor (store them in packaged_task/promise/future). Callbacks, events you name it. Since the specialization then only is on return type not the arguments ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.