Search code examples
c++c++14variadic

Using variadic template as attribute


A couple of weeks ago, I asked this question about a "variadic" matrix class and now I am trying to apply the answer in a different project and I am getting error's I don't understand. Basically the problem is that I don't understand how to use the "variadic" matrix class as an attribute in another class (which is named Test in the minimal working example).

Minimal working example main.cpp:

#include <iostream>
#include <memory>
#include "matrix.hpp"
#include "test.hpp"
using namespace std;

int main(int argc, char *argv[])
{
    Test T();
    return 0;
}

matrix.hpp:

#ifndef MATRIX_H
#define MATRIX_H

template<typename T>
class Matrix 
     {
      private:
        std::vector<size_t> dimensions;
        std::unique_ptr<T[]> _data;

      public:
        Matrix() 
        {}

        Matrix(std::vector<size_t> dims) 
        : dimensions(std::move(dims)) 
        {
            size_t size = flat_size();
            _data = std::make_unique<T[]>(size);
         }

         template<typename ... Dimensions>
         Matrix(size_t dim, Dimensions&&... dims) 
         {
           size_t size = apply_dimensions(dim, std::forward<Dimensions>(dims)...);
          _data = std::make_unique<T[]>(size);
         }   

         template<typename ... Indexes>
         T & operator()(size_t idx, Indexes&& ... indexes) 
         {
            if (sizeof...(indexes)+1 != dimensions.size())
                 throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
            size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
            return at(flat_index);
         }

        template<typename ... Indexes>
        T const& operator()(size_t idx, Indexes&& ... indexes) const 
        {
            if (sizeof...(indexes)+1 != dimensions.size())
                throw std::runtime_error("Incorrect number of parameters used to retrieve Matrix Data!");
            size_t flat_index = get_flat_index(0, idx, std::forward<Indexes>(indexes)...);
            return at(flat_index);
        }

        Matrix(const Matrix&) 
        {}

        Matrix(Matrix&& other)
        : Matrix() 
        {
            swap(*this, other);
        }

        Matrix & operator=(Matrix other)
        {
            swap(*this, other); 
            return *this;
        }

        friend void swap(Matrix& first, Matrix& second) 
        {
            using std::swap;
            swap(first.dimensions, second.dimensions);
            swap(first._data, second._data);
         }


         size_t dimension_size(size_t dim) const {
            return dimensions[dim];
         }

        size_t num_of_dimensions() const {
            return dimensions.size();
        }

     private:
   template<typename ... Dimensions>
    size_t apply_dimensions(size_t dim, Dimensions&& ... dims)
    {
        dimensions.emplace_back(dim);
        return dim * apply_dimensions(std::forward<Dimensions>(dims)...);
    }

    size_t apply_dimensions(size_t dim) 
    {
        dimensions.emplace_back(dim);
        return dim;
    }

    template<typename ... Indexes>
    size_t get_flat_index(size_t dim, size_t index, Indexes&& ... indexes) const 
    {
        return get_offset(index, dim) + get_flat_index(dim + 1, std::forward<Indexes>(indexes)...);
    }

    size_t get_flat_index(size_t dim, size_t index) const 
    {
        return get_offset(index, dim);
    }

    size_t get_offset(size_t index, size_t dim) const 
    {
        if (index >= dimensions[dim])
            throw std::runtime_error("Index out of Bounds");
        for (size_t i = dim + 1; i < dimensions.size(); i++) 
        {
            index *= dimensions[i];
        }
        return index;
    }

    T& at(size_t flat_index) {
        return _data[flat_index];
    }

    T const& at(size_t flat_index) const {
        return _data[flat_index];
    }

    size_t flat_size() const {
        size_t size = 1;
        for (size_t dim : dimensions)
            size *= dim;
        return size;
    }
 };

 #endif // MATRIX_H

test.hpp:

#ifndef TEST_H
#define TEST_H
#include "matrix.hpp"
#include <vector>
#include <memory>
class Test
{
public:
    Test();
    Matrix<double>* temp = NULL;
    Matrix<double> mat;
};
#endif // TEST_H

test.cpp:

#include "test.hpp"

Test::Test()
{
    mat = Matrix<double>{9, 2, 8};
}

After compiling with "g++ -std=c++14 *.cpp -o cfd.out", I get the following kind of errors:

matrix.hpp:8:14: error: ‘vector’ in namespace ‘std’ does not name a template type
     std::vector<size_t> dimensions;
          ^
matrix.hpp:15:27: error: expected ‘)’ before ‘<’ token
     Matrix(std::vector<size_t> dims) 
                       ^
