Gcc Error with Variadic Templates: "Sorry, Unimplemented: Cannot Expand 'Identifier...' into a Fixed-Length Argument List"

GCC error with variadic templates: Sorry, unimplemented: cannot expand 'Identifier...' into a fixed-length argument list

There is a trick to get this to work with gcc. The feature isn't fully implemented yet, but you can structure the code to avoid the unimplemented sections. Manually expanding a variadic template into a parameter list won't work. But template specialization can do that for you.

template< char head, char ... rest >
struct head_broken
{
static const char value = head;
};

template< char ... all >
struct head_works; // make the compiler hapy

template< char head, char ... rest >
struct head_works<head,rest...> // specialization
{
static const char value = head;
};

template<char ... all >
struct do_head
{
static const char head = head_works<all...>::value;
//Sorry, unimplemented: cannot expand 'all...' into a fixed-length arugment list
//static const char head = head_broken<all...>::value;
};

int main
{
std::cout << head_works<'a','b','c','d'>::value << std::endl;
std::cout << head_broken<'a','b','c','d'>::value << std::endl;
std::cout << do_head<'a','b','c','d'>::head << std::endl;
}

I tested this with gcc 4.4.1

Variadic templates, compiler apologies

The second version avoids the bug because the primary template is declared as template<typename...>, i.e. it's variadic. The key to the bug is in the error message: "sorry, unimplemented: cannot expand 'args ...' into a fixed-length argument list" (emphasis mine).

Thus counter<Args...>::value will work on that second case because counter is tailored to accept any number of arguments. However, in the first case where the primary template is declared as template<typename T, typename... args>, the compiler has to separate args into a fixed-length part (T) and a variadic part (the new args). Presumably that's the very functionality that is not implemented in your version of GCC.

I have no reason to believe that whatever machinery allows the <T, Args...> specialization of the second case to match the <typename...> primary template could be reused for fixed-length expansion.

(Finally since the support of C++11 features is marked as 'experimental' by GCC you really can't have any expectation as to what will work and what won't, much less why and how. Those kind of questions can only reasonably be answered by GCC developers on their mailing-list, not us. We're not mind readers.)

How can I use GoogleMock in jenkins?

It seems as though the version of GCC you have on CentOS is too old to handle these variadic templates. I would expect 4.4.7 to have them available since the status page seems to indicate they should. However this question seems to confirm the situation.

My advice would be to upgrade the compiler using the dev-toolset 2 repo. This will give you access to GCC 4.8 on CentOS 6 (which by the GCC version is what I'm guessing you're using) but with a modified stdlib which means your binaries will still run using the runtime from CentOS/RedHat 5.

Variadic templates. Some issues

What does return std::index_sequence_for()) ?

assuming T_value... is T, T, T (i.e. 3 types...)

std::index_sequence<0, 1, 2>

Why in yadda(std::get(tuple)...); there is Is instead of Is...? Therefore, what does it mean Is? Is... in unpacked ( expanded ) types pack but what is Is.

Is represents 'the current value of Is' while Is... is being unpacked. The trailing ... causes unpacking of the expression in which Is is used.

