Reputation: 218
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. E.g. 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
Upvotes: 2
Views: 526
Reputation: 275385
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.
Upvotes: 3
Reputation: 10939
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;
}
Upvotes: 0