Question

[StructLayout(LayoutKind.Sequential)]
public struct Demo
{
    double X;
    double Y;
}


var data = new Demo[128];

FillWithMeaningfulValues(data);

double[] doubles;

Copy(data, out doubles); // ?

How do I copy the demo array into the doubles array without having to for(...) through each element? In C++, I would use memcpy, but in C# I did not find what I need in Marshal.Copy.

void MethodIDoNotWantToUse(Demo[] demo, out double[] doubles)
{
    doubles = new double[demo.Length * 2];
    for(int i = 0, j = 0; i < demo.Length; ++i)
    {
        doubles[j++] = demo[i].X;
        doubles[j++] = demo[i].Y;
    }  
}

void MethodIWouldPreferToUse(Demo[] demo, out double[] doubles)
{
    doubles = new double[demo.Length * 2];
    memcopy(doubles, demo, demo.Length * 2 * sizeof(double));
}
Was it helpful?

Solution 2

Since the struct is blittable, the array of the struct is blittable. Therefore you can pin the array of struct and copy into the double array with Marshal.Copy.

void CopyDemoArrayToDoubleArray(Demo[] demo, out double[] doubles)
{
    doubles = new double[demo.Length * 2];
    GCHandle gch = GCHandle.Alloc(demo, GCHandleType.Pinned);
    try
    {
        IntPtr demoPtr = gch.AddrOfPinnedObject();
        Marshal.Copy(demoPtr, doubles, 0, doubles.Length);
    }
    finally
    {
        gch.Free();
    }
}

You might do well to benchmark this against the simpler for loop that you want to avoid. It is plausible that the for loop will perform perfectly adequately.

OTHER TIPS

You'll do something like this. Marshal.Copy do provides you what you need.

Demo[] array = new Demo[2];
array[0] = new Demo {X = 5.6, Y= 6.6};
array[1] = new Demo {X = 7.6, Y = 8.6};
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
try
{
    IntPtr pointer = handle.AddrOfPinnedObject();
    double[] copy = new double[array.Length*2];//This length may be calculated
    Marshal.Copy(pointer, copy, 0, copy.Length);
}
finally
{
    if (handle.IsAllocated)
        handle.Free();
}

It's possible to write a generic method that can convert arrays of any compatible type (by "compatible" I mean "elements must be value types and the size of the elements must be compatible").

You can use P/Invoke to call the Windows API CopyMemory() method.

However, bear in mind that there may not be any performance advantage to doing it this way; you should perform careful timings to be sure.

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

public TOut[] ConvertArray<TIn, TOut>(TIn[] input) where TIn:struct where TOut:struct
{
    if (input == null)
        throw new ArgumentNullException("input");

    int sizeTIn   = Marshal.SizeOf(typeof(TIn));
    int sizeTOut  = Marshal.SizeOf(typeof(TOut));
    int sizeBytes = input.Length*sizeTIn;

    if ((sizeBytes % sizeTOut) != 0)
        throw new ArgumentException("Size of input type is not compatible with size of output type.");

    int sizeOut = sizeBytes/sizeTOut;

    var output = new TOut[sizeOut];

    GCHandle inHandle  = GCHandle.Alloc(input,  GCHandleType.Pinned);
    GCHandle outHandle = GCHandle.Alloc(output, GCHandleType.Pinned);

    try
    {
        IntPtr inPtr  = inHandle.AddrOfPinnedObject();
        IntPtr outPtr = outHandle.AddrOfPinnedObject();

        CopyMemory(outPtr, inPtr, (uint)sizeBytes);
    }

    finally
    {
        outHandle.Free();
        inHandle.Free();
    }

    return output;
}

For your example, you could call this like so:

Demo[] test = new Demo[10];

for (int i = 0; i < 10; ++i)
    test[i] = new Demo {X = i, Y = i};

var result = ConvertArray<Demo, double>(test);

for (int i = 0; i < 20; ++i)
    Console.WriteLine(result[i]);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top