3

This question is an extension of this question.

I would like to adapt the wrapper for the two dimensional case. This is my first attempt:

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:

The orginal matlab code looks like this:

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

and can be invoked like this:

a = [ 1 2; 3 4]

r = test(a)

producing:

r =

     4     6

Unfortunately the produced C cannot achieve what Matlab can achieve (i.e. return an array):

__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;
}
Community
  • 1
  • 1
cs0815
  • 16,751
  • 45
  • 136
  • 299
  • `allocatedSize` is the total number of elements. So that `*sizeof(double)` is wrong. Just remove it. Set `allocatedSize` to `data.GetLength(0) * data.GetLength(1)`. The main problem is that you cannot blit double[,] onto a col major MATLAB array. I am correct that it is col major right. Once I know that, then I can adapt your code. – David Heffernan Feb 23 '13 at 15:49
  • Thanks. I have adapted the code as you suggested. For new double[,] { { 1, 2, 4 }, { 1, 3, 4 } } I obtain 7 rather than 7 and 8. – cs0815 Feb 23 '13 at 15:56

1 Answers1

2

I'm assuming that the MATLAB array struct use col-major ordering. In which case the struct constructor needs to look like this:

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

If the native code expects row-major then it's simpler. A C# multi-dimensional array is stored as a contiguous row-major array. So you can use code very similar to the one dimensional code I provided in your recent question.

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

Note well that these two variants differ in the way they pass the data to the native code. The first version passes a copy of the original data. The second passes a reference to the original data. I'm not sure how you want your code to behave. It's easy enough to adapt the second version to pass a copy. As for the first version, if you wanted the native code to modify the data and have those modifications reflected back to the managed code, then you'd need to marshal the modifications back after the native call returned.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks. Tiny mistake. Please use ; after nRow – cs0815 Feb 23 '13 at 16:13
  • So, is it col major or row major? I'm curious now. – David Heffernan Feb 23 '13 at 16:23
  • I have to say that I have some issues. I can pass in 2d arrays now but the return value is only a scalar - matlab coder just generates this signature. This is kind of ok for as I can pass in a one dimensional non-fixed array (i.e. first column of 2d array) and get the result. – cs0815 Feb 23 '13 at 16:34
  • You could pass an empty `emxArray_real_T` by reference as a parameter. And use this to return the result. Or you could return `emxArray_real_T*` as a function return value and marshal it as `IntPtr`. I don't know enough about Matlab coder to understand how allocation works. Also, I'm desperate to know, col-major or row-major? – David Heffernan Feb 23 '13 at 16:38
  • see added C code. I think it's col-major. try what you suggested now. – cs0815 Feb 23 '13 at 16:44
  • I can help you with marshalling, pinvoke, how to allocate a Matlab array structure. But I really can't help with Matlab coder. – David Heffernan Feb 23 '13 at 16:47
  • The saga continuous. almost there ... see: http://stackoverflow.com/questions/15043247/de-marshal-results – cs0815 Feb 23 '13 at 17:16
  • OK, so it's row major then? – David Heffernan Feb 23 '13 at 18:52