Especially, which std::get fits from (1)-(8) (http://en.cppreference.com/w/cpp/utility/tuple/get)

In this case the tuple reference is a const std::tuple<T_values...>& so it'll be number 3.

Why call_yadda_with_tuple gets std::index_sequence. After all, this argument is nameless so it is useless. I suppose that it is connected with deduction types but I cannot see how does it help?

It's there simply to cause Is... to exist and therefore allow you to expand across all Is in the sequence.

edit:

Here's an example with comments that hopefully explain what's going on

#include <utility>
#include <tuple>
#include <string>
#include <iostream>

// for any value I, write a comma and space to stdout
// (i.e. ignore the value I)
template<std::size_t I>
void emit_sep()
{
std::cout << ", ";
}

// specialise for when I is zero... no comma in this case
template<>
void emit_sep<0>()
{
}

// emit and value at some position I. Use emit_sep<I> to determine whether
// to print a separator
template<std::size_t I, class T>
void emit(const T& t)
{
emit_sep<I>();
std::cout << t;
}

// given a tuple type and a sequence of integers (Is...) emit the value
// at each index position of the tuple. Take care to emit a separator only
// before each element after the first one
template<class Tuple, size_t...Is>
void impl_show_it(const Tuple& tup, std::index_sequence<Is...>)
{
using expand = int[];

std::cout << "here are the indexes in the index_sequence: ";
// the following line will expand (in our example) to:
// void(int[] { 0,
// (emit<0>(0), 0),
// (emit<1>(1), 0),
// (emit<2>(2), 0),
// });
// and the optimiser will remove the operations which have no observable
// side-effects (namely, building an array of ints which is never used)
// so the code emitted will be equivalent to:
// emit<0>(0); emit<1>(1); emit<2>(2);
//
void(expand {
0,
(emit<Is>(Is), 0)...
});
std::cout << std::endl;

std::cout << "here are the values in the tuple: ";
void(expand {
0,
(emit<Is>(std::get<Is>(tup)), 0)...
});
std::cout << std::endl;
}

// for some tuple type, compute the size of the tuple, build an index sequence
// representing each INDEX in the tuple and then use that sequence to call
// impl_show_it in order to actually perform the write
template<class Tuple>
void show_it(const Tuple& tup)
{
constexpr auto tuple_size = std::tuple_size<Tuple>::value;
auto sequence = std::make_index_sequence<tuple_size>();
impl_show_it(tup, sequence);
}

// make a tuple and then show it on stdout
int main()
{
auto t = std::make_tuple(6, std::string("hello"), 5.5);
show_it(t);
}

expected results:

here are the indexes in the index_sequence: 0, 1, 2
here are the values in the tuple: 6, hello, 5.5

c++11 syntax variadic templates expand with rebind

MSVC's variadic templates implementation is a huge mess, and many slightly more complex expansion patterns don't work. The usual workaround is to extract the complex part into a helper template. Try this:

template <typename Var, typename T>
struct rebindOne { typedef typename T::template rebindVar<Var>::type type; };

template<typename Var, typename... T>
struct rebindVar<Var, std::tuple<T...> > {
typedef typename std::tuple< typename rebindOne<Var, T>::type... > type;
};

C++11 variadic templates and comma-separated expressions equivalence

Because in the first case you don't have comma-separated arguments but you are instead using the comma-operator, a totally different beast.

You can implement the function expand recursively:

inline void expand() {}

template<typename T, typename... Args>
inline void expand(T&& head, Args&&... tail)
{
some_function(head);
expand(tail...);
}

Is Boost.Tuple compatible with C++0x variadic templates?

It doesn't seem to like expanding Args... to T1, T2, T3, ..., T9 as Boost has it.

As a workaround, use constructs that don't require this expansion:

#include <boost/tuple/tuple.hpp>

template <typename... Args>
auto my_make_tuple(Args... args) -> decltype(boost::make_tuple(args...))
{
return {args...};
}

int main (void)
{
boost::tuple<int, char> t = my_make_tuple(8, 'c');
}

Another option might be to do the expanding manually, seeing that boost::tuple supports up to 10 arguments.

#include <boost/tuple/tuple.hpp>

template <unsigned, class, class...> struct nth_argument;

template <unsigned N, class Default, class T, class... Args>
struct nth_argument<N, Default, T, Args...>
{
typedef typename nth_argument<N - 1, Default, Args...>::type type;
};

template <class Default, class T, class... Args>
struct nth_argument<0, Default, T, Args...>
{
typedef T type;
};

template <unsigned N, class Default>
struct nth_argument<N, Default>
{
typedef Default type;
};

template <typename ...Args>
struct tuple_from_var_template
{
typedef boost::tuple<
typename nth_argument<0, boost::tuples::null_type, Args...>::type,
typename nth_argument<1, boost::tuples::null_type, Args...>::type,
typename nth_argument<2, boost::tuples::null_type, Args...>::type,
typename nth_argument<3, boost::tuples::null_type, Args...>::type,
typename nth_argument<4, boost::tuples::null_type, Args...>::type,
typename nth_argument<5, boost::tuples::null_type, Args...>::type,
typename nth_argument<6, boost::tuples::null_type, Args...>::type,
typename nth_argument<7, boost::tuples::null_type, Args...>::type,
typename nth_argument<8, boost::tuples::null_type, Args...>::type,
typename nth_argument<9, boost::tuples::null_type, Args...>::type
> type;
};

template <typename... Args>
typename tuple_from_var_template<Args...>::type my_make_tuple(Args... args)
{
return typename tuple_from_var_template<Args...>::type(args...);
}

int main (void)
{
boost::tuple<int, char> t = my_make_tuple(8, 'c');
}

How to properly use references with variadic templates

I would not use rvalue references here, because that will allow you to bind to rvalues which can allow such nonsensical code as:

inc(1);

So, I would stick with regular references:

template<typename T>
void inc(T& t) { ++t; }

template<typename T,typename ... Args>
void inc(T& t, Args& ... args) { ++t; inc(args...); }


Related Topics



Leave a reply



Submit