A_Man
A_Man

Reputation: 131

CRTP: Determine derived class type in base class function to allow code reuse

Lets say I have a CRTP template class for matrices

template<class T, class Derived>
class MatrixBase{
private:
    //...

public:
    Derived some_function(const Derived &other){
          Derived& self = (Derived&)*this; // In my application I cant use static_cast.

          // Some calculations..., which will determine the below 
          // defined variables "some_number_of_rows" and "some_number_of_cols"

          // If Derived = DynamicMatrix<T>, then result should be declared as:
          DynamicMatrix<T> result(some_number_of_rows, some_number_of_cols);

          // while if Derived = StaticMatrix<T, Rows, Cols>, then result should be declared as:
          StaticMatrix<T, some_number_of_rows, some_number_of_cols> result;

          // Perform some more calculations...

          return result;
    }
};

template<class T>
class DynamicMatrix{
private:
     size_t n_rows, n_cols;
     T *data;
public:
     DynamicMatrix(const size_t n_rows, const size_t n_cols);
     // ...
};

template<class T, int Rows, int Cols>
class StaticMatrix{
private:
     size_t n_rows = Rows, n_cols = Cols;
     T data[Rows * Cols];
public:
     StaticMatrix() {}
     // ...
};

How can I check for the Derived class type in MatrixBase::some_function(const Derived &other) to use this base function in both derived classes?, preventing the need for redefinition/override/code duplicates in these classes separately. In this case it is basically only the declaration of the result matrix that requires me to check for the Derived class type, as the declaration is different depending on if its a fixed-size or dynamic matrix. Other solutions than type-checking are also welcome.

NOTE: I cannot use standard functionality due to the nature of my application.

EDIT: The some_number_of_rows and some_number_of_cols in the example function are in general not constexpr, as they are dependent on the function and size of the object matrix. For instance for a transpose function, the result would have to have dimension <Derived.n_cols, Derived.n_rows, and in the case of a column-wise dot product, <1, Derived.n_cols>.

Upvotes: 3

Views: 214

Answers (2)

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

Reputation: 120229

This is a challenging problem. Basically, some_number_of_rows and some_number_of_cols must be constexpr in case of StaticMatrix, and cannot be constexpr in case of DynamicMatrix.

One solution is to delegate new matrix creation to the derived class. It will do the size computation as constexpr or not constexpr, whichever works for it.

Another one is to make the size computation in the CRTP class twice, once as constexpr and once as not constexpr, and pass both results to the derived object creation function: constexpr is passed as a template argument, and non-constexpr as a regular argument. The creation function is specialized for both static and dynamic matrices. The static version ignores the non-constexpr parameters, and vice versa.

Upvotes: 1

Jarod42
Jarod42

Reputation: 218323

As I understand, you might add those factory method inside Derived:

If some_number_of_rows, some_number_of_cols can be constexpr (to satisfy the more constrained Derived) in both case, you might do something like:

template<class T>
class DynamicMatrix
{
  // ...
  template <std::size_t Row, std::size_t Col>
  static DynamicMatrix<T> Create() { return DynamicMatrix(Row, Col); }
};

template <class T, int Rows, int Cols>
class StaticMatrix{
  // ...
  template <std::size_t Row, std::size_t Col>
  static StaticMatrix<T, Row, Col> Create() { return {}; }
};

With

auto result = Derived::Create<some_number_of_rows, some_number_of_cols>();

else you have to move that computation into Derived functions too:

template<class T>
class DynamicMatrix
{
  // ...
  DynamicMatrix<T> CreateEmptyTransposed() const { return DynamicMatrix(n_cols, n_rows); }
};

template <class T, int Rows, int Cols>
class StaticMatrix{
  // ...
  StaticMatrix<T, Cols, Row> CreateEmptyTransposed() const { return {}; }
};

With

auto result = self.CreateEmptyTransposed();

Upvotes: 0

Related Questions