SHOW ME THE CODE !

Binary tree

150px-Binary_tree.svg.png

1633990707354.png
 
Exsan, how does your work compare to similar stuff in Boost Intrusive

I have not seen this before, please let me go through it.
However, before any comment I would say that my work is kind of THINK DIFFERENT, so far roughly more than 100K lines source code- fully integrated. I developed most of the library of my product, I try to avoid external libraries, as you can imagine my work is just: OOP + POINTERS
By the way, which version of Boost have you lately installed in your machine? 1.77?
have you tried the new Visual Studio 64 bits? do you recommend it ?
 
Last edited:
Refreshing my C++ programming skills by reviewing the material in the book : C++17 by Ivor Horton; the idea is to try and implement some numerical code in the short term.

Any feedback is super-helpful.

Merge Sort.
Merge Sort:
// MergeSort.cpp : MergeSort on an std::array<T,N>

#include <iostream>
#include <array>
#include <cmath>
#include <iomanip>

template <class T, std::size_t N>
void merge(std::array<T, N>& A, const int l, const int m, const int r)
{
    // A[l..m] and A[m+1..r] are two sorted lists and
    // we are interested to merge them.

    int l1{ l };        //This index always points to the beginning of the first list
    int l2{ m + 1 };    //This index always points to the beginning of the second list
    int i{ 0 };         //This index always points to the beginning of the merged list
  
    std::array<T, N> result {};  //The merged list will be held in a fixed-size array (auto storage duration), until
                                        //merge is complete.

    // Iterate until we do not reach the end of atleast one of the lists
    while (l1 <= m && l2 <= r)
    {
        // Compare the current element of the first list with the second list
        // and find the least element.
        if (A[l1] <= A[l2])
        {
            result[i] = A[l1];
            ++l1;
        }
        else
        {
            result[i] = A[l2];
            ++l2;
        }
        ++i;
    }

    // If the end of either one of the lists is reached, flush all the remaining elements
    // of the other list to result
    while (l1 <= m)
    {
        result[i] = A[l1];
        ++i;
        ++l1;
    }

    while (l2 <= r)
    {
        result[i] = A[l1];
        ++i;
        ++l2;
    }

    //Copy all elements from the result into A[l..r]
    int j{ l };
    for (int k{}; k < r - l + 1; ++k)
    {
        A[j] = result[k];
        ++j;
    }
}

template <class T, int N>
void mergeSort(std::array<T,N>& a, int l, int r)
{
    // If the problem is of size 1, then we are done. There is nothing to be
    // sorted.
    if (N == 1 || l == r)
        return;

    // Divide the problem into 2 smaller subproblems of a smaller size (N/2).
    int m{ static_cast<int>(floor((l + r) / 2.0)) };

    // Recursively conquer the two sub-problems of size (N/2)
    mergeSort<T, N>(a, l, m);
    mergeSort<T, N>(a, m+1, r);

    // Now, we have two sorted lists and we would like to merge them.
    merge<T, N>(a, l, m, r);
}

int main()
{
    std::array<int, 10> A{ 10,9,8,7,6,5,4,3,2,1 };
    std::cout << "Before mergeSort : " << std::endl;
    for (int element : A)
    {
        std::cout << std::setw(5) << element;
    }
    std::cout << std::endl;
    mergeSort(A, 0, 9);

    std::cout << "After mergeSort : " << std::endl;
    for (int element : A)
    {
        std::cout << std::setw(5) << element;
    }
    std::cout << std::endl;
    return 0;
}
 
Last edited:

Daniel Duffy

C++ author, trainer
Is there not a standard STL algorithm?
A while loop with nested if else might be 1) slow, 2) shaky/flaky, 3) difficult to maintain.
e.g. arrays of length 1'000'000, exception handling etc.

It looks like reinvention of wheel. It means less time for 'bigger' stuff.

BTW what's a good rationale for merge()? Just asking. It's not documented.

// Complexity: O(N^2)??
 
