4

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

Community
  • 1
  • 1
Sean Lynch
  • 2,852
  • 4
  • 32
  • 46
  • It doesn't help you much but the code compiles with clang 3.4 (trunk). – Ali Nov 15 '13 at 17:33
  • I just tested it with g++4.9 snapshot and it works there too. – Sean Lynch Nov 15 '13 at 17:46
  • That is good news but it unfortunately doesn't help you if you need to use gcc 4.8 – Ali Nov 15 '13 at 18:00
  • See my silly work around I guess.. – Brandon Nov 15 '13 at 20:03
  • I added answer that compile also with GCC 4.7 (tested) in addiction to GCC 4.8 (tested) probably a bit late maybe it can still be usefull for you. You should note that with such code clang is generating an assembly bloat it is one of few cases where GCC performs much better. – CoffeDeveloper Oct 21 '14 at 16:44

2 Answers2

1

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)();
}
Brandon
  • 22,723
  • 11
  • 93
  • 186
1

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:) )

Community
  • 1
  • 1
CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69