Question

I have a matrix class very tailored for the algorithm I need to implement. I know about Eigen but it doesn't fit my bill so I had to do my own. I have been working all along with Column Major ordering and now I have the strong use case to employ Row Major too and so I would like to specialize my template matrix class with an extra template parameter that defines the ordering but I don't want to break the existing code.

The concrete effect of this will be to use the template partial specialization to generate differently two or three key class methods e.g. operator(int i, int j) that define the different ordering, a similar concept can be done using pre-processor #define but this is not very elegant and only works compiling all in one mode or the other. This is a sketch of what I'm trying to accomplish:

enum tmatrix_order {
    COLUMN_MAJOR, ROW_MAJOR
};

/**
* Concrete definition of matrix in major column ordering that delegates most
* operations to LAPACK and BLAS functions. This implementation provides support
* for QR decomposition and fast updates. The following sequence of QR updates
* are supported:
*
* 1) [addcol -> addcol] integrates nicely with the LAPACK compact form.
* 2) [addcol -> delcol] delcols holds additional Q, and most to date R
* 3) [delcol -> delcol] delcols holds additional Q, and most to date R
* 4) [delcol -> addcol] delcols Q must also be applied to the new column
* 5) [addcol -> addrow] addrows holds additional Q, R is updated in original QR
* 6) [delcol -> addrow] addrows holds additional Q, R is updated in original QR
*/
template<typename T, tmatrix_order O = COLUMN_MAJOR>
class tmatrix {
private:
    // basic matrix structure
    T* __restrict m_data;
    int           m_rows, m_cols;     
// ...
};

template <typename T>
inline T& tmatrix<T, COLUMN_MAJOR>::operator()(int i, int j) {
  return m_data[j*m_rows + i];
}

template <typename T>
inline const T& tmatrix<T, COLUMN_MAJOR>::operator()(int i, int j) const {
  return m_data[j*m_rows + i];
}

template <typename T>
inline T& tmatrix<T, ROW_MAJOR>::operator()(int i, int j) {
  return m_data[i*m_cols + j];
}

template <typename T>
inline const T& tmatrix<T, ROW_MAJOR>::operator()(int i, int j) const {
  return m_data[i*m_cols + j];
}

but the compiler will complain of the partial specialization:

/Users/bravegag/code/fastcode_project/code/src/matrix.h:227:59: error: invalid use of incomplete type 'class tmatrix<T, (tmatrix_order)0u>'
/Users/bravegag/code/fastcode_project/code/src/matrix.h:45:7: error: declaration of 'class tmatrix<T, (tmatrix_order)0u>'

However, if I fully specialize these function like shown below it will work, but this is very inflexible:

inline double& tmatrix<double, COLUMN_MAJOR>::elem(int i, int j) {
  return m_data[j*m_rows + i];
}

Is this a language partial template specialization support issue or am I using the wrong syntax?

Was it helpful?

Solution

A possible solution:

enum tmatrix_order {
  COLUMN_MAJOR, ROW_MAJOR
};

template<typename T>
class tmatrix_base {
  protected:
    // basic matrix structure
    T* __restrict m_data;
    int           m_rows, m_cols;
};     

template<typename T, tmatrix_order O = COLUMN_MAJOR>
class tmatrix : public tmatrix_base<T>{
  public:
    tmatrix() {this->m_data = new T[5];}
    T& operator()(int i, int j) {
      return this->m_data[j*this->m_rows + i];
    }
    const T& operator()(int i, int j) const {
      return this->m_data[j*this->m_rows + i];
    }
};

template<typename T>
class tmatrix<T, ROW_MAJOR> : public tmatrix_base<T>{
  public:
    tmatrix() {this->m_data = new T[5];}
    T& operator()(int i, int j) {
      return this->m_data[i*this->m_cols + j];
    }
    const T& operator()(int i, int j) const {
      return this->m_data[i*this->m_cols + j];
    }
};

int main()
{
  tmatrix<double, COLUMN_MAJOR> m1;
  m1(0, 0);
  tmatrix<double, ROW_MAJOR> m2;
  m2(0, 0);
}

The idea is that you provide the full class definition, instead of providing only the functions in the specialized class. I think the basic problem is that the compiler does not know what that class's definition is otherwise.

Note: you need the this-> to be able to access the templated base members, otherwise lookup will fail.

Note: the constructor is just there so I could test the main function without blowing up. You will need your own (which I'm sure you already have)

OTHER TIPS

I'd keep it simple, and write it like this:

template <typename T, tmatrix_order O>
inline T& tmatrix<T, O>::operator()(int i, int j) {
  if (O == COLUMN_MAJOR) {
    return m_data[j*m_rows + i];
  } else {
    return m_data[i*m_cols + j];
  }
}

While not guaranteed by the language specification, I bet your compiler will optimize out that comparison with a compile-time constant.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top