38

While doing variadic template programming in C++11 on GCC, once in a while I get an error that says "Sorry, unimplemented: cannot expand 'Identifier...' into a fixed-length arugment list." If I remove the "..." in the code then I get a different error: "error: parameter packs not expanded with '...'".

So if I have the "..." in, GCC calls that an error, and if I take the "..." out, GCC calls that an error too.

The only way I have been able to deal with this is to completely rewrite the template metaprogram from scratch using a different approach, and (with luck) I eventually come up with code that doesn't cause the error. But I would really like to know what I was doing wrong. Despite Googling for it and despite much experimentation, I can't pin down what it is that I'm doing differently between variadic template code that does produce this error, and code that does not have the error.

The wording of the error message seems to imply that the code should work according the C++11 standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?

Here's some code that produces the error. Note: I don't need you to write a correct implementation for me, but rather just to point out what is about my code that is causing this specific error

// Used as a container for a set of types.
template <typename... Types> struct TypePack
{
    // Given a TypePack<T1, T2, T3> and T=T4, returns TypePack<T1, T2, T3, T4>
    template <typename T>
    struct Add
    {
        typedef TypePack<Types..., T> type;
    };
};

// Takes the set (First, Others...) and, while N > 0, adds (First) to TPack.
// TPack is a TypePack containing between 0 and N-1 types.
template <int N, typename TPack, typename First, typename... Others>
struct TypePackFirstN
{
    // sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list
    typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type;
};

// The stop condition for TypePackFirstN:  when N is 0, return the TypePack that has been built up.
template <typename TPack, typename... Others>
struct TypePackFirstN<0, TPack, Others...> //sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list
{
    typedef TPack type;
};

EDIT: I've noticed that while a partial template instantiation that looks like does incur the error:

template <typename... T>
struct SomeStruct<1, 2, 3, T...> {};

Rewriting it as this does not produce an error:

template <typename... T>
struct SomeStruct<1, 2, 3, TypePack<T...>> {};

It seems that you can declare parameters to partial specializations to be variadic; i.e. this line is OK:

template <typename... T>

But you cannot actually use those parameter packs in the specialization, i.e. this part is not OK:

SomeStruct<1, 2, 3, T...>

The fact that you can make it work if you wrap the pack in some other type, i.e. like this:

SomeStruct<1, 2, 3, TypePack<T...>>

to me implies that the declaration of the variadic parameter to a partial template specialization was successful, and you just can't use it directly. Can anyone confirm this?

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Dennis
  • 2,607
  • 3
  • 21
  • 28

4 Answers4

48

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

Konstantin Weitz
  • 6,180
  • 8
  • 26
  • 43
deft_code
  • 57,255
  • 29
  • 141
  • 224
  • This should be actually the best answer. Specialization is the way to go with gcc 4.4. –  Sep 02 '10 at 21:10
  • I changed this to the accepted answer because I actually just used the work-around from this answer and it solved my problem. Note that if you have a lot of template code, using this work around seems to consume more compile time / size than not. However, after I discovered this was increasing my build size/time, I found I was able to roll this up into an outer template I already had that was already specialized, and so amortized the cost using the specialization for the work around. – Dennis Jul 25 '11 at 00:11
  • Can you please explain what special effect does partial specialization does here so that bug is now gone or i should ask how partial specialization resolves the issue , thanks a lot , waiting for reply ? – Mr.Anubis Oct 10 '11 at 14:13
  • GCC has code to handle variadic templates. However GCC 4.4 at least is missing to code to expand a variadic template paramter into a template that has some fixed parameters, (eg `broken_head`). Once you get past the unimplemented part (expand it another way) the rest of the variadics work fine. Specialization is just a way to avoid forcing the compiler to expand `all...` directly. It can expand it into the type with no fixed parameters, then the specialization code can pick the best fit (the one with `head` as a fixed parameter). – deft_code Oct 10 '11 at 14:51
  • Your this line " **Once you get past the unimplemented part (expand it another way) the rest of the variadics work fine** " and following lines went above my broken head :D, Can you please explain the process by giving some example i.e ow partial specialization resolves the issue ? , probably by editing your answer? . Thanks a lot :) – Mr.Anubis Oct 10 '11 at 16:29
