简体   繁体   中英

Variadic Functions With Variadic Type Parameters

I'm not sure if what I'm attempting is allowed by the standard, and may not even make sense to do, so by all means feel free to correct me.

I'm trying to pass a variable number of std::function objects to a function which accepts variadic arguments, and also accepts variadic template parameters. The template parameters specify the return types of the functions that are passed in. The signature of the function looks like so:

template <typename ... TRets>
void DoStuff(std::function<TRets...()> funcs...)

What I'm attempting to do is pass the return values from each of the funcs passed in to another function which accepts them in an expanded value form. Eg std::make_shared<TType>(funcs()...);

I'm using g++ 7.1.1 with the --std=c++17 flag which causes the compiler to seg fault. Obviously it shouldn't seg fault, but is there anything in the standard that says the above code is invalid? Or perhaps, is there a different syntax to accomplish this goal?

For completeness, here is a minimal working (for some definition of working) example:

#include <functional>
#include <memory>

class MyClass {
    public:
        int m_a;
        double m_b;
        MyClass(int a, double b) : m_a(a), m_b(b) {};
};


template <typename TReturn, typename ... Args>
std::shared_ptr<TReturn> CallFunctions(std::function<Args...()> funcs...) {
    // Do stuff here
    return std::make_shared<TReturn>(funcs()...);
}

int main(int argc, char * argv[]) {
    auto x = CallFunctions<MyClass, int, double>(
        [] () { return 5; },
        [] () { return 3.14; }
    );

    return 0;
}

Edited to show better intent of my original question

template <class R, class... Args>
std::shared_ptr<R> CallFunctions(std::function<Args()>... funcs) {
  return std::make_shared<R>( funcs()... );
}

this is C++11. It is inefficient.

template <class R, class... Args>
auto CallFunctions(Args&&... funcs) 
-> decltype(std::make_shared<R>( funcs()... ))
{
  return std::make_shared<R>( funcs()... );
}

which removes the needless type erasure and need to explicly pass the return types.

If you really really want to pass the return types:

template <class R, class... Args, class...Fs>
auto CallFunctions(Fs&&... funcs) 
-> decltype(std::make_shared<R>( static_cast<Args>(funcs())... ))
{
  return std::make_shared<R>( static_cast<Args>(funcs())... );
}

but I advise doing the 2nd solution above.

You can use C++17 std::apply to use std::invoke on each element of the parameter pack. Then you do not need std::function anymore, because std::invoke can only call callable objects and also you don't have to give explicit template arguments anymore (which is an anti-pattern anyway). As a bonus you can even retrieve the return values of the called functions.

#include <functional>
#include <iostream>
#include <memory>
#include <tuple>

template < typename... R, typename... Args >
auto CallFunctions(Args... funcs) {
    // Do stuff here
  return std::apply(
    [] (auto&&... x) {
      return std::make_tuple(std::make_shared<R>(std::invoke(x))...);
    }, std::make_tuple(funcs...)
    );
}

int main() {
    auto r = CallFunctions<int,double>(
        [] () { return 5; },
        [] () { return 3.14; }
    );

    std::cout << *std::get<0>(r) << ' ' << *std::get<1>(r) << '\n';

    return 0;
}

Live example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM