Search code examples
c++operator-overloadingostream

Overloading ostream operator in custom class


I am trying to play with CRTP with a custom matrix class. Now I am trying to overloading the ostream operator, follow https://msdn.microsoft.com/en-us/library/1z2f6c2k.aspx.

However, everything seems to be compiling but the program never exists and prints nothing on the screen. I am scratching my head as what is happening.

Anyway, this is the relevant code (Sorry it is a bit lengthy)

#ifndef EXPERIMENT_POINTERMATRIX_H
#define EXPERIMENT_POINTERMATRIX_H

#include <cstdlib>
#include <ostream>
#include "macro.h"
namespace PM {
    template<typename T, typename Derived>
    class MatrixBase{
    public:
        size_t nRow;
        size_t nCol;

        MatrixBase(const size_t nRow_,const size_t nCol_):nRow(nRow_), nCol(nCol_){}
        Derived& derived(){
            return *static_cast<Derived*>(this);
        }
        const Derived& derived() const{
            return *static_cast<Derived*>(this);
        }

        T& operator()(const size_t i, const size_t j){
            CHECK_BOUND(i,j,*this);
            return derived().operator()(i,j);
        }
        const T& operator()(const size_t i, const size_t j) const {
            return const_cast<T&>(
                    static_cast<const MatrixBase<T, Derived>&>(*this).operator()(i,j));

        }

        inline T rows(){
            return nRow;
        }
        const T rows() const {
            return nRow;
        }
        inline T cols(){
            return nCol;
        }
        const T cols() const {
            return nCol;
        }
        template<typename t1, typename t2>
        friend std::ostream& operator<<(std::ostream& os, const MatrixBase<t1, t2> & matrix);
    };
    template<typename t1, typename t2>
    std::ostream& operator<<(std::ostream& os, const MatrixBase<t1, t2>& matrix){

        for (size_t i =0;i<matrix.rows();i++){

            os << matrix(i,0);
            if (matrix.cols()>1) {
                for (size_t j = 1; j < matrix.cols(); j++) {
                    os << "," << matrix(i, j);
                }
            }
            os << std::endl;
        }
        return os;
    };

    template<typename T, typename Derived>
    class Matrix : public MatrixBase<T, Matrix<T, Derived>>{
    public:
        T * data;

        Matrix(const size_t nRow, const size_t nCol):MatrixBase<T, Matrix<T, Derived>>(nRow, nCol){
            data = (T*) malloc(sizeof(T)*nRow*nCol);
        }

        ~Matrix(){
            free(data);
        }
        Derived& derived(){
            return *static_cast<Derived*>(this);
        }
        const Derived& derived() const{
            return *static_cast<Derived*>(this);
        }

        T& operator()(const size_t i, const size_t j){
            return derived().operator()(i,j);
        }


    };

    template<typename T, typename Derived>
    class MatrixView : public MatrixBase<T, MatrixView<T, Derived>>{
    public:
        T * data;
        MatrixView(const size_t nRow, size_t nCol, T * other):MatrixBase<T, MatrixView<T, Derived>>(nRow, nCol), data(other){}
        T& operator()(const size_t i, const size_t j){
            return derived().operator()(i,j);
        }
        Derived& derived(){
            return *static_cast<Derived*>(this);
        }
        const Derived& derived() const{
            return *static_cast<Derived*>(this);
        }
    };

    template<typename T>
    class MatrixRowMajor: public Matrix<T, MatrixRowMajor<T>>{
    public:

        MatrixRowMajor(const size_t nRow, const size_t nCol):Matrix<T, MatrixRowMajor<T>>(nRow, nCol){}
        T& operator()(const size_t i, const size_t j){
            using base = MatrixBase<T, Matrix<T, MatrixRowMajor<T>>>;
            using super = Matrix<T, MatrixRowMajor<T>>;
            return super::data[i*base::nCol+j];
        }
    };
    template<typename T>
    class MatrixColMajor: public Matrix<T, MatrixColMajor<T>>{
    public:
        MatrixColMajor(const size_t nRow, const size_t nCol):Matrix<T, MatrixColMajor<T>>(nRow, nCol){}
        T& operator()(const size_t i, const size_t j){
            using base = MatrixBase<T, Matrix<T, MatrixColMajor<T>>>;
            using super = Matrix<T, MatrixColMajor<T>>;
            return super::data[i+j*base::nRow];
        }
    };
    template<typename T>
    class MatrixViewRowMajor : public MatrixView<T, MatrixViewRowMajor<T>>{
    public:
        MatrixViewRowMajor(const size_t nRow, const size_t nCol, T* other):MatrixView<T, MatrixViewRowMajor<T>>(nRow, nCol, other){}
        T& operator()(const size_t i, const size_t j){
            using base = MatrixBase<T, Matrix<T, MatrixViewRowMajor<T>>>;
            using super = MatrixView<T, MatrixViewRowMajor<T>>;
            return super::data[i*base::nCol+j];
        }
    };

    template<typename T>
    class MatrixViewColMajor : public MatrixView<T, MatrixViewColMajor<T>>{
    public:
        MatrixViewColMajor(const size_t nRow, const size_t nCol, T* other):MatrixView<T, MatrixViewRowMajor<T>>(nRow, nCol, other){}
        T& operator()(const size_t i, const size_t j){
            using base = MatrixBase<T, Matrix<T, MatrixViewRowMajor<T>>>;
            using super = MatrixView<T, MatrixViewRowMajor<T>>;
            return super::data[i+j*base::nRow];
        }
};
}

void test_print(){
    using namespace PM;
    using namespace std;
    MatrixRowMajor<double> matrix(10, 1);
    for (int i =0;i<matrix.rows();i++){
        matrix(i,0)=1.0;
        std::cout << "i'th entry is " <<matrix(i,0) << std::endl; //This is fine
    }
    std::cout << matrix; //This is not fine
}
#endif //EXPERIMENT_POINTERMATRIX_H

The whole program is compiled using g++4.9 (enabling c++11)

EDIT: In order to test whether it is the problem of the operator, I create the following methods (at MatrixBase):

void print(){
            for (size_t i =0;i<this->rows();i++){
                std::cout  << this->operator()(i,0);
                if (this->cols()>1) {
                    for (size_t j = 1; j < this->cols(); j++) {
                        std::cout << "," << this->operator()(i, j);
                    }
                }
                std::cout << std::endl;
            }
        }

and call the methods like matrix.print(). This works as expected.

Unfortunately, the debugger gives no useful information as the program stops in the line os << matrix(i,0), and when I stop the program to retrieve the information, it says fails to get the frame (Clion as debugger)


Solution

  • The (member function of MatrixBase) operator() unconditionally calls itself, so is infinitely recursive.

    const T& operator()(const size_t i, const size_t j) const {
            return const_cast<T&>(
                    static_cast<const MatrixBase<T, Derived>&>(*this).operator()(i,j));
    
        }
    

    This function is tail-recursive, so can result in an infinite loop rather than crashing due to great call depths.

    Unrelated to your question, it is usually considered inadvisable to use malloc() in C++ - particularly when working with types (e.g. C++ classes) that may not be directly compatible with C - results, for the user of your class, depending on the type T, can be undefined. Use operator new instead. Even better, use a standard container.