Reputation: 2983
I've seen a few questions here relating to a gcc bug with capturing variadic arguments in a lambda. See for example: Does lambda capture support variadic template arguments or Compiler bug, or non standard code? - Variadic template capture in lambda. I have the following contrived example of what I'm trying to do
#include <iostream>
#include <functional>
class TestVariadicLambda {
public:
template<typename... Args>
std::function<void()> getFunc(Args... args) {
return [=]{ printArgs(args...); };
}
template<typename T, typename... Args>
void printArgs(T value, Args... args) {
std::cout << value << ", ";
printArgs(args...);
}
void printArgs() {std::cout << "\n";}
};
In gcc 4.8.2 I get the following errors:
../src/TestVariadicLambda.h: In lambda function:
../src/TestVariadicLambda.h:9:25: error: parameter packs not expanded with ‘...’:
return [=]{ printArgs(args...); };
^
../src/TestVariadicLambda.h:9:25: note: ‘args’
../src/TestVariadicLambda.h:9:29: error: expansion pattern ‘args’ contains no argument packs
return [=]{ printArgs(args...); };
^
My question is how do I work around this since it won't work in gcc4.8
Upvotes: 4
Views: 2523
Reputation: 8315
Here's a workaround that works statically at least on GCC 4.8.0 (should also works on VisualStudio and clang as well)
(code mostly from this answer, what I did is wrapping argument in a tuple and then use the tuple as by-pass)
#include <iostream>
#include <functional>
#include <tuple>
using namespace std;
// ------------- UTILITY---------------
template<int...> struct index_tuple{};
template<int I, typename IndexTuple, typename... Types>
struct make_indexes_impl;
template<int I, int... Indexes, typename T, typename ... Types>
struct make_indexes_impl<I, index_tuple<Indexes...>, T, Types...>
{
typedef typename make_indexes_impl<I + 1, index_tuple<Indexes..., I>, Types...>::type type;
};
template<int I, int... Indexes>
struct make_indexes_impl<I, index_tuple<Indexes...> >
{
typedef index_tuple<Indexes...> type;
};
template<typename ... Types>
struct make_indexes : make_indexes_impl<0, index_tuple<>, Types...>
{};
template<class Ret, class... Args, int... Indexes >
Ret apply_helper( Ret (*pf)(Args...), index_tuple< Indexes... >, tuple<Args...>&& tup)
{
return pf( forward<Args>( get<Indexes>(tup))... );
}
template<class Ret, class ... Args>
Ret apply(Ret (*pf)(Args...), const tuple<Args...>& tup)
{
return apply_helper(pf, typename make_indexes<Args...>::type(), tuple<Args...>(tup));
}
template<class Ret, class ... Args>
Ret apply(Ret (*pf)(Args...), tuple<Args...>&& tup)
{
return apply_helper(pf, typename make_indexes<Args...>::type(), forward<tuple<Args...>>(tup));
}
/// ------------------- REAL CODE --------
void printArgs() {std::cout << "\n";}
template<typename T, typename... Args>
void printArgs(T value, Args... args) {
std::cout << value << ", ";
printArgs(args...);
}
template<typename... Args>
std::function<void()> getFunc(Args... args) {
std::tuple<Args...> tup(args...);
return [=]{ apply<void, Args...>(printArgs, tup); };
}
int main(){
auto f = getFunc<int,float,const char*>(4,5.4f,"hello");
f();
return 0;
}
Also why don't you use std::bind? Basically std::bind can create a lambda for your with parameters of your desire (just syntactic sugar, choose the most viable way for you:) )
Upvotes: 1
Reputation: 23510
The following will work.
#include <iostream>
#include <functional>
#include <vector>
std::string to_string(const char* s)
{
return s;
}
class Test
{
private:
void print() {}
public:
template<typename T, typename... Args>
void print(T value, Args... args)
{
std::cout << value << "\n";
print(args...);
}
template<typename... Args>
std::function<void()> getFunc(Args... args)
{
using namespace std;
std::vector<std::string> ArgumentList;
std::initializer_list<int> {(ArgumentList.push_back(to_string(args)), 0)...};
return [=] {for (const auto &t : ArgumentList){print(t);}};
}
};
int main()
{
Test().getFunc("Hey", 1, 2, 3.0f)();
}
Upvotes: 1