3

As far as I understand the error is reported because the compiler sees the declaration of the template class as a class with at least 3 arguments followed by the optional ones. Since you try to refer to it with 2 arguments followed by the expansion list, it gets confused and issues this error. In order to make it compile correctly you just need to first declare the template like this:

template <int N, typename TPack, typename... Others>
struct TypePackFirstN;

And after that the recursion step definition has to be restated as a template specialization. (this does the trick with gcc 4.5.0 20100404).

// Takes the set (First, Others...) and, while N > 0, adds (First) to TPack.
// TPack is a TypePack containing between 0 and N-1 types.
template <int N, typename TPack, typename First, typename... Others>
struct TypePackFirstN<N, TPack, First, Others...>
{
    // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone
    typedef typename TypePackFirstN<N-1, typename TPack::template Add<First>::type, Others...>::type type;
};

// The stop condition for TypePackFirstN:  when N is 0, return the TypePack that has been built up.
template <typename TPack, typename... Others>
struct TypePackFirstN<0, TPack, Others...>         // Error "sorry, unimplemented: cannot expand ‘Others ...’ into a fixed-length argument list" should be now gone
{
    typedef TPack type;
};
2

What version of GCC are you using? According to this GCC status page, GCC 4.4 should support it.

Testing with GCC 4.4.2, I get similar error.

The wording of the error message seems to imply that the code should work according the C++0x standard, but that GCC doesn't support it yet. Or perhaps it is a compiler bug?

This is correct, GCC understands the code but cannot yet spit out GIMPLE for it.

As for what causes the error, it's the extension of template variable list to another template's list of variables.

LiraNuna
  • 64,916
  • 15
  • 117
  • 140
  • Could you explain more fully what it means to extend a template variable list into another template's list? I was beginning to think it had to do with not being able to use variadic template packs directly as arguments to a template specialization. – Dennis Jan 01 '10 at 21:33
  • 2
    If the pack expansion creates a template-argument list where some arguments hit fixed parameters, and some hit a template parameter pack, then this error does seem to occur. I rewrote your code to this, and it does work: http://codepad.org/IBT60IbJ (i had to introduce another partial specialization, to make the `<0, A, B>` case choose an uniq specialization). In this one, each pack expansion always hits a corresponding parameter pack only. – Johannes Schaub - litb Jan 01 '10 at 22:00
1

deft_code's answer is correct. I'm posting this just in case this it's helpful seeing a side-by-side comparison of broken versus fixed code.

I'll take the code sample from the following question someone posted that was duplicated to this one and is now closed: Is ther a good workaround for GCC's "sorry, unimplemented: cannot expand ‘NEXT ...’ into a fixed-length argument list" error?

#include <iostream>

template <int FIRST, int... NEXT>
struct Test {
    static const int VALUE = FIRST + Test<NEXT...>::VALUE;
};

template <int FIRST>
struct Test<FIRST> {
    static const int VALUE = FIRST;
};

int main() {
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6"
    return 0;
}

This, when compiled, gives:

g++ -std=c++11 -o test test.cc
test.cc:5:50: sorry, unimplemented: cannot expand âNEXT ...â into a fixed-length argument list

But this works (I added comments where code was changed):

#include <iostream>

template <int ... ALL> // Adeed
struct Test;           // Added

template <int FIRST, int... NEXT>
struct Test<FIRST, NEXT...> { // Note: specialized with <FIRST, NEXT...>
    static const int VALUE = FIRST + Test<NEXT...>::VALUE;
};

template <int FIRST>
struct Test<FIRST> {
    static const int VALUE = FIRST;
};

int main() {
    std::cout << Test<1, 2, 3>::VALUE << std::endl; // print "6"
    return 0;
}

Note what was done in this three line change marked by the comments: what was originally the first template was made a specialized template of the newly added variadic template.

Community
  • 1
  • 1
firebush
  • 5,180
  • 4
  • 34
  • 45