C# 适用于二维情况的包装器类

C# 适用于二维情况的包装器类,c#,interop,pinvoke,matlab-coder,C#,Interop,Pinvoke,Matlab Coder,这个问题是这个问题的延伸 我想调整包装的二维情况。这是我第一次尝试: public class EmxArrayRealTWrapper : IDisposable { private readonly emxArray_real_T _value; private GCHandle _dataHandle; private GCHandle _sizeHandle; public emxArray_real_T Value { get { return _value; } } pu

这个问题是这个问题的延伸

我想调整包装的二维情况。这是我第一次尝试:

public class EmxArrayRealTWrapper : IDisposable
{
private readonly emxArray_real_T _value;
private GCHandle _dataHandle;
private GCHandle _sizeHandle;

public emxArray_real_T Value
{
    get { return _value; }
}

public EmxArrayRealTWrapper(double[,] data)
{
    _dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
    _value.data = _dataHandle.AddrOfPinnedObject();
    _sizeHandle = GCHandle.Alloc(new int[] { data.GetLength(0), data.GetLength(1) }, GCHandleType.Pinned);
    _value.size = _sizeHandle.AddrOfPinnedObject();
    _value.allocatedSize = data.GetLength(0) * data.GetLength(1) * sizeof(double);
    _value.numDimensions = 2;
    _value.canFreeData = false;
}

public void Dispose()
{
    _dataHandle.Free();
    _sizeHandle.Free();
    GC.SuppressFinalize(this);
}

~EmxArrayRealTWrapper()
{
    Dispose();
}
}

[StructLayout(LayoutKind.Sequential)]
public struct emxArray_real_T
{
public IntPtr data;
public IntPtr size;
public int allocatedSize;
public int numDimensions;
[MarshalAs(UnmanagedType.U1)]
public bool canFreeData;
}
附言:

原始matlab代码如下所示:

    function [x] = test(a)
    %#codegen

    x = 0;
    if(~isempty(coder.target))
      assert(isa(a,'double'));
      assert(all(size(a) == [1 Inf]));
   end

    x = sum(a);
a = [ 1 2; 3 4]

r = test(a)
public EmxArrayRealTWrapper(double[,] data)
{
    int nRow = data.GetLength(0);
    int nCol = data.GetLength(1);

    double[] flattenedData = new double[nCol * nRow];
    int index = 0;
    for (int col=0; col<nCol; col++)
    {
        for (int row=0; row<nRow; row++)
        {
            flattenedData[index] = data[row, col];
            index++;
        }
    }                    

    _dataHandle = GCHandle.Alloc(flattenedData, GCHandleType.Pinned);
    _value.data = _dataHandle.AddrOfPinnedObject();
    _sizeHandle = GCHandle.Alloc(new int[] { nCol, nRow }, GCHandleType.Pinned);
    _value.size = _sizeHandle.AddrOfPinnedObject();
    _value.allocatedSize = nCol * nRow;
    _value.numDimensions = 2;
    _value.canFreeData = false;
}
可以这样调用:

    function [x] = test(a)
    %#codegen

    x = 0;
    if(~isempty(coder.target))
      assert(isa(a,'double'));
      assert(all(size(a) == [1 Inf]));
   end

    x = sum(a);
a = [ 1 2; 3 4]

r = test(a)
public EmxArrayRealTWrapper(double[,] data)
{
    int nRow = data.GetLength(0);
    int nCol = data.GetLength(1);

    double[] flattenedData = new double[nCol * nRow];
    int index = 0;
    for (int col=0; col<nCol; col++)
    {
        for (int row=0; row<nRow; row++)
        {
            flattenedData[index] = data[row, col];
            index++;
        }
    }                    

    _dataHandle = GCHandle.Alloc(flattenedData, GCHandleType.Pinned);
    _value.data = _dataHandle.AddrOfPinnedObject();
    _sizeHandle = GCHandle.Alloc(new int[] { nCol, nRow }, GCHandleType.Pinned);
    _value.size = _sizeHandle.AddrOfPinnedObject();
    _value.allocatedSize = nCol * nRow;
    _value.numDimensions = 2;
    _value.canFreeData = false;
}
制作:

r =

     4     6
不幸的是,生成的C无法实现Matlab可以实现的功能(即返回数组):

\u declspec(dllexport)实际测试(const emxArray\u real\u T*a);
实际测试(常数emxArray\u real\u T*a)
{
real_T x;
int32_T k;
如果(a->size[1]==0){
x=0.0;
}否则{
x=a->数据[0];
对于(k=2;k大小[1];k++){
x+=a->数据[k-1];
}
}
返回x;
}

我假设MATLAB数组结构使用col主顺序。在这种情况下,结构构造函数需要如下所示:

    function [x] = test(a)
    %#codegen

    x = 0;
    if(~isempty(coder.target))
      assert(isa(a,'double'));
      assert(all(size(a) == [1 Inf]));
   end

    x = sum(a);
a = [ 1 2; 3 4]

r = test(a)
public EmxArrayRealTWrapper(double[,] data)
{
    int nRow = data.GetLength(0);
    int nCol = data.GetLength(1);

    double[] flattenedData = new double[nCol * nRow];
    int index = 0;
    for (int col=0; col<nCol; col++)
    {
        for (int row=0; row<nRow; row++)
        {
            flattenedData[index] = data[row, col];
            index++;
        }
    }                    

    _dataHandle = GCHandle.Alloc(flattenedData, GCHandleType.Pinned);
    _value.data = _dataHandle.AddrOfPinnedObject();
    _sizeHandle = GCHandle.Alloc(new int[] { nCol, nRow }, GCHandleType.Pinned);
    _value.size = _sizeHandle.AddrOfPinnedObject();
    _value.allocatedSize = nCol * nRow;
    _value.numDimensions = 2;
    _value.canFreeData = false;
}

请注意,这两个变体将数据传递给本机代码的方式不同。第一个版本传递原始数据的副本。第二个传递对原始数据的引用。我不确定您希望代码如何运行。修改第二个版本以传递副本非常容易。对于第一个版本,如果您希望本机代码修改数据并将这些修改反映回托管代码,那么您需要在本机调用返回后将修改封送回。

allocatedSize
是元素总数。因此,
*sizeof(double)
是错误的。把它拿走。将
allocatedSize
设置为
data.GetLength(0)*data.GetLength(1)
。主要问题是不能将double[,]blit到一个数组上。我认为这是正确的。一旦我知道了,我就可以修改你的代码了。谢谢。我已经按照你的建议修改了代码。对于新的双[,]{{1,2,4},{1,3,4},我得到7,而不是7和8。谢谢。小错误。请使用;在nRowSo之后,是col专业还是row专业?我现在很好奇。我不得不说我有一些问题。我现在可以传递2d数组,但是返回值只是一个标量,matlab编码器只是生成这个签名。这对我来说还行,因为我可以传入一维非固定数组(即2d数组的第一列)并得到结果。您可以通过引用将空的
emxArray\u real\T
作为参数传递。并使用它返回结果。或者您可以将
emxArray\u real\u T*
作为函数返回值返回,并将其封送为
IntPtr
。我对Matlab编码器了解不够,无法理解分配是如何工作的。另外,我非常想知道,col major还是row major?参见添加的C代码。我想是上校少校。现在试试你的建议。