matrix.hpp: In member function ‘T& Matrix<T>::operator()(size_t, Indexes&& ...)’:
matrix.hpp:32:41: error: ‘dimensions’ was not declared in this scope
         if (sizeof...(indexes)+1 != dimensions.size())
                                     ^
matrix.hpp: In member function ‘const T& Matrix<T>::operator()(size_t, Indexes&& ...) const’:
matrix.hpp:41:41: error: ‘dimensions’ was not declared in this scope
         if (sizeof...(indexes)+1 != dimensions.size())
                                     ^
matrix.hpp: In member function ‘size_t Matrix<T>::dimension_size(size_t) const’:
matrix.hpp:71:20: error: ‘dimensions’ was not declared in this scope
         return dimensions[dim];
matrix.hpp: In member function ‘size_t Matrix<T>::num_of_dimensions() const’:
matrix.hpp:75:20: error: ‘dimensions’ was not declared in this scope
         return dimensions.size();
                ^
matrix.hpp: In member function ‘size_t Matrix<T>::apply_dimensions(size_t, Dimensions&& ...)’:
matrix.hpp:82:13: error: ‘dimensions’ was not declared in this scope
         dimensions.emplace_back(dim);
         ^
matrix.hpp: In member function ‘size_t Matrix<T>::apply_dimensions(size_t)’:
matrix.hpp:88:13: error: ‘dimensions’ was not declared in this scope
         dimensions.emplace_back(dim);
         ^
matrix.hpp: In member function ‘size_t Matrix<T>::get_offset(size_t, size_t) const’:
matrix.hpp:105:26: error: ‘dimensions’ was not declared in this scope
         if (index >= dimensions[dim])
                      ^
matrix.hpp:107:42: error: ‘dimensions’ was not declared in this scope
         for (size_t i = dim + 1; i < dimensions.size(); i++) 
                                      ^
matrix.hpp: In member function ‘size_t Matrix<T>::flat_size() const’:
matrix.hpp:124:31: error: ‘dimensions’ was not declared in this scope
         for (size_t dim : dimensions)
                           ^
In file included from test.hpp:3:0,
             from test.cpp:1:
matrix.hpp:8:14: error: ‘vector’ in namespace ‘std’ does not name a template type
     std::vector<size_t> dimensions;
          ^
matrix.hpp:9:14: error: ‘unique_ptr’ in namespace ‘std’ does not name a template type
     std::unique_ptr<T[]> _data;
          ^
matrix.hpp:15:27: error: expected ‘)’ before ‘<’ token
     Matrix(std::vector<size_t> dims) 
                       ^
matrix.hpp:23:23: error: expected ‘)’ before ‘dim’
     Matrix(size_t dim, Dimensions&&... dims) 
                   ^
matrix.hpp:30:24: error: ‘size_t’ was not declared in this scope
     T & operator()(size_t idx, Indexes&& ... indexes) 
                    ^
matrix.hpp:30:43: error: expected primary-expression before ‘&&’ token
     T & operator()(size_t idx, Indexes&& ... indexes) 
                                       ^
matrix.hpp:30:46: error: expected primary-expression before ‘...’ token
     T & operator()(size_t idx, Indexes&& ... indexes) 
                                          ^
matrix.hpp:30:57: error: declaration of ‘operator()’ as non-function
     T & operator()(size_t idx, Indexes&& ... indexes) 
                                                     ^
matrix.hpp:39:29: error: ‘size_t’ was not declared in this scope
     T const& operator()(size_t idx, Indexes&& ... indexes) const 
                         ^
matrix.hpp:39:48: error: expected primary-expression before ‘&&’ token
     T const& operator()(size_t idx, Indexes&& ... indexes) const 
                                            ^
matrix.hpp:39:51: error: expected primary-expression before ‘...’ token
     T const& operator()(size_t idx, Indexes&& ... indexes) const 
                                               ^
matrix.hpp:39:62: error: declaration of ‘operator()’ as non-function
     T const& operator()(size_t idx, Indexes&& ... indexes) const 
                                                          ^
