This is an extension to this question to be able to return an array rather than a scalar.
The produced C code from the matlab code via matlab coder looks ok now (see below). I just try to figure out how to get the results back into the C# world. Here is my first attempt:
C# code
[DllImport(@"C:\bla\CPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void test(ref emxArray_real_T a, ref emxArray_real_T result);
static void Main(string[] args)
{
double[,] array2D = new double[,] { { 1, 2, 4 }, { 1, 3, 4 } };
var wrapper = new EmxArrayRealTWrapper(array2D);
var t = wrapper.Value;
var t1 = wrapper.Value;
test(ref t, ref t1);
}
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);
_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 code:
function [result] = test(a, result)
%#codegen
if(~isempty(coder.target))
assert(isa(a,'double'));
assert(all(size(a) == [1 Inf]));
assert(isa(result,'double'));
assert(all(size(result) == [1 Inf]));
end
result = sum(a);
produced C code
void test(const emxArray_real_T *a, emxArray_real_T *result)
{
real_T y;
int32_T k;
if (a->size[1] == 0) {
y = 0.0;
} else {
y = a->data[0];
for (k = 2; k <= a->size[1]; k++) {
y += a->data[k - 1];
}
}
k = result->size[0] * result->size[1];
result->size[0] = 1;
result->size[1] = 1;
emxEnsureCapacity((emxArray__common *)result, k, (int32_T)sizeof(real_T));
result->data[0] = y;
}
PS:
Given David's answer I am trying something like this at the moment:
[DllImport(@"C:\bla\CPlusPlus.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void test(ref emxArray_real_T a, ref emxArray_real_T result);
static void Main(string[] args)
{
double[,] array2D = new double[,] { { 1, 2, 4 }, { 1, 3, 4 } };
double[,] temp = new double[,] { { 0 }, { 0 } };
var wrapper = new EmxArrayRealTWrapper(array2D);
var wrapper1 = new EmxArrayRealTWrapper(temp);
var t = wrapper.Value;
var t1 = wrapper1.Value;
test(ref t, ref t1);
// initialise this by your call to the native code
int[] size = new int[2];
Marshal.Copy(t1.size, size, 0, 2);
int nCol = size[0];
int nRow = size[1];
double[] data = new double[nCol * nRow];
Marshal.Copy(t1.data, data, 0, nCol * nRow);
}
This only gives me one entry: 7 nCol and nRow are equal to 1.
You are essentially asking how to read the contents of a
emxArray_real_T
into a C# object.Let's first consider a 1D array. Read it like this:
And that's it. You'd want to assert that
result.numDimensions == 1
.And you may not need to do the
Marshal.Copy
step. You probably still have access to the array you passed forresult.data
and so you can just use that.The two dimensional case is just more of the same. Again you'll want to check that
result.numDimensions == 2
.That puts the data in a one-dimensional array and presumably you'll want to put this into a two-dimensional managed array. Assuming the MATLAB is col-major, you'll need to deal with the col-major to row-major translation.