Search code examples
c++abstract-classeigeneigen3

A generic function to store Eigen matrix in a binary file


I know that the following code can be used to write the data of an Eigen Matrix with datatype as double to a binary file.

template<class  T> 
void WriteEigenMatrix(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& m, const char* fileName)
{
    std::ofstream outFile(fileName, std::ios_base::out | std::ios_base::binary);
    outFile.write(m.data(), m.rows()*m.cols());
    outFile.close();`
}

How can I generalize this function to accept other Eigen data types like Eigen::Vector and Eigen::Array as inputs?
If I use Eigen::EigenBase, I know that the function will accept all such data types. However, the problem is that I can no longer use m.data(), as it is not defined in the base class.
Any suggestion to solve this issue?


Solution

  • If you want to allow only "Dense" objects, you can to it as follows:

    template<class Derived>
    void WriteEigen(const Eigen::DenseBase<Derived>& dense, const char* fileName) {
        std::ofstream outFile(fileName, std::ios_base::out | std::ios_base::binary);
        outFile.write(reinterpret_cast<const char*>(dense.derived().data()), dense.rows() * dense.cols());
        outFile.close();
    }
    

    All dense Matrices (including vector & array) inherit from DenseBase, calling derived on DenseBase will give you back the concrete matrix type.

    Or use any Base you like.

    After comments from @chtz and @Homer512 this version uses PlainObjectBase and reshaped in combination with STL algorithm to write to the file, avoiding reinterpret_cast to char* and the possible failure in case innerStride() != 1.

    template<class Derived>
    void WriteEigen(const Eigen::PlainObjectBase<Derived>& dense, std::string_view fileName) {
        std::ofstream outFile(fileName, std::ios_base::out | std::ios_base::binary);
        const auto& reshaped = dense.reshaped();
        std::copy(reshaped.begin(), reshaped.end(), std::ostream_iterator<typename Derived::Scalar>(outFile));
    }
    

    reshaped() allows to iterate over all entries in an STL manner see example, which will take care of all possible strides. C++20 ranges allows for the nicer

    std::ranges::copy(reshaped, std::ostream_iterator<typename Derived::Scalar>(outFile));
    

    Using std::ostream_iterator allows to use std::(ranges::)copy instead of ostream::write() which operates on bytes. To get the underlying data type of the Eigen object, Dense::Scalar can be used.