matrix.hpp:70:9: error: ‘size_t’ does not name a type
     size_t dimension_size(size_t dim) const {
     ^
matrix.hpp:74:9: error: ‘size_t’ does not name a type
     size_t num_of_dimensions() const {
     ^
matrix.hpp:80:9: error: ‘size_t’ does not name a type
     size_t apply_dimensions(size_t dim, Dimensions&& ... dims)
     ^
matrix.hpp:86:9: error: ‘size_t’ does not name a type
     size_t apply_dimensions(size_t dim) 
     ^
matrix.hpp:93:9: error: ‘size_t’ does not name a type
     size_t get_flat_index(size_t dim, size_t index, Indexes&& ... indexes) 
     ^
matrix.hpp:98:9: error: ‘size_t’ does not name a type
     size_t get_flat_index(size_t dim, size_t index) const 
     ^
matrix.hpp:103:9: error: ‘size_t’ does not name a type
     size_t get_offset(size_t index, size_t dim) const 
     ^
matrix.hpp:114:12: error: expected ‘;’ at end of member declaration
     T& at(size_t flat_index) {
        ^
matrix.hpp:114:22: error: expected ‘)’ before ‘flat_index’
     T& at(size_t flat_index) {
                  ^
matrix.hpp:118:18: error: expected ‘;’ at end of member declaration
     T const& at(size_t flat_index) const {
              ^
matrix.hpp:118:21: error: redeclaration of ‘const T& Matrix<T>::at’
     T const& at(size_t flat_index) const {
                 ^
matrix.hpp:114:15: note: previous declaration ‘T& Matrix<T>::at’
     T& at(size_t flat_index) {
           ^
matrix.hpp:118:28: error: expected ‘)’ before ‘flat_index’
     T const& at(size_t flat_index) const {
                        ^
matrix.hpp:122:9: error: ‘size_t’ does not name a type
     size_t flat_size() const {
     ^
matrix.hpp: In function ‘void swap(Matrix<T>&, Matrix<T>&)’:
matrix.hpp:64:24: error: ‘std::swap’ has not been declared
         using std::swap;
                    ^
In file included from test.cpp:1:0:
test.hpp: At global scope:
test.hpp:8:28: error: ‘NULL’ was not declared in this scope
 Matrix<double>* temp = NULL;
                        ^
test.cpp: In constructor ‘Test::Test()’:
test.cpp:5:33: error: no matching function for call to ‘Matrix<double>::Matrix(<brace-enclosed initializer list>)’
 mat = Matrix<double>{9, 2, 8};
                             ^
In file included from test.hpp:3:0,
             from test.cpp:1:
matrix.hpp:50:9: note: candidate: Matrix<T>::Matrix(Matrix<T>&&) [with T = double]
     Matrix(Matrix&& other)
     ^
matrix.hpp:50:9: note:   candidate expects 1 argument, 3 provided
matrix.hpp:47:9: note: candidate: Matrix<T>::Matrix(const Matrix<T>&) [with T = double]
     Matrix(const Matrix&) 
     ^
matrix.hpp:47:9: note:   candidate expects 1 argument, 3 provided
matrix.hpp:12:9: note: candidate: Matrix<T>::Matrix() [with T = double]
     Matrix() 
     ^
matrix.hpp:12:9: note:   candidate expects 0 arguments, 3 provided
matrix.hpp: In instantiation of ‘Matrix<T>::Matrix() [with T = double]’:
test.cpp:3:12:   required from here
matrix.hpp:12:9: error: uninitialized reference member in ‘double&’ [-fpermissive]
matrix.hpp:114:15: note: ‘double& Matrix<double>::at’ should be initialized
     T& at(size_t flat_index) {

So far as I understand, there is a problem with recognizing the template type but I don't know how to solve it. Finally, I want to remark that I've had no problems with using the matrix class outside of another class. For instance the following piece of code seems to work fine.

#include <iostream>
#include <memory>
#include "fcontainer.hpp"
#include "printer.hpp"
#include "matrix.hpp"
#include "test.hpp"
using namespace std;

int main(int argc, char *argv[])
{
    std::cout << "Input the sizes of each of the dimensions.\n";
    std::string line;
    std::getline(std::cin, line);
    std::stringstream ss(line);
    size_t dim;
    std::vector<size_t> dimensions;
    while(ss >> dim)
        dimensions.emplace_back(dim);
    Matrix<double> mat{6, 5};
    mat(5, 2) = 17;
    Matrix<double>* temp = NULL;
    mat = Matrix<double>{9, 2, 8};
    temp = &(mat);
    mat(5, 1, 7) = 24;
    cout << mat(5, 1, 7) << endl;
    cout << (*temp)(5, 1, 7) << endl;
    return 0;
}

So I would like to understand what goes wrong in a class and what I can do to solve it.


Solution

  • As @Oliv and @AndyG said, you need to add #include for the dependencies:

    #include <vector>
    #include <memory>
    

    You should include them before Matrix is defined. One way to do that is to move the #include statements earlier in Test.hpp, but even better is to add them to the start of Matrix.hpp so that the matrix header file is self-contained.