3

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

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

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;
}

PS:

原始 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)

生产:

r =

     4     6

不幸的是,生成的 C 无法达到 Matlab 可以达到的效果(即返回一个数组):

__declspec(dllexport) real_T test(const emxArray_real_T *a);

real_T test(const emxArray_real_T *a)
{
  real_T x;
  int32_T k;
  if (a->size[1] == 0) {
    x = 0.0;
  } else {
    x = a->data[0];
    for (k = 2; k <= a->size[1]; k++) {
      x += a->data[k - 1];
    }
  }

  return x;
}
4

1 回答 1

2

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

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;
}

如果本机代码需要行优先,那么它更简单。AC# 多维数组存储为连续的行优先数组。因此,您可以使用与我在您最近的问题中提供的一维代码非常相似的代码。

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

请注意,这两种变体在将数据传递给本机代码的方式上有所不同。第一个版本传递原始数据的副本。第二个传递对原始数据的引用。我不确定你希望你的代码如何表现。调整第二个版本以传递副本很容易。对于第一个版本,如果您希望本机代码修改数据并将这些修改反映回托管代码,那么您需要在本机调用返回后将修改编组回来。

于 2013-02-23T15:59:56.130 回答