I have a template matrix class with some typical member functions such as inverse
, determinant
, operator*
, etc.. And I want to reuse the code for these member functions in template realizations for both fixed- and dynamic size matrices.
Is this possible? and if yes, how?
In the code below, like Eigen, I use "-1
" for dynamic dimensions. Yes I know I could use a library for this, but for my application, that is not feasible. The standard functionality is not possible due to the nature of the application (CUDA)
Is it possible to make a template class with different member variable size depending on the template parameters? For instance when Dynamic_Rows = -1
and Dynamic_Cols = -1
, then the data is just T **data
, but otherwise its T data[Rows][Cols]
.
As of now, I have one template class for dynamic size matrices ("minimal" example below, note that the code is subject to errors/mistakes because I am relatively fresh in "advanced" class templating).
But I want to have a fixed size data member variable in the case of a fixed size matrix instantiation.
template<class T, int Dynamic_Rows, int Dynamic_Cols>
class CMatrix<T, -1, -1>
{
private:
size_t n_rows, n_cols;
T** data;
void allocate_data();
void deallocate_data();
public:
CMatrix(const size_t n_rows, const size_t n_cols);
CMatrix(const CMatrix& other);
~CMatrix();
CMatrix& operator=(const CMatrix& rhs);
CMatrix exp() const;
};
with for instance the code for the exp()
function below as
template <class T, int Dynamic_Rows, int Dynamic_Cols>
CMatrix<T, -1, -1> CMatrix<T, -1, -1>::exp() const
{
CMatrix<T, -1, -1> result(n_rows, n_cols);
for (size_t i = 0; i < n_rows; i++)
{
for (size_t j = 0; j < n_cols; j++)
{
result.data[i][j] = exp(result.data[i][j]);
}
}
return result;
}
The only thing I can think of now to allow for both dynamic and fixed-size matrices is to basically implement another template of the class as
template<class T, size_t Rows, size_t Cols>
class CMatrix<T, Rows, Cols>
{
private:
size_t n_rows = Rows, n_cols = Cols;
T data[Rows][Cols];
public:
CMatrix() {}
CMatrix(const CMatrix& other);
CMatrix& operator=(const CMatrix& rhs);
CMatrix exp() const;
};
using a variadic template
template<class T, int...> class CMatrix;
but then that would just duplicate the code for most of my member functions!
This is a very generic way to do such specializations using CRTP. It doesn;t require that there is a simple single condition that can be used to select an implementation of a data member, and leave the rest unchanged.
template <class T, class Impl> class MatrixBase {
// data is NOT defined here
Impl exp() const
{
Impl& self = static_cast<Impl&>(*this); // <-- this is magic
Impl result = self; // <-- this is magic
for (size_t i = 0; i < n_rows; i++)
{
for (size_t j = 0; j < n_cols; j++)
{
result.data[i][j] = exp(result.data[i][j]); // <-- data is defined in Impl
}
}
return result;
}
// other functions that do not depend on actual type of data
};
template <class T>
class DynamicMatrix : public MatrixBase<T, DynamicMatrix<T>> {
T** data;
// define constructors etc here
};
template <class T, int Rows, int Cols>
class StaticMatrix : public MatrixBase<T, StaticMatrix<T, Rows, Cols>> {
T[Rows][Cols] data;
// define constructors etc here
};
Now that you have both StaticMatrix
and DynamicMatrix
, you can unify them into a single alias template if you wish.
template <class T, int Rows, int Cols>
using CMatrix = std::conditional_t <(Rows >= 0 && Cols >= 0),
StaticMatrix<T, Rows, Cols>,
DynamicMatrix<T>
>;