MattyZ
MattyZ

Reputation: 1561

Using variadic templates with templated class arguments

#include <stdlib.h>
#include <stdint.h>
#include <vector>

struct MyClass1 {
        virtual ~MyClass1() = default;  
};

template <typename T>    
struct MyClass2 : public MyClass1 {};

template <class...C> class MyClass3 { //will be singleton class
    public:
        MyClass3() 
        {
            make_classes<C...>(_class_vec);
        }

    private:
       static const size_t _num_classes = sizeof...(C);
       static uint8_t *_buf[_num_classes];
       std::vector<MyClass1*> _class_vec;

       void make_classes(std::vector<MyClass1*> &vec) {}

       template <class First, class... Rest> void make_classes(std::vector<MyClass1*> &vec) {
            static size_t count;

            First* tmp = new (_buf[count++]) First;
            vec.push_back(tmp);
            make_classes<Rest...>(vec); 
        }   

};

int main() {
    auto foo = MyClass3<MyClass2<int>, MyClass2<char>>();
    return 0;
}

I'm trying to pass a set of variadic template arguments to the constructor of Class3, and use placement new to put them in the cells of an appropriately sized pool, the first dimension of which is calculated at compile time. Then construct a vector of pointers to them with the type of the base object (the derived objects that are being passed into the template of Class3 are templated with a single variable type.)

This compiles OK, but when I go to create an object of type Class3 in main I get the following error:

prog.cpp: In instantiation of 'void MyClass3<C>::make_classes(std::vector<MyClass1*>&) [with First = MyClass2<char>; Rest = {}; C = {MyClass2<int>, MyClass2<char>}]':
prog.cpp:32:29:   required from 'void MyClass3<C>::make_classes(std::vector<MyClass1*>&) [with First = MyClass2<int>; Rest = {MyClass2<char>}; C = {MyClass2<int>, MyClass2<char>}]'
prog.cpp:17:29:   required from 'MyClass3<C>::MyClass3() [with C = {MyClass2<int>, MyClass2<char>}]'
prog.cpp:38:57:   required from here
prog.cpp:32:29: error: no matching function for call to 'MyClass3<MyClass2<int>, MyClass2<char> >::make_classes(std::vector<MyClass1*>&)'
        make_classes<Rest...>(vec); 
                             ^
prog.cpp:27:55: note: candidate: template<class First, class ... Rest> void MyClass3<C>::make_classes(std::vector<MyClass1*>&) [with First = First; Rest = {Rest ...}; C = {MyClass2<int>, MyClass2<char>}]
            template <class First, class... Rest> void make_classes(std::vector<MyClass1*> &vec) {
                                                       ^
prog.cpp:27:55: note:   template argument deduction/substitution failed:
prog.cpp:32:29: note:   couldn't deduce template parameter 'First'
        make_classes<Rest...>(vec); 

What's the correct way to accomplish this using variadic templates? Please keep in mind that I'm working on an embedded platform, so my compiler is C++11 compatible, but much of the STL and Boost is not really available to me.

Upvotes: 1

Views: 166

Answers (3)

skypjack
skypjack

Reputation: 50540

Recursion in your code lacks the base case, that is the one having an empty parameter pack.
Non template member function does not take part to the game, for you are always explicitly specializing the template member function with the line:

make_classes<Rest...>(0, vec);

A possible solution would be this:

  • Modify your constructor:

    MyClass3() 
    {
        make_classes<C...>(0, _class_vec);
    }
    
  • Update both your make_classes functions:

    template<typename... T>
    std::enable_if_t<sizeof...(T)==0>
    make_classes(int, std::vector<MyClass1*> &vec) {}
    
    template <class First, class... Rest> void make_classes(char, std::vector<MyClass1*> &vec) {
            static size_t count;
    
            First* tmp = new (_buf[count++]) First;
            vec.push_back(tmp);
            make_classes<Rest...>(0, vec); 
    }
    

NOTE: it won't compile in any case, unless you also define _buf somewhere.

Upvotes: 2

Sam Varshavchik
Sam Varshavchik

Reputation: 118300

The compilation error is because the last recursive call to make_classes is:

make_classes<>(vec);

And, of course, this does not match the template function.

void make_classes(std::vector<MyClass1*> &vec) {}

This is not a template function. There's a difference between:

make_classes(vec);

and

make_classes<>(vec);

You could try specializing the template function, but with the whole thing inside a template class, this gets ugly rather quickly.

The easiest solution is to replace make_classes() with:

template <class First> void make_1class(std::vector<MyClass1*> &vec)
{
    static size_t count;

    First* tmp = new (_buf[count++]) First;
    vec.push_back(tmp);
}

template <typename OneClassLeft>
void make_classes(std::vector<MyClass1*> &vec)
{
    make_1class<OneClassLeft>(vec);
}

template <class First, class Second, class ...Rest>
void make_classes(std::vector<MyClass1*> &vec)
{
    make_1class<First>(vec);

    make_classes<Second, Rest...>(vec);
}

Upvotes: 2

xaxxon
xaxxon

Reputation: 19761

You have a recursive call with no base case. Eventually you're calling make_classes<>(vec); because when you called it with one type, First ate that and Rest... was left empty.

You need something to handle that base case template<class First> and you should be good.

Upvotes: 1

Related Questions