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;
}
I'm assuming that the MATLAB array struct use col-major ordering. In which case the struct constructor needs to look like this:
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.
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.