Reputation: 62906
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
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
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