Search code examples
c#matlabpinvokematlab-coder

emxArray_real_T to C# struct plus initialisation


I am trying to create a 'constructor' for this C# struct (initial attempt included):

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

public emxArray_real_T(double[] cSharpData)
{
    var arraySize = Marshal.SizeOf(cSharpData[0]) * cSharpData.Length;
    this.data = Marshal.AllocHGlobal(arraySize);
    // ????
    numDimensions = 1;
    canFreeData = false;
}
}

The C corresponding C struct looks like this:

typedef struct emxArray_real_T
{
   real_T *data;
   int32_T *size;
   int32_T allocated;
   int32_T numDimensions;
   boolean_T canFreeData;
} emxArray_real_T;

and is explained here.

Looking forward to any comments/answers. Thanks!


Solution

  • You have a couple of choices as to how to do this. You can allocate unmanaged memory. And then copy the contents of your managed memory across. And then presumably copy it back when your call into native code returns.

    Since your example sets canFreeData to false then I guess you want to do it the other way. And that is to pass the managed memory down to the native code. In order to do that you need to pin it to protect it from GC movement.

    In order to make either of these approaches work I think you need a wrapper class to managed either the native memory, or the pinning. Here's how I'd tackle the pinning approach:

    [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;
    }
    
    public class emxArray_real_T_Wrapper : IDisposable
    {
        private emxArray_real_T value;
        private GCHandle dataHandle;
        private GCHandle sizeHandle;
    
        public emxArray_real_T Value {
            get { return value; } 
        }
    
        public emxArray_real_T_Wrapper(double[] data)
        {
            dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
            value.data = dataHandle.AddrOfPinnedObject();
            sizeHandle = GCHandle.Alloc(new int[] { data.Length }, GCHandleType.Pinned);
            value.size = sizeHandle.AddrOfPinnedObject();
            value.allocatedSize = data.Length;
            value.numDimensions = 1;
            value.canFreeData = false;
        }
    
        public void Dispose()
        {
            dataHandle.Free();
            sizeHandle.Free();
            GC.SuppressFinalize(this);
        }
    
        ~emxArray_real_T_Wrapper()
        {
            Dispose();
        }
    }