Last edited:
Is there not a standard STL algorithm?
A while loop with nested if else might be 1) slow, 2) shaky/flaky, 3) difficult to maintain.
e.g. arrays of length 1'000'000, exception handling etc.

It looks like reinvention of wheel. It means less time for 'bigger' stuff.

BTW what's a good rationale for merge()? Just asking. It's not documented.

// Complexity: O(N^2)??

Daniel, in case of Merge sort, if the array has size 2^M = N , then at level j of the recursion tree, you are left with 2^j arrays of size N/2^j each that you wanna merge. The merge operation is linear so, each stage should take like O(N) work and there are \log N/\log 2 stages so, its ~N \log N; that's atleast what I recall.

merge() is supposed to simply encapsulate the functionality of merging two sorted arrays.

I will add some exception handling; also want to learn to write unit tests for my code (I have used xUnit before, but haven't used a framework for C++).

BTW, i did bookmark this SE post on how to implement classic sorting algorithms in modern C++. But, I don't know the standard library <algorithms> just yet. The book I am using is modular; might take me a little while to write code like in that SE post.
 
Hi guys,

I am writing a matrix class - and wanted to get some feedback. My idea is that one should be capable of creating both fixed-size(compile-time) and dynamic matrices. So, containerType template parameter would be either std::array<T,N> or std::vector<N>.

I should provide proper iterators to traverse through my matrix and also need to overload the assignment operator.

Matrix.h:
#pragma once

#include <array>
#include <vector>

template <typename T, int r, int c, typename cType=std::array<T,r*c>>
class Matrix;

using Matrix1d = Matrix<double, 1, 1>;
using Matrix2d = Matrix<double, 2, 2>;
using Matrix3d = Matrix<double, 3, 3>;
using Matrix4d = Matrix<double, 4, 4>;

using Matrix1i = Matrix<int, 1, 1>;
using Matrix2i = Matrix<int, 2, 2>;
using Matrix3i = Matrix<int, 3, 3>;
using Matrix4i = Matrix<int, 4, 4>;

using Matrix1f = Matrix<float, 1, 1>;
using Matrix2f = Matrix<float, 2, 2>;
using Matrix3f = Matrix<float, 3, 3>;
using Matrix4f = Matrix<float, 4, 4>;

using Vector1d = Matrix<double, 1, 1>;
using Vector2d = Matrix<double, 1, 2>;
using Vector3d = Matrix<double, 1, 3>;
using Vector4d = Matrix<double, 1, 4>;

using Vector1i = Matrix<int, 1, 1>;
using Vector2i = Matrix<int, 2, 2>;
using Vector3i = Matrix<int, 3, 3>;
using Vector4i = Matrix<int, 4, 4>;

using Vector1f = Matrix<float, 1, 1>;
using Vector2f = Matrix<float, 2, 2>;
using Vector3f = Matrix<float, 3, 3>;
using Vector4f = Matrix<float, 4, 4>;

using MatrixXi = Matrix<int, 0, 0, std::vector<int>>;
using MatrixXd = Matrix<double, 0, 0, std::vector<double>>;
using MatrixXf = Matrix<float, 0, 0, std::vector<float>>;

template <typename scalarType, int rowsAtCompileTime = 0, int colsAtCompileTime = 0, typename containerType = std::array<scalarType,(rowsAtCompileTime * colsAtCompileTime)>>
class Matrix
{
private:
    containerType A;
    int _rows;
    int _cols;
public:
    Matrix() = default;
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;
 
    //Overloaded operators
    scalarType operator()(const int i, const int j) const;
    scalarType& operator()(const int i, const int j);
    Matrix operator+(const Matrix& m) const;
    Matrix operator-(const Matrix& m) const;
    Matrix operator*(const Matrix& m) const;
};

Matrix.cpp:
#pragma once

#include "Matrix.h"
#include <stdexcept>

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::Matrix(const Matrix& m):A{m.A}, _rows{m.rows()}, _cols{m.cols()}, _size{m.size()}
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::rows() const
{
    return _rows;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::cols() const
{
    return _cols;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::size() const
{
    return A.size();
}

/// <summary>
/// Coefficient accessors.
/// The primary coefficient accessor is the overloaded parenthesis operators. The data of the matrix
/// is stored in an underlying sequential container such as std::array or std::vector. The element
/// A(i,j) is at an offset of (i*_cols)+j in memory.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i">The row index</param>
/// <param name="j">The col index</param>
/// <returns>The element at position (i,j)</returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j) const
{
    containerType::const_iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

/// <summary>
/// Matrix addition.
/// Overload of the binary operator +. The left hand side and the right hand side matrix must have
/// the same number of rows and columns.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i"></param>
/// <param name="j"></param>
/// <returns></returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType& Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j)
{
    containerType::iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator+(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }

 
    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator-(const Matrix& B) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }
    return result;
}

template<typename scalarType, int m, int n, int p, typename containerType>
Matrix<scalarType, m, p, containerType> operator*(const Matrix<scalarType, m, n, containerType>& A, const Matrix<scalarType, n, p, containerType>& B)
{
    Matrix<scalarType, m, p, containerType> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }
}
 
Last edited:
Hi guys,

I am writing a matrix class - and wanted to get some feedback. My idea is that one should be capable of creating both fixed-size(compile-time) and dynamic matrices. So, containerType template parameter would be either std::array<T,N> or std::vector<N>.

I should provide proper iterators to traverse through my matrix and also need to overload the assignment operator.

Matrix.h:
#pragma once

#include <array>
#include <vector>

template <typename T, int r, int c, typename cType=std::array<T,r*c>>
class Matrix;

using Matrix1d = Matrix<double, 1, 1>;
using Matrix2d = Matrix<double, 2, 2>;
using Matrix3d = Matrix<double, 3, 3>;
using Matrix4d = Matrix<double, 4, 4>;

using Matrix1i = Matrix<int, 1, 1>;
using Matrix2i = Matrix<int, 2, 2>;
using Matrix3i = Matrix<int, 3, 3>;
using Matrix4i = Matrix<int, 4, 4>;

using Matrix1f = Matrix<float, 1, 1>;
using Matrix2f = Matrix<float, 2, 2>;
using Matrix3f = Matrix<float, 3, 3>;
using Matrix4f = Matrix<float, 4, 4>;

using Vector1d = Matrix<double, 1, 1>;
using Vector2d = Matrix<double, 1, 2>;
using Vector3d = Matrix<double, 1, 3>;
using Vector4d = Matrix<double, 1, 4>;

using Vector1i = Matrix<int, 1, 1>;
using Vector2i = Matrix<int, 2, 2>;
using Vector3i = Matrix<int, 3, 3>;
using Vector4i = Matrix<int, 4, 4>;

using Vector1f = Matrix<float, 1, 1>;
using Vector2f = Matrix<float, 2, 2>;
using Vector3f = Matrix<float, 3, 3>;
using Vector4f = Matrix<float, 4, 4>;

using MatrixXi = Matrix<int, 0, 0, std::vector<int>>;
using MatrixXd = Matrix<double, 0, 0, std::vector<double>>;
using MatrixXf = Matrix<float, 0, 0, std::vector<float>>;

template <typename scalarType, int rowsAtCompileTime = 0, int colsAtCompileTime = 0, typename containerType = std::array<scalarType,(rowsAtCompileTime * colsAtCompileTime)>>
class Matrix
{
private:
    containerType A;
    int _rows;
    int _cols;
public:
    Matrix() = default;
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;
 
    //Overloaded operators
    scalarType operator()(const int i, const int j) const;
    scalarType& operator()(const int i, const int j);
    Matrix operator+(const Matrix& m) const;
    Matrix operator-(const Matrix& m) const;
    Matrix operator*(const Matrix& m) const;
};

Matrix.cpp:
#pragma once

#include "Matrix.h"
#include <stdexcept>

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::Matrix(const Matrix& m):A{m.A}, _rows{m.rows()}, _cols{m.cols()}, _size{m.size()}
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::rows() const
{
    return _rows;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::cols() const
{
    return _cols;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::size() const
{
    return A.size();
}

/// <summary>
/// Coefficient accessors.
/// The primary coefficient accessor is the overloaded parenthesis operators. The data of the matrix
/// is stored in an underlying sequential container such as std::array or std::vector. The element
/// A(i,j) is at an offset of (i*_cols)+j in memory.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i">The row index</param>
/// <param name="j">The col index</param>
/// <returns>The element at position (i,j)</returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j) const
{
    containerType::const_iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

/// <summary>
/// Matrix addition.
/// Overload of the binary operator +. The left hand side and the right hand side matrix must have
/// the same number of rows and columns.
/// </summary>
/// <typeparam name="scalarType"></typeparam>
/// <typeparam name="containerType"></typeparam>
/// <param name="i"></param>
/// <param name="j"></param>
/// <returns></returns>
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
scalarType& Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator()(const int i, const int j)
{
    containerType::iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator+(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }

 
    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime, typename containerType>
Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType>::operator-(const Matrix& B) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, containerType> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        containerType::const_iterator it1{ A.begin() };
        containerType::const_iterator it2{ m.A.begin() };
        containerType::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }
    return result;
}

template<typename scalarType, int m, int n, int p, typename containerType>
Matrix<scalarType, m, p, containerType> operator*(const Matrix<scalarType, m, n, containerType>& A, const Matrix<scalarType, n, p, containerType>& B)
{
    Matrix<scalarType, m, p, containerType> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }
}
Sorry for the duplication, but posting some working code now. One failure I corrected:
  • When using class templates, the member function definitions should have been in the same header-file Matrix.h, because these are really function templates (not instantiated). Failing to do this results in the compiler not instantiating them, and the linker would complain about missing symbols.
A matrix M can be initialized as, M << 1, 0, 0,
0, 1, 0,
0, 0, 1.

Nice to have functionality.

Any suggestions/feedback is super-helpful.

Matrix.h:
#pragma once

#include <array>
#include <vector>
#include <stdexcept>
#include <type_traits>
#include <iterator>

template <typename T, int r=0, int c=0, typename cType=std::array<T,r*c>>
class Matrix;

using Matrix1d = Matrix<double, 1, 1, std::array<double,1>>;
using Matrix2d = Matrix<double, 2, 2, std::array<double, 4>>;
using Matrix3d = Matrix<double, 3, 3, std::array<double, 9>>;
using Matrix4d = Matrix<double, 4, 4, std::array<double, 16>>;

using Matrix1i = Matrix<int, 1, 1, std::array<int, 1>>;
using Matrix2i = Matrix<int, 2, 2, std::array<int, 4>>;
using Matrix3i = Matrix<int, 3, 3, std::array<int, 9>>;
using Matrix4i = Matrix<int, 4, 4, std::array<int, 16>>;

using Matrix1f = Matrix<float, 1, 1, std::array<float, 1>>;
using Matrix2f = Matrix<float, 2, 2, std::array<float, 4>>;
using Matrix3f = Matrix<float, 3, 3, std::array<float, 9>>;
using Matrix4f = Matrix<float, 4, 4, std::array<float, 16>>;

using Vector1d = Matrix<double, 1, 1, std::vector<double>>;
using Vector2d = Matrix<double, 1, 2, std::vector<double>>;
using Vector3d = Matrix<double, 1, 3, std::vector<double>>;
using Vector4d = Matrix<double, 1, 4, std::vector<double>>;

using Vector1i = Matrix<int, 1, 1, std::vector<double>>;
using Vector2i = Matrix<int, 2, 2, std::vector<double>>;
using Vector3i = Matrix<int, 3, 3, std::vector<double>>;
using Vector4i = Matrix<int, 4, 4, std::vector<double>>;

using Vector1f = Matrix<float, 1, 1, std::vector<double>>;
using Vector2f = Matrix<float, 2, 2, std::vector<double>>;
using Vector3f = Matrix<float, 3, 3, std::vector<double>>;
using Vector4f = Matrix<float, 4, 4, std::vector<double>>;

using MatrixXi = Matrix<int, 0, 0, std::vector<int>>;
using MatrixXd = Matrix<double, 0, 0, std::vector<double>>;
using MatrixXf = Matrix<float, 0, 0, std::vector<float>>;

template <typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
class Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>
{
private:
    std::array<scalarType, rowsAtCompileTime* colsAtCompileTime> A;
    int _rows;
    int _cols;
    int _size;
    typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::iterator currentPosition{A.begin()};
public:
    Matrix();
    Matrix(int m, int n);
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;
    
    //Overloaded operators
    scalarType operator()(const int i, const int j) const;        //Subscript operator
    scalarType& operator()(const int i, const int j);            //Subscript operator const arrays
    Matrix operator+(const Matrix& m) const;                    
    Matrix operator-(const Matrix& m) const;
    Matrix& operator<<(const scalarType x);
    Matrix& operator,(const scalarType x);
    Matrix& operator=(const Matrix& right_hand_side);
};

template <typename scalarType>
class Matrix<typename scalarType, 0, 0, std::vector<scalarType>>
{
private:
    std::vector<scalarType> A;
    int _rows;
    int _cols;
    int _size;
    typename std::vector<scalarType>::iterator currentPosition{};
public:
    Matrix();
    Matrix(int m, int n);
    Matrix(const Matrix& m);

    int rows() const;
    int cols() const;
    int size() const;

    //Overloaded operators
    scalarType operator()(const int i, const int j) const;
    scalarType& operator()(const int i, const int j);
    Matrix operator+(const Matrix& m) const;
    Matrix operator-(const Matrix& m) const;
    Matrix& operator<<(const scalarType x);
    Matrix& operator,(const scalarType x);
    Matrix& operator=(const Matrix& right_hand_side);
};

// Non-member operator functions
template<typename scalarType, int m, int n, int p, int q>
Matrix<scalarType, m, q, std::vector<scalarType>> operator*(const Matrix<scalarType, m, n, std::vector<scalarType>>& A, const Matrix<scalarType, p, q, std::vector<scalarType>>& B);

template<typename scalarType, int m, int n, int p, int q>
Matrix<scalarType, m, q, std::array<scalarType,m*q>> operator*(const Matrix<scalarType, m, n, std::array<scalarType,m*n>>& A, const Matrix<scalarType, p, q, std::array<scalarType,p*q>>& B);


//Default constructor
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::Matrix():_rows{rowsAtCompileTime}, _cols{colsAtCompileTime}, currentPosition{A.begin()}
{
    //Do nothing.
}

//Default constructor
template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::Matrix() :_rows{ 0 }, _cols{ 0 }
{
    currentPosition = A.begin();
}

//Fixed-Size matrices
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType,rowsAtCompileTime * colsAtCompileTime>>::Matrix(int m, int n)
{
    //Do nothing.
}

//Run-time matrices
template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::Matrix(int m, int n) : currentPosition{A.begin()}
{
    int numOfElements{ m * n };
    for (int i{}; i < numOfElements; ++i)
    {
        A.push_back(0);
    }
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::Matrix(const Matrix& m): A{m.A}, _rows{m.rows()}, _cols{m.cols()}, currentPosition{m.currentPosition}
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType,rowsAtCompileTime*colsAtCompileTime>>::Matrix(const Matrix& m) : A{ m.A }, _rows{ m.rows() }, _cols{ m.cols() }, currentPosition{ m.currentPosition }
{
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType,rowsAtCompileTime* colsAtCompileTime>>::rows() const
{
    return _rows;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::cols() const
{
    return _cols;
}

template<typename scalarType>
inline int Matrix<scalarType, 0, 0, std::vector<scalarType>>::rows() const
{
    return _rows;
}

template<typename scalarType>
inline int Matrix<scalarType, 0, 0, std::vector<scalarType>>::cols() const
{
    return _cols;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline int Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::size() const
{
    return A.size();
}

template<typename scalarType>
inline int Matrix<scalarType, 0, 0, std::vector<scalarType>>::size() const
{
    return A.size();
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline scalarType Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator()(const int i, const int j) const
{
    typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::const_iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline scalarType& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator()(const int i, const int j)
{
    typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::iterator it{ A.begin() };
    it = it + (i * _cols) + j;
    if (it < A.end())
        return *it;
    else
        throw std::out_of_range("\nError accessing an element beyond matrix bounds");
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator+(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::const_iterator it1{ A.begin() };
        typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::const_iterator it2{ m.A.begin() };
        typename std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator-(const Matrix& m) const
{
    Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::array<scalarType, rowsAtCompileTime*colsAtCompileTime>::const_iterator it1{ A.begin() };
        typename std::array<scalarType, rowsAtCompileTime*colsAtCompileTime>::const_iterator it2{ m.A.begin() };
        typename std::array<scalarType, rowsAtCompileTime*colsAtCompileTime>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator<<(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>& Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator<<(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator,(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
inline Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>& Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime, std::array<scalarType, rowsAtCompileTime* colsAtCompileTime>>::operator=(const Matrix& right_hand_side)
{
    if (this == &right_hand_side)
        return *this;

    this->A = right_hand_side.A;
    this->_rows = right_hand_side._rows;
    this->_cols = right_hand_side._cols;
    this->currentPosition = right_hand_side.currentPosition;
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>& Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator=(const Matrix& right_hand_side)
{
    if (this == &right_hand_side)
        return *this;

    this->A = right_hand_side.A;
    this->_rows = right_hand_side._rows;
    this->_cols = right_hand_side._cols;
    this->currentPosition = right_hand_side.currentPosition;
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>>& Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator,(const scalarType x)
{
    if (currentPosition < A.end())
    {
        *currentPosition = x;
        ++currentPosition;
    }
    else
    {
        throw std::logic_error("Error: Attempting to set values beyond matrix bounds!");
    }
    return *this;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>> Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator+(const Matrix& m) const
{
    Matrix<scalarType, 0, 0, std::vector<scalarType>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::vector<scalarType>::const_iterator it1{ A.begin() };
        typename std::vector<scalarType>::const_iterator it2{ m.A.begin() };
        typename std::vector<scalarType>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 - *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType>
inline Matrix<typename scalarType, 0, 0, std::vector<scalarType>> Matrix<typename scalarType, 0, 0, std::vector<scalarType>>::operator-(const Matrix& m) const
{
    Matrix<scalarType, 0, 0, std::vector<scalarType>> result{};

    if (this->rows() == m.rows() && this->cols() == m.cols())
    {
        typename std::vector<scalarType>::const_iterator it1{ A.begin() };
        typename std::vector<scalarType>::const_iterator it2{ m.A.begin() };
        typename std::vector<scalarType>::iterator resultIter{ result.A.begin() };
        while (it1 < A.end() && it2 < m.A.end())
        {
            *resultIter = *it1 + *it2;
            ++it1; ++it2; ++resultIter;
        }
    }
    else
    {
        throw std::logic_error("Matrices have different dimensions; therefore cannot be added!");
    }


    return result;
}

template<typename scalarType, int m, int n, int p, int q>
inline Matrix<scalarType, m, q, std::vector<scalarType>> operator*(const Matrix<scalarType, m, n, std::vector<scalarType>>& A, const Matrix<scalarType, p, q, std::vector<scalarType>>& B)
{
    if (n != p)
        throw std::logic_error("Error multiplying the matrices; the number of cols(A) must equal the number of rows(B)!");

    Matrix<scalarType, m, q, std::vector<scalarType>> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }

    return result;
}

template<typename scalarType, int m, int n, int p, int q>
Matrix<scalarType, m, q, std::array<scalarType, m* q>> operator*(const Matrix<scalarType, m, n, std::array<scalarType, m* n>>& A, const Matrix<scalarType, p, q, std::array<scalarType, p* q>>& B)
{
    if (n != p)
        throw std::logic_error("Error multiplying the matrices; the number of cols(A) must equal the number of rows(B)!");

    Matrix<scalarType, m, q, std::array<scalarType, m * q>> result;

    for (int i{}; i < m; ++i)
    {
        for (int k{}; k < p; ++k)
        {
            scalarType sum{};
            for (int j{}; j < n; ++j)
            {
                sum += A(i, j) * B(j, k);
            }
            result(i, k) = sum;
        }
    }

    return result;
}

TestMatrix.cpp:
// MatrixImpl.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <iostream>
#include "Matrix.h"
#include <iomanip>

int main()
{
    Matrix<int, 3, 3> m1;
    m1 <<1, 0, 0,
        0, 1, 0,
        0, 0, 1;

    Matrix<int, 3, 3> m2;
    m2 << 2, 0, 0,
        0, 1, 0,
        0, 0, 3;

    Matrix<int, 3, 3> m3 = m1 + m2;

    for (int i{}; i < 3; ++i)
    {
        for (int j{}; j < 3; ++j)
        {
            std::cout << std::setw(5) << m3(i, j);
        }
        std::cout << std::endl;
    }
    return 0;
}
 
the notation m << .. is kinda non-standard and a bit weird. See how Python does it. And Boost uBLAS.

C++ initialisation

Hi Daniel,
  • Added a constructor that takes std::initializer_list<std::initializer_list<T>> as a parameter and initializes the matrix.

C++:
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
Matrix<typename scalarType, rowsAtCompileTime, colsAtCompileTime>::Matrix(std::initializer_list<std::initializer_list<scalarType>> list):Matrix<scalarType,rowsAtCompileTime,colsAtCompileTime>{}    //Delegate to the default constructor to set up the initial array
{
    // Initialize the array from our list.
    typename std::array<scalarType,rowsAtCompileTime*colsAtCompileTime>::iterator A_it{ A.begin() };
    typename std::initializer_list<std::initializer_list<scalarType>>::iterator i{};

    for (i = list.begin(); i < list.end(); ++i)
    {
        typename std::initializer_list<scalarType>::iterator j{};
        for (j = (*i).begin(); j < (*i).end(); ++j)
        {
            *A_it = *j;
            ++A_it;
        }
    }
}

  • Pretty printing the matrix in a rectangular format.
C++:
template<typename scalarType, int rowsAtCompileTime, int colsAtCompileTime>
std::ostream& operator<<(std::ostream& os, Matrix<scalarType, rowsAtCompileTime, colsAtCompileTime>& m)
{
    for (int i{}; i < m.rows(); ++i)
    {
        os << "Row(";
        for (int j{}; j < m.cols(); ++j)
        {
            if (j != m.cols() - 1)
                os << m(i, j) << std::setw(5);
            else
                os << m(i, j);
            if (j % 10 == 0 && j > 0)
                os << std::endl;
        }

        os << ")" << std::endl;
    }

    return os;
}
  • Multiplication with scalars.
C++:
template<typename scalarType, int m, int n, typename T>
Matrix<scalarType, m, n>& operator*(const T k, Matrix<scalarType, m, n>& mat)
{
    for (int i{}; i < m.rows(); ++i)
    {
        for (int j{}; j < m.cols(); ++j)
        {
            mat(i, j) *= k;
        }
    }

    return mat;
}

  • Created two separate types : Matrix for compile-time matrices and MatrixX for run-time matrices, since they really are different, instead of shoe-horning them into one.
I think something that might make this piece of code more useful, is the ability to promote a matrix e.g. from Matrix<int> to Matrix<long>(of the same dimensions). Right now, addition is too restrictive.

Quick Question - I am building this project in VS as a DLL. But, libraries are no good use, if traders/structurers/risk managers can't easily use them. I have seen a couple of solutions that allow exposing this functionality to excel. Do you have any suggestions?

Also, I read up a bit on the net, and my understanding is __declspec(dllexport) is used to expose C++ class member functions in a DLL to the the outside. Is my understanding correct?
 
Last edited:
Top