Search code examples
c++c++-clieigenmathnet-numerics

C++/CLI converting managed MathNet Matrix to Eigen MatrixXi for use in native class


I'm trying to build a managed wrapper for a native C++ class that holds an Eigen::MatrixXi. Let's say the native class has one member matrix that it stores and gets through its constructor, and a method that uses that member at a later time:

#include <Eigen/Dense>
#include <Eigen/Core>
#include <iostream>

using Eigen::MatrixXi;

class Foo
{
public:
    const MatrixXi matrix;

    Foo(const MatrixXi &matrix);
    void Bar();
};

Foo::Foo(const MatrixXi &matrix)
    : matrix(matrix) {}

void Foo::Bar()
{
    for (int y = 0; y < matrix.rows(); y++)
    {
        for (int x = 0; x < matrix.cols(); x++)
        {
            std::cout << matrix(y, x);
        }
        std::cout << std::endl;
    }
}

I made the following wrapper:

using namespace MathNet::Numerics::LinearAlgebra;
using namespace MathNet::Numerics::LinearAlgebra::Single;

template<class T>
public ref class ManagedObject
{
protected:
    T* m_Instance;
public:
    ManagedObject(T* instance)
        : m_Instance(instance)
    {
    }
    virtual ~ManagedObject()
    {
        if (m_Instance != nullptr)
        {
            delete m_Instance;
        }
    }
    !ManagedObject()
    {
        if (m_Instance != nullptr)
        {
            delete m_Instance;
        }
    }
    T* GetInstance()
    {
        return m_Instance;
    }
};

public ref class ManagedFoo : public ManagedObject<Foo>
{
public:
    ManagedFoo(MathNet::Numerics::LinearAlgebra::Matrix<float>^ matrix);
    void Bar();
};

ManagedFoo::ManagedFoo(MathNet::Numerics::LinearAlgebra::Matrix<float>^ matrix)
    : ManagedObject(new Foo(mathnet_to_eigen(matrix))) {}

void ManagedFoo::Bar()
{
    m_Instance->Bar();
}

What would the mathnet_to_eigen method look like? I've tried the following:

#include <vector>
#include <Eigen/Dense>
#include <Eigen/Core>

using Eigen::MatrixXi;

static Eigen::MatrixXi mathnet_to_eigen(MathNet::Numerics::LinearAlgebra::Matrix<float>^ data)
{
    int length = data->RowCount * data->ColumnCount;
    std::vector<int> data_converted(length);

    for (size_t i = 0; i < data->RowCount; i++)
    {
        for (size_t j = 0; j < data->ColumnCount; j++)
        {
            data_converted[i * data->ColumnCount + j] = data[i, j];
        }
    }

    Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data_converted[0], data->RowCount, data->ColumnCount);

    return mat;
}

This seems to work, but I'm uncertain about the memory management and efficiency. Should I use pinning? And what would that look like? I have the feeling that I'm not doing it correctly and might be using a pointer to memory that is garbage collected.

I don't care in this case about how the float to int conversion is done (rounding/taking integer part).


Solution

  • I found this to be the cleanest way to do it:

    static Eigen::MatrixXf MathNetToEigen(MathNet::Numerics::LinearAlgebra::Matrix<float>^ data)
    {
        auto mat = Eigen::MatrixXf(data->RowCount, data->ColumnCount);
        for (size_t i = 0; i < data->RowCount; i++)
            for (size_t j = 0; j < data->ColumnCount; j++)
                mat(i, j) = data[i, j];
    
        return mat;
    }
    

    And converting back:

    static MathNet::Numerics::LinearAlgebra::Matrix<float>^ EigenToMathNet(Eigen::MatrixXf& data)
    {
        auto mat = MathNet::Numerics::LinearAlgebra::Matrix<float>::Build->Dense(data.rows(), data.cols());
        for (size_t i = 0; i < data.rows(); i++)
            for (size_t j = 0; j < data.cols(); j++)
                mat[i, j] = data(i, j);
    
        return mat;
    }