This is my code and the error which comes is
no matching function for call to to_reduced_row_echelon_form(float[((unsigned int)((int)r))][((unsigned int)((int)c))])'
#include <algorithm> // for std::swap
#include <cstddef>
#include <cassert>
// Matrix traits: This describes how a matrix is accessed. By
// externalizing this information into a traits class, the same code
// can be used both with native arrays and matrix classes. To use the
// default implementation of the traits class, a matrix type has to
// provide the following definitions as members:
//
// * typedef ... index_type;
// - The type used for indexing (e.g. size_t)
// * typedef ... value_type;
// - The element type of the matrix (e.g. double)
// * index_type min_row() const;
// - returns the minimal allowed row index
// * index_type max_row() const;
// - returns the maximal allowed row index
// * index_type min_column() const;
// - returns the minimal allowed column index
// * index_type max_column() const;
// - returns the maximal allowed column index
// * value_type& operator()(index_type i, index_type k)
// - returns a reference to the element i,k, where
// min_row() <= i <= max_row()
// min_column() <= k <= max_column()
// * value_type operator()(index_type i, index_type k) const
// - returns the value of element i,k
//
// Note that the functions are all inline and simple, so the compiler
// should completely optimize them away.
template<typename MatrixType> struct matrix_traits
{
typedef typename MatrixType::index_type index_type;
typedef typename MatrixType::value_type value_type;
static index_type min_row(MatrixType const& A)
{ return A.min_row(); }
static index_type max_row(MatrixType const& A)
{ return A.max_row(); }
static index_type min_column(MatrixType const& A)
{ return A.min_column(); }
static index_type max_column(MatrixType const& A)
{ return A.max_column(); }
static value_type& element(MatrixType& A, index_type i, index_type k)
{ return A(i,k); }
static value_type element(MatrixType const& A, index_type i, index_type k)
{ return A(i,k); }
};
// specialization of the matrix traits for built-in two-dimensional
// arrays
template<typename T, std::size_t rows, std::size_t columns>
struct matrix_traits<T[rows][columns]>
{
typedef std::size_t index_type;
typedef T value_type;
static index_type min_row(T const (&)[rows][columns])
{ return 0; }
static index_type max_row(T const (&)[rows][columns])
{ return rows-1; }
static index_type min_column(T const (&)[rows][columns])
{ return 0; }
static index_type max_column(T const (&)[rows][columns])
{ return columns-1; }
static value_type& element(T (&A)[rows][columns],
index_type i, index_type k)
{ return A[i][k]; }
static value_type element(T const (&A)[rows][columns],
index_type i, index_type k)
{ return A[i][k]; }
};
// Swap rows i and k of a matrix A
// Note that due to the reference, both dimensions are preserved for
// built-in arrays
template<typename MatrixType>
void swap_rows(MatrixType& A,
typename matrix_traits<MatrixType>::index_type i,
typename matrix_traits<MatrixType>::index_type k)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
// check indices
assert(mt.min_row(A) <= i);
assert(i <= mt.max_row(A));
assert(mt.min_row(A) <= k);
assert(k <= mt.max_row(A));
for (index_type col = mt.min_column(A); col <= mt.max_column(A); ++col)
std::swap(mt.element(A, i, col), mt.element(A, k, col));
}
// divide row i of matrix A by v
template<typename MatrixType>
void divide_row(MatrixType& A,
typename matrix_traits<MatrixType>::index_type i,
typename matrix_traits<MatrixType>::value_type v)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
assert(mt.min_row(A) <= i);
assert(i <= mt.max_row(A));
assert(v != 0);
for (index_type col = mt.min_column(A); col <= mt.max_column(A); ++col)
mt.element(A, i, col) /= v;
}
// in matrix A, add v times row k to row i
template<typename MatrixType>
void add_multiple_row(MatrixType& A,
typename matrix_traits<MatrixType>::index_type i,
typename matrix_traits<MatrixType>::index_type k,
typename matrix_traits<MatrixType>::value_type v)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
assert(mt.min_row(A) <= i);
assert(i <= mt.max_row(A));
assert(mt.min_row(A) <= k);
assert(k <= mt.max_row(A));
for (index_type col = mt.min_column(A); col <= mt.max_column(A); ++col)
mt.element(A, i, col) += v * mt.element(A, k, col);
}
// convert A to reduced row echelon form
template<typename MatrixType>
void to_reduced_row_echelon_form(MatrixType& A)
{
matrix_traits<MatrixType> mt;
typedef typename matrix_traits<MatrixType>::index_type index_type;
index_type lead = mt.min_row(A);
for (index_type row = mt.min_row(A); row <= mt.max_row(A); ++row)
{
if (lead > mt.max_column(A))
return;
index_type i = row;
while (mt.element(A, i, lead) == 0)
{
++i;
if (i > mt.max_row(A))
{
i = row;
++lead;
if (lead > mt.max_column(A))
return;
}
}
swap_rows(A, i, row);
divide_row(A, row, mt.element(A, row, lead));
for (i = mt.min_row(A); i <= mt.max_row(A); ++i)
{
if (i != row)
add_multiple_row(A, i, row, -mt.element(A, i, lead));
}
}
}
// test code
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int r=0,c=0,i,j;
cout << "Enter no of rows of the matrix";
cin >> r;
cout << "Enter no of columns of the matrix";
cin >> c;
float l[r][c];
int p = 0, q = 0;
while (p < r) {
while (q < c) {
cin >> l[p][q];
q = q + 1;
}
p = p + 1;
q = 0;
}
to_reduced_row_echelon_form(l);
for (int i = 0; i < r; ++i)
{
for (int j = 0; j < c; ++j)
std::cout << l[i][j] << '\t';
std::cout << "\n";
}
system("pause");
return EXIT_SUCCESS;
}
calling the function whose code i took from https://rosettacode.org/wiki/Reduced_row_echelon_form
Variable length arrays (VLAs) like float l[r][c]; are not part of standard C++, so any attempt to reason when they might or might not work is fruitless.
The requirements of the type passed to to_reduced_row_echelon_form are clearly described in the comment starting 'Matrix traits'. You need to define a class with those features. Something like this
class MyMatrix
{
public:
MyMatrix(int r, int c) : r(r), c(c), l(r, std::vector<int>(c)) {}
std::vector<int>& operator[](int r) { return l[r]; }
const std::vector<int>& operator[](int r) const { return l[r]; }
using index_type = int;
using value_type = int;
value_type& operator()(index_type i, index_type j) { return l[i][j]; }
value_type operator()(index_type i, index_type j) const { return l[i][j]; }
index_type min_row() const { return 0; }
index_type max_row() const { return r - 1; }
index_type min_column() const { return 0; }
index_type max_column() const { return c - 1; }
private:
int r, c;
std::vector<std::vector<int>> l;
};
Not the most elegant or efficient code, but it does compile and hopefully works.
Then use it like this
MyMatrix l(r, c);
...
to_reduced_row_echelon_form(l);
Related
I'm continually irritated that matrix dimensions are accessed with getters. Sure one can have public rows and cols but these should be const so that users of the Matrix class can't directly modify them. This is a natural way of coding but, because using const members is regarded, apparently incorrectly, as not possible without UB, results in people using non-const fields when they really should be consts that are mutable when needed such as resizing, or assigning.
What I'd like is something that can be used like this:
Matrix2D<double> a(2, 3);
int index{};
for (int i = 0; i < a.rows; i++)
for (int ii = 0; ii < a.cols; ii++)
a(i, ii) = index++;
but where a.rows=5; isn't compilable because it's const. And it would be great if the class can be included in vectors and other containers.
Now comes: Implement C++20's P0784 (More constexpr containers)
https://reviews.llvm.org/D68364?id=222943
It should be doable without casts or UB and can even be done when evaluating const expressions. I believe c++20 has made it doable by providing the functions std::destroy_at and std::construct_at. I have attached an answer using c++20 that appears to show it is indeed possible, and easily done, to provide const member objects in classes without an undue burden.
So the question is do the new constexpr functions in c++20, which provide the ability to destroy and then construct an object with different consts valid? It certainly appears so and it passes the consexpr lack of UB test.
If you want to have const properties you can try this :
class Matrix {
public:
const int rows;
const int cols;
Matrix(int _rows, int _cols) : rows(_rows), cols(_cols)
{
}
};
The rows and cols properties of a Matrix instance are initialized once in the constructor.
Well, here's my pass at using const rows and cols in a 2D matrix. It passes UB tests and can be used in collections like vector w/o weird restrictions. It's a partial implementation. No bounds checking and such but to provide a possible approach to the problem.
matrix2d.h
#pragma once
#include <memory>
#include <vector>
#include <initializer_list>
#include <stdexcept>
#include <array>
template<class T>
class Matrix2D
{
std::vector<T> v;
public:
const size_t rows;
const size_t cols;
constexpr Matrix2D(const Matrix2D& m) = default;
constexpr Matrix2D(Matrix2D&& m) noexcept = default;
explicit constexpr Matrix2D(size_t a_rows=0, size_t a_cols=0) : v(a_rows* a_cols), rows(a_rows), cols(a_cols) {}
constexpr Matrix2D(std::initializer_list<std::initializer_list<T>> list) : rows(list.size()), cols((list.begin())->size())
{
for (auto& p1 : list)
for (auto p2 : p1)
v.push_back(p2);
}
constexpr Matrix2D& operator=(const Matrix2D& mat)
{
std::vector<T> tmp_v = mat.v;
std::construct_at(&this->rows, mat.rows);
std::construct_at(&this->cols, mat.cols);
this->v.swap(tmp_v);
return *this;
}
constexpr Matrix2D& operator=(Matrix2D&& mat) noexcept
{
std::construct_at(&this->rows, mat.rows);
std::construct_at(&this->cols, mat.cols);
this->v.swap(mat.v);
return *this;
}
// user methods
constexpr T* operator[](size_t row) { // alternate bracket indexing
return &v[row * cols];
};
constexpr T& operator()(size_t row, size_t col)
{
return v[row * cols + col];
}
constexpr const T& operator()(size_t row, size_t col) const
{
return v[row * cols + col];
}
constexpr Matrix2D operator+(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols) throw std::range_error("cols and rows must be the same");
Matrix2D ret = *this;
for (size_t i = 0; i < ret.v.size(); i++)
ret.v[i] += v1.v[i];
return ret;
}
constexpr Matrix2D operator-(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols) throw std::range_error("cols and rows must be the same");
Matrix2D ret = *this;
for (size_t i = 0; i < ret.v.size(); i++)
ret.v[i] -= v1.v[i];
return ret;
}
constexpr Matrix2D operator*(Matrix2D const& v1) const
{
if (cols != v1.rows) throw std::range_error("cols of first must == rows of second");
Matrix2D v2 = v1.transpose();
Matrix2D ret(rows, v1.cols);
for (size_t row = 0; row < rows; row++)
for (size_t col = 0; col < v1.cols; col++)
{
T tmp{};
for (size_t row_col = 0; row_col < cols; row_col++)
tmp += this->operator()(row, row_col) * v2(col, row_col);
ret(row, col) = tmp;
}
return ret;
}
constexpr Matrix2D transpose() const
{
Matrix2D ret(cols, rows);
for (size_t r = 0; r < rows; r++)
for (size_t c = 0; c < cols; c++)
ret(c, r) = this->operator()(r, c);
return ret;
}
// Adds rows to end
constexpr Matrix2D add_rows(const Matrix2D& new_rows)
{
if (cols != new_rows.cols) throw std::range_error("cols cols must match");
Matrix2D ret = *this;
std::construct_at(&ret.rows, rows + new_rows.rows);
ret.v.insert(ret.v.end(), new_rows.v.begin(), new_rows.v.end());
return ret;
}
constexpr Matrix2D add_cols(const Matrix2D& new_cols)
{
if (rows != new_cols.rows) throw std::range_error("rows must match");
Matrix2D ret(rows, cols + new_cols.cols);
for (size_t row = 0; row < rows; row++)
{
for (size_t col = 0; col < cols; col++)
ret(row, col) = this->operator()(row, col);
for (size_t col = cols; col < ret.cols; col++)
ret(row, col) = new_cols(row, col - cols);
}
return ret;
}
constexpr bool operator==(Matrix2D const& v1) const
{
if (rows != v1.rows || cols != v1.cols || v.size() != v1.v.size())
return false;
for (size_t i = 0; i < v.size(); i++)
if (v[i] != v1.v[i])
return false;
return true;
}
constexpr bool operator!=(Matrix2D const& v1) const
{
return !(*this == v1);
}
// friends
template <size_t a_rows, size_t a_cols>
friend constexpr auto make_std_array(const Matrix2D& v)
{
if (a_rows != v.rows || a_cols != v.cols) throw std::range_error("template cols and rows must be the same");
std::array<std::array<T, a_cols>, a_rows> ret{};
for (size_t r = 0; r < a_rows; r++)
for (size_t c = 0; c < a_cols; c++)
ret[r][c] = v(r, c);
return ret;
}
};
Source.cpp
#include <vector>
#include <algorithm>
#include <iostream>
#include "matrix2d.h"
constexpr std::array<std::array<int, 3>, 4> foo()
{
Matrix2D<double> a(2, 3), b;
Matrix2D d(a);
int index{};
for (int i = 0; i < a.rows; i++)
for (int ii = 0; ii < a.cols; ii++)
a(i, ii) = index++;
b = a + a;
Matrix2D<int> z1{ {1},{3},{5} };
z1 = z1.add_cols({{2}, {4}, {6}});
z1 = z1.add_rows({{7,8}});
// z1 is now {{1,2},{3,4},{5,6},{7,8}}
Matrix2D<int> z2{ {1,2,3},{4,5,6} };
Matrix2D<int> z3 = z1 * z2; // z3: 4 rows, 3 cols
// from separate math program product of z1 and z2
Matrix2D<int> ref{ {9,12,15},{19,26,33},{29,40,51},{39,54,69} };
// test transpose
Matrix2D<int> tmp = ref.transpose(); // tmp: now 3 rows, 4 cols
if (ref == tmp) throw; // verify not equal
tmp = tmp.transpose(); // now ref==tmp==z3
if (ref != tmp || ref != z3) throw;
// Check using vector of matrixes, verify sort on row size works
std::vector<Matrix2D<int>> v;
v.push_back(z1);
v.push_back(z2);
v.push_back(z1);
v.push_back(z2);
std::sort(v.begin(), v.end(), [](auto const& m1, auto const& m2) {return m1.rows < m2.rows; });
for (size_t i = 0; i < v.size() - 1; i++)
if (v[i].rows > v[i + 1].rows) throw;
// return constexpr of 2D std::array
std::array<std::array<int, 3>, 4> array = make_std_array<4,3>(ref); // ref must have 4 rows, 3 cols
return array;
}
int main()
{
auto print = [](auto& a) {
for (auto& x : a)
{
for (auto y : x)
std::cout << y << " ";
std::cout << '\n';
}
std::cout << '\n';
};
// run time
auto zz = foo();
print(zz);
// validating no UB in Matrix2D;
// and creating a nested, std::array initialized at compile time
//constexpr std::array<std::array<int, 3>, 4> x = foo();
constexpr std::array<std::array<int, 3>, 4> x = foo(); // compile time
print(x);
return 0;
}
I have a matrix A and I need to pass a pointer to a function to access the elements of a given column, here is a part of the code, where PtrColumn should be a pointer to the vector representing the column j of A. Is there a way to get this pointer without calculating the transpose of the matrix A.
vector<vector<double>> A;
int l=4,c=5;
A.reserve(l)
for(size_t i=0;i<l;++i)
{
vector<double> row;
row.reserve(c)
for(size_t j=0;j<c;++j)
row.puch_back(j);
A.puch_back(row);
}
////// some part of the code/////
for(size_t j=0;j<c;++j)
function(PtrColumn) //PtrColumn is a pointer to a vector, and my vector is the column j of A
I would use a matrix class and a wrapper class for the columns:
#include <iostream>
#include <vector>
class Matrix;
class Column {
Matrix *A;
std::size_t column;
public:
Column(Matrix &A, std::size_t column);
double &operator[](std::size_t row);
std::size_t size() const;
};
class Matrix {
std::size_t columns;
std::size_t rows;
std::vector<double> A;
public:
Matrix(std::size_t l, std::size_t r) : columns(r), rows(l), A(l * r) {}
double &operator()(std::size_t i, std::size_t j) { return A[i * columns + j]; }
std::size_t getColumns() const { return columns; }
std::size_t getRows() const { return rows; }
Column getColumn(std::size_t column) { return Column(*this, column); }
};
Column::Column(Matrix &A, std::size_t column) : A(&A), column(column) {}
double &Column::operator[](std::size_t row) { return (*A)(row, column); }
std::size_t Column::size() const { return A->getRows(); }
void function(Column col) {
std::cout << col[2] << '\n';
std::cout << col.size() << '\n';
}
int main() {
int l = 4, c = 5;
Matrix A(l, c);
for(size_t i=0;i<l;++i)
for(size_t j=0;j<c;++j)
A(i, j) = j;
////// some part of the code/////
for(size_t j = 0; j < c; ++j)
function(A.getColumn(j)); //PtrColumn is a pointer to a vector, and my vector is the column j of A
}
Here is a code without matrix class
#include <iostream>
#include <vector>
class Column {
std::vector<std::vector<double>> *A;
std::size_t column;
public:
Column(std::vector<std::vector<double>> &A, std::size_t column) : A(&A), column(column) {}
double &operator[](std::size_t row) { return (*A)[row][column]; }
std::size_t size() const { return A->size(); }
};
void function(Column col) {
std::cout << col[2] << '\n';
std::cout << col.size() << '\n';
}
int main() {
std::vector<std::vector<double>> A;
int l=4,c=5;
A.reserve(l);
for(size_t i=0;i<l;++i) {
std::vector<double> row;
row.reserve(c);
for(size_t j=0;j<c;++j)
row.push_back(j);
A.push_back(row);
}
////// some part of the code/////
for(size_t j=0;j<c;++j)
function(Column(A, j)); //PtrColumn is a pointer to a vector, and my vector is the column j of A
}
I'm working on a project which requires me to write my own matrix class implementation. I decided to implement the matrix class as a template to provide compile-time error-checking in the following way:
template<size_t N, size_t M> // N × M matrix
class Matrix
{
// implementation...
};
I managed to implement basic operations such as addition/subtraction, transpose and multiplication. However, I'm having trouble implementing the determinant. I was thinking of implementing it recursively using the Laplace expansion, so I must first implement a way to calculate the i,j minor of a matrix. The problem is, the minor of an N × N matrix is an (N-1) × (N-1) matrix. The following does not compile: (error message is Error C2059 syntax error: '<', pointing to the first line in the function)
template<size_t N>
Matrix<N-1, N-1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
Matrix<N-1, N-1> minor;
// calculate i,j minor
return minor
}
How could I go around this and calculate the minor, while keeping the templated form of the class?
EDIT: I was asked to provide a working example. Here is the relevant part of my code, I tried to keep it as minimal as possible. My Matrix class uses a Vector class, which I also wrote myself. I removed any unrelated code, and also changed any error-checks to asserts, as the actual code throws an exception class, which again was written by me.
Here is the Vector.h file:
#pragma once
#include <vector>
#include <cassert>
template<size_t S>
class Vector
{
public:
Vector(double fInitialValue = 0.0);
Vector(std::initializer_list<double> il);
// indexing range is 0...S-1
double operator[](size_t i) const;
double& operator[](size_t i);
private:
std::vector<double> m_vec;
};
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
template<size_t S>
Vector<S>::Vector(double fInitialValue) : m_vec(S, fInitialValue)
{
}
template<size_t S>
Vector<S>::Vector(std::initializer_list<double> il) : m_vec(il)
{
assert(il.size() == S);
}
template<size_t S>
double Vector<S>::operator[](size_t i) const
{
return m_vec[i];
}
template<size_t S>
double& Vector<S>::operator[](size_t i)
{
return m_vec[i];
}
And here is the Matrix.h file:
#pragma once
#include "Vector.h"
template<size_t N, size_t M>
class Matrix
{
public:
Matrix(double fInitialValue = 0.0);
Matrix(std::initializer_list<Vector<M>> il);
// indexing range is 0...N-1, 0...M-1
Vector<M> operator[](int i) const;
Vector<M>& operator[](int i);
double Determinant() const;
private:
std::vector<Vector<M>> m_mat; // a collection of row vectors
template <size_t N>
friend Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j);
};
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
template<size_t N, size_t M>
Matrix<N, M>::Matrix(double fInitialValue)
: m_mat(N, Vector<M>(fInitialValue)) {}
template<size_t N, size_t M>
Matrix<N, M>::Matrix(std::initializer_list<Vector<M>> il) : m_mat(il)
{
assert(il.size() == N);
}
template<size_t N, size_t M>
Vector<M> Matrix<N, M>::operator[](int i) const
{
return m_mat[i];
}
template<size_t N, size_t M>
Vector<M>& Matrix<N, M>::operator[](int i)
{
return m_mat[i];
}
template<size_t N, size_t M>
double Matrix<N, M>::Determinant() const
{
assert(N == M);
if (N == 2) {
return m_mat[0][0] * m_mat[1][1] - m_mat[0][1] * m_mat[1][0];
}
double det = 0;
for (size_t j = 0; j < N; j++) {
if (j % 2) {
det += m_mat[0][j] * Minor((*this), 0, j).Determinant();
}
else {
det -= m_mat[0][j] * Minor((*this), 0, j).Determinant();
}
}
return det;
}
template <size_t N>
Matrix<N - 1, N - 1> Minor(const Matrix<N, N>& mat, size_t i, size_t j)
{
Matrix<N - 1, N - 1> minor;
for (size_t n = 0; n < i; n++) {
for (size_t m = 0; m < j; m++) {
minor[n][m] = mat[n][m];
}
}
for (size_t n = i + 1; n < N; n++) {
for (size_t m = 0; m < j; m++) {
minor[n - 1][m] = mat[n][m];
}
}
for (size_t n = 0; n < i; n++) {
for (size_t m = j + 1; m < N; m++) {
minor[n][m - 1] = mat[n][m];
}
}
for (size_t n = i + 1; n < N; n++) {
for (size_t m = j + 1; m < N; m++) {
minor[n - 1][m - 1] = mat[n][m];
}
}
return minor;
}
Compiling these along with a simple main.cpp file:
#include "Matrix.h"
#include <iostream>
int main() {
Matrix<3, 3> mat = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
std::cout << mat.Determinant();
}
produces - Error C2760 syntax error: unexpected token '<', expected 'declaration' ...\matrix.h 67
EDIT2: Apparently I had written the template arguments as <N - 1><N - 1> instead of <N -1, N-1> in the implementation of the Minor function. Changing that fixed the error, but introduced a new one - compilation hangs, and after a minute or so I get Error C1060 compiler is out of heap space ...\matrix.h 65
I am trying to use class templates to matrices. But I have run into a problem with matrix multiplication.
template<typename T, unsigned int N, unsigned int M>
class Matrix : public MatrixBase<Matrix<T, N, M>, T, N, M> {
template<unsigned int K>
friend Matrix<T, N, K> operator*(const Matrix<T, N, M>& m1, const Matrix<T, M, K>& m2) {
Matrix<T, N, K> ret;
for (unsigned int n = 0; n != N; n++) {
for (unsigned int k = 0; k != K; k++) {
ret.i[n][k] = 0;
for (unsigned int m = 0; m != M; m++) {
ret.i[n][k] += m1.i[n][m]*m2.i[m][k];
}
}
}
return ret;
}
};
When it then comes to multiplying two mat4's(4x4 matrices), like so:
m_model = (m_view*m_model);
It gives the error Invalid operands to binary expression ('mat4' (aka 'Matrix<float, 4, 4>') and 'mat4'). Having had a look online I can see this is not the intended use of function templates, as you have to assign on call the template arguments. Is there a way around this similar to what I first intended, i.e. automatic assignment of the template argument based on the second argument of the function?
Here are the definitions of MatrixBase and Matrix(aka mat4) respectively:
MatrixBase
template<typename T , unsigned int M>
struct ComponentColumn{
T& operator[](int m) {
return i[m];
}
const T& operator[](int m) const {
return i[m];
}
T i[M];
};
//-----------MATRIXBASE-----------
template <typename ChildT, typename T, unsigned int N, unsigned int M>
class MatrixBase {
public:
MatrixBase() {}
MatrixBase<ChildT, T, N, M> operator*=(const MatrixBase<ChildT, T, N, M>& m1) {
MatrixBase<ChildT, T, N, M> ret;
for (unsigned int n = 0; n != N; n++) {
for (int k = 0; k != M; k++) {
ret.i[n][k] = 0;
for (unsigned int m = 0; m != M; m++) {
ret.i[n][k] += (*this).i[n][m]*m1.i[m][k];
}
}
}
*this = ret;
return ret;
}
MatrixBase<ChildT, T, N, M> operator+(const MatrixBase<ChildT, T, N, M>& m1) {
MatrixBase<ChildT, T, N, M> ret;
for (int n = 0; n != N; n++) {
for (int m = 0; m != M; m++) {
ret.i[n][m] = i[n][m];
}
}
return ret;
}
ComponentColumn<T, M>& operator[](int n) {
return this->i[n];
}
const ComponentColumn<T, M>& operator[](int n) const {
return this->i[n];
}
explicit operator T*() {
return &(*this)[0][0];
}
protected:
ComponentColumn<T, M> i[N];
};
mat4
template<typename T>
class Matrix<T, 4, 4> : public MatrixBase<Matrix<T, 4, 4>, T, 4, 4> {
public:
Matrix<T, 4, 4>() {
for (unsigned int n = 0; n != 4; n++) {
for (unsigned int m = 0; m != 4; m++) {
if (n == m) {
(*this)[n][m] = 1;
} else {
(*this)[n][m] = 0;
}
}
}
}
Matrix<T, 4, 4>(const Matrix<T, 3, 3>& m) {
(*this)[0][0] = m[0][0]; (*this)[1][0] = m[1][0]; (*this)[2][0] = m[2][0]; (*this)[3][0] = 0;
(*this)[0][1] = m[0][1]; (*this)[1][1] = m[1][1]; (*this)[2][1] = m[2][1]; (*this)[3][1] = 0;
(*this)[0][2] = m[0][2]; (*this)[1][2] = m[1][2]; (*this)[2][2] = m[2][2]; (*this)[3][2] = 0;
(*this)[0][3] = 0; (*this)[1][3] = 0; (*this)[2][3] = 0; (*this)[3][3] = 1;
}
static Matrix<T, 4, 4> Translate(T x, T y, T z);
static Matrix<T, 4, 4> Translate(const vec3& v);
static Matrix<T, 4, 4> Scale(T s);
static Matrix<T, 4, 4> Rotate(T degrees);
static Matrix<T, 4, 4> Frustum(T left, T right, T bottom, T top, T near, T far);
explicit operator Matrix<T, 3, 3>() {
Matrix<T, 3, 3> ret;
for (int n = 0; n != 3; n++) {
for (int m = 0; m != 3; m++) {
ret[n][m] = (*this)[n][m];
}
}
return ret;
}
Matrix<T, 4, 4> Transpose() {
Matrix<T, 4, 4> ret = Matrix<T, 4, 4>();
for (unsigned int n = 0; n != 4; n++) {
for (unsigned int m = 0; m != 4; m++) {
ret.i[n][m] = this->i[m][n];
}
}
*this = ret;
return ret;
}
Matrix<T, 4, 4> Inverse();
};
Unless you are doing this for practice, which would be a good exercise, I would just use an existing linear algebra library which implements matrix vector operations. Such as Armadillo: http://arma.sourceforge.net/
Not an answer, but to share what worked for me and assure the correctness of the method of defining the multiplication operator:
template<typename T, unsigned int N, unsigned int M>
class Matrix {
public:
template<unsigned int K>
friend Matrix<T, N, K> operator*(const Matrix<T, N, M>& m1, const Matrix<T, M, K>& m2) {
Matrix<T, N, K> ret;
for (unsigned int n = 0; n != N; n++) {
for (unsigned int k = 0; k != K; k++) {
ret.i[n][k] = 0;
for (unsigned int m = 0; m != M; m++) {
ret.i[n][k] += m1.i[n][m] * m2.i[m][k];
}
}
}
return ret;
}
array<array<T, M>, N> i;
};
int main() {
Matrix<float, 4, 6> m1; Matrix<float, 6, 10> m2;
auto m3 = (m1 * m2);
cout << m3.i[0][0] << m3.i[3][9] << "\n";
system("pause");
}
In order to learn about C++ templates I am writing a simple Matrix class. So far it has been working well, but I want to add the ability to slice the Matrix to extract a sub-matrix. I am struggling to figure out how to define the size of the return matrix. I have tried the following:
#include <cstdint>
#include <array>
#include <initializer_list>
template<typename T, std::size_t M, std::size_t N>
class Matrix
{
public:
Matrix(void): m_data{0} {}
Matrix(const std::initializer_list<std::initializer_list<T>> m)
{
for(auto i = m.begin(); i != m.end(); i++)
{
for(auto j = i->begin(); j != i->end(); j++)
{
(*this)(i - m.begin(), j - i->begin()) = *j;
}
}
}
T& operator()(const std::size_t i, const std::size_t j)
{
return m_data.at(i + j * N);
}
const T& operator()(const std::size_t i, const std::size_t j) const
{
return m_data.at(i + j * N);
}
template<std::size_t X, std::size_t Y>
Matrix<T,X,Y> slice(const std::size_t iStart, const std::size_t iEnd, const std::size_t jStart, const std::size_t jEnd)
{
Matrix<T,iEnd-iStart+1,jEnd-jStart+1> result;
for(std::size_t i = iStart; i <= iEnd; i++)
{
for(std::size_t j = jStart; j <= jEnd; j++)
{
result(i - iStart, j - jStart) = (*this)(i,j);
}
}
return result;
}
};
int main(void)
{
Matrix<double,3,3> m1 = {{1,2,3},{4,5,6},{7,8,9}};
Matrix<double,2,2> m2 = m1.slice(0,1,0,1);
return 0;
}
But I just get an error saying that 'iEnd' is not a constant expression. What would be the correct way to go about this?
You cannot use function parameters to instantiate templates. You need to pass in the sizes as template arguments to slice:
template<std::size_t iStart, std::size_t iEnd, std::size_t jStart, std::size_t jEnd,
std::size_t I = iEnd-iStart+1, std::size_t J = jEnd-jStart+1>
Matrix<T,I,J> slice()
{
Matrix<T,I,J> result;
///...
return result;
}