1

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?

Aziz Shaikh
  • 16,245
  • 11
  • 62
  • 79
SkyWalker
  • 13,729
  • 18
  • 91
  • 187
  • Yes, I had to edit a few times simplifying the code for the question, I think it is correct now. T should be the template and not double since I want partial specialization and not full specialization i.e. I would like to be able to reuse the same column major versions for double as well as for float. – SkyWalker Jun 08 '12 at 13:23
  • 1
    In C++03, if I remember correctly the class method partial specialization is not allowed. The method has to be fully specialized. I think there are duplicate questions. – iammilind Jun 08 '12 at 13:28
  • @iammilind is right - this doesn't work because you can't partially specialize functions in C++, only classes - see – Orion Edwards Jan 15 '14 at 20:10

2 Answers2

3

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)

Attila
  • 28,265
  • 3
  • 46
  • 55
  • Interesting, thank you. Looks promising but I kind of dislike the branching as I think it bloats the code with as you mention duplicated constructors etc etc. – SkyWalker Jun 08 '12 at 14:16
2

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.

user52875
  • 3,020
  • 22
  • 21
  • 1
    I'm sorry but this is not "to keep it simple" this is dodging the proper use of a language feature "partial template specialization" with a hack that will most likely cause a huge performance overhead since operator(int, int) is called mega-zillion times. But thanks anyway! – SkyWalker Jun 08 '12 at 14:11
  • 2
    @Giovanni: I don't think that this will cause any performance impact whatsoever, even if called mega-zillion times. The 'if' will be optimized away. I've used this trick (to pass booleans as template parameters instead of function arguments) before to optimize branches in much more complex functions. I apologize for not having used template specialization, which is in my opinion overkill in this situation. – user52875 Jun 08 '12 at 14:25
  • An "overkill"? this is only your very subjective opinion. Using template parameter in this case is very spot-on. Checkout authoritative libraries like Eigen and boost how is done, exactly like this. – SkyWalker Jun 08 '12 at 14:46
  • how much spaghetti and suboptimal code has been written under the flag "keep it simple" ... I have more than dozens of functions that would need this "forking", will I have "if-then-else" everywhere? NO. Honestly to me "keep it simple" translates into unsophisticated and uneducated developers hacking in only what they understand because they don't know or are unable to exploit all the language features and patterns correctly. – SkyWalker Jun 09 '12 at 10:00
  • @GiovanniAzua i see no runtime "if-then-else" and see no spaghetti code. – Johannes Schaub - litb Jun 11 '12 at 11:21
  • 1
    Johannes, because you are not looking at his >dozens of functions he referred to, those that would need the extra conditional added to every single one of them. That's his unwanted spaghetti. Not only is it the opposite of "Don't Repeat Yourself", it may not even qualify as "Keep It Simple" in that case. – Sz. Nov 30 '15 at 02:25
  • However, the trick itself in the answer is a nice a one, indeed. – Sz. Nov 30 '15 at 02:30