Contiguous matrix with double subscript operator [][] in c++

140 Views Asked by At

I'm trying to implement a simple Matrix struct that allows me to access it with double subscript operator and have the guarantee that the underlying elements are stored contiguously into memory.

Until now I was using std::array<std::array<float, 4>, 4> matrix. But as far as I understood there is no real guarantee that the elements will be stored contiguously.

So I came up with something like this:

struct RawMatrix4 {
    float m[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 
                    0.0f, 0.0f, 0.0f, 0.0f, 
                    0.0f, 0.0f, 0.0f, 0.0f, 
                    0.0f, 0.0f, 0.0f, 0.0f };

    struct Row { 
        float *m;
        int rowIdx4;

        const float &operator[](int j) const { return (*this)[j]; }
        float &operator[](int j) {
            assert(j >= 0 && j < 4);
            return m[rowIdx4 + j];
        }
    };

    const Row operator[](int i) const { return (*this)[i]; }
    Row operator[](int i) {
        assert(i >= 0 && i < 4);
        return Row{m, i * 4};
    }
};

This allows me to create a RawMatrix4 m; and then access the elements like this m[0][0]. And since the underlying structure is an array of 16 elements I have the guarantee that the elements are stored contiguously in memory.

However I'm not sure this is the best way to do this, and I wanted to ask your opinion if there is a better and more efficient way to achieve this. Also, I have some segmentation fault in my code, that I cannot really figure out. I would appreciate any help. Here is a minimal example, using the code above, that doesn't work: https://godbolt.org/z/nKoo6e6ro.

1

There are 1 best solutions below

1
DevX10 On

Thanks to the comments I fixed the recursive call:

struct RawMatrix4 {
    float m[16] = { 0.0f, 0.0f, 0.0f, 0.0f, 
                    0.0f, 0.0f, 0.0f, 0.0f, 
                    0.0f, 0.0f, 0.0f, 0.0f, 
                    0.0f, 0.0f, 0.0f, 0.0f };

    template<typename T>
    struct Row { 
        T *m;

        const float &operator[](int j) const { 
            assert(j >= 0 && j < 4);
            return m[j];
        }
        float &operator[](int j) {
            assert(j >= 0 && j < 4);
            return m[j];
        }
    };

    const Row<const float> operator[](int i) const { 
        assert(i >= 0 && i < 4);
        return Row<const float>{&m[i * 4]};
    }
    Row<float> operator[](int i) {
        assert(i >= 0 && i < 4);
        return Row<float>{&m[i * 4]};
    }
};

However, as suggested in the comments, It might be easier to overload float& operator()(size_t i, size_t j) and float const& operator()(size_t i, size_t j) const, if you're starting a new project.