hyde
hyde

Reputation: 62906

How to use 2-dimensional array as template argument?

I want to pass a two dimensional array as argument to a template function, without specifying dimensions and without using a macro. Here's the code to demonstrate what I want. The important bits are makeHow() method near the middle, and calling it at the end.

How can I make the Matrix::makeHow() method to work correctly?

#include <algorithm>
#include <iostream>
#include <memory>
#include <ostream>

class Matrix {

    int rows = 0;
    int cols = 0;
    std::unique_ptr<double> data{};
    friend std::ostream &operator<<(std::ostream &stream, const Matrix &m);

public:
    // just example: this works but is inconvenient
    template<class ARRAY>
    static Matrix make(int rows, ARRAY *source)
    {
        Matrix m;
        m.cols = sizeof(*source)/ sizeof(**source);
        m.rows = rows;
        m.data.reset(new double[m.rows * m.cols]);
        std::copy(&(source[0][0]), &(source[0][0]) + (m.rows*m.cols), m.data.get());
        std::cout << __PRETTY_FUNCTION__ << ": rows * cols = " << m.rows << " * " << m.cols << std::endl;
        return m;
    }

    // just example: this works but is inconvenient, too
    template<int ROWS, class ARRAY>
    static Matrix make(ARRAY *source)
    {
        Matrix m;
        m.cols = sizeof(*source)/ sizeof(**source);
        m.rows = ROWS;
        m.data.reset(new double[m.rows * m.cols]);
        std::copy(&(source[0][0]), &(source[0][0]) + (m.rows*m.cols), m.data.get());
        std::cout << __PRETTY_FUNCTION__ << ": rows * cols = " << m.rows << " * " << m.cols << std::endl;
        return m;
    }

    // THE IMPORTANT PART: this doesn't work, sizeof(ARRAY) is size of pointer
    template<class ARRAY>
    static Matrix makeHow(ARRAY *source)
    {
        Matrix m;
        m.cols = sizeof(*source)/ sizeof(**source);
        m.rows = sizeof(ARRAY) / sizeof(*source);
        m.data.reset(new double[m.rows * m.cols]);
        std::copy(&(source[0][0]), &(source[0][0]) + (m.rows*m.cols), m.data.get());
        std::cout << __PRETTY_FUNCTION__ << ": rows * cols = " << m.rows << " * " << m.cols << std::endl;
        return m;
    }
};

// Convenience macro, has all the ugly macro problems, but works...
#define make_matrix(array) Matrix::make<sizeof(array)/sizeof(*array)>(array)

std::ostream &operator<<(std::ostream &stream, const Matrix &m)
{
    for(int row = 0; row < m.rows; ++row) {
        for(int col = 0; col < m.cols; ++col) {
            auto index = row * m.cols + col;
            stream << m.data.get()[index] << ' ';
        }
        stream << '\n';
    }
    return stream;
}

int main()
{
    double data[][2] = { {1.1, 1.2},
                         {2.1, 2.2},
                         {3.1, 3.2} };
    constexpr int ROWS = sizeof(data)/sizeof(*data);

    std::cout << "\nUsing explicit syntax 1:\n" << Matrix::make(ROWS, data);
    std::cout << "\nUsing explicit syntax 2:\n" << Matrix::make<ROWS>(data);
    std::cout << "\nUsing #define macro:\n" << make_matrix(data);
    std::cout << "\nUsing broken function:\n" << Matrix::makeHow(data); // HOW TO MAKE THIS WORK?
}

And this produces the output

Using explicit syntax 1:
static Matrix Matrix::make(int, ARRAY*) [with ARRAY = double [2]]: rows * cols = 3 * 2
1.1 1.2 
2.1 2.2 
3.1 3.2 

Using explicit syntax 2:
static Matrix Matrix::make(ARRAY*) [with int ROWS = 3; ARRAY = double [2]]: rows * cols = 3 * 2
1.1 1.2 
2.1 2.2 
3.1 3.2 

Using #define macro:
static Matrix Matrix::make(ARRAY*) [with int ROWS = 3; ARRAY = double [2]]: rows * cols = 3 * 2
1.1 1.2 
2.1 2.2 
3.1 3.2 

Using broken function:
static Matrix Matrix::makeHow(ARRAY*) [with ARRAY = double [2]]: rows * cols = 1 * 2
1.1 1.2 

Upvotes: 0

Views: 1915

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 120239

template<class ARRAY>
static Matrix makeHow(ARRAY & source)
//                          ^
//                          |
//                          |
//               +---------------------+
//               | this one right here |
//               +---------------------+
{
    Matrix m;
    m.rows = sizeof(source)  / sizeof(*source);
    m.cols = sizeof(*source) / sizeof(**source);
    ... etc
}

Upvotes: 1

D&#250;thomhas
D&#250;thomhas

Reputation: 10123

To pass an array as a templated item, you need to also template the dimensions:

template <typename T, std::size_t N>
void f( T (&xs)[ N ] )
{
  for (auto x : xs)
    ...
}

For a two-dimensional array you just need to add another dimension:

template <typename T, std::size_t R, std::size_t C>
void f( T (&xs)[ R ][ C ] )
{
  for (auto & row : xs)
    for (auto x : row)
      ...
}

Remember, however, that every different array you have will generate a new function in your executable. I prefer to use the templated stuff to generate inline functions to interface with a more primitive low level function that works over a 1D array but knows how to compute the indices into each row and column (because the template passes along the dimensions of the array). That way only the more primitive version creates code, and the inline template functions disappear.

Upvotes: 2

Related Questions