Update float array from C++ native plugin











up vote
1
down vote

favorite












I am seeing a pretty bizarre issue while trying to pass an array from C++ to C#. I am using Marshal.Copy (specifically: https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx).



Problem: float array from C++ to C# is yielding a few NaN's in the resulting array.
(Note: I am working in the context of the Unity game engine)





Code



Example C++ code:



extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
std::vector<float> results;
std::vector<SOME_TYPE> key_points = <SOME_POINTS>

for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
results.push_back(static_cast<float>(iter->pt.x));
results.push_back(static_cast<float>(iter->pt.y));
}

*points = results.data();
*count = results.size();

//<Print results to csv here>

return true;
}


Example C# code:



[DllImport("NativePlugin")]
private static extern bool getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);

private static float getFloatArrayFromNative() {
IntPtr ptrResultItems = IntPtr.Zero;
int resultItemsLength = 0;
bool success = getSomeFloats (ref ptrResultItems, ref resultItemsLength);
float resultItems = null;
if (success) {
// Load the results into a managed array.
resultItems = new float[resultItemsLength];
Marshal.Copy (ptrResultItems
, resultItems
, 0
, resultItemsLength);

// <PRINT out resultItems to csv here>

return resultItems;
} else {
Debug.Log ("Something went wrong getting some floats");
return new float { -1, -2 };
}
}




Example Ouput:
Take the following example:
C++ output (print_out.csv):




123, 456, 789




C# output (print_out_cs.csv):




123, NaN, 789






I'm completely stumped on this one. I just don't understand why only some (roughly 7/100) floats are returning NaN. Does anyone have any advice/insight that might help?



Thanks!










share|improve this question
























  • Are those values real values? (the ones on the example)
    – Gusman
    Jun 7 '17 at 21:36












  • @Gusman yes, those are real values. The values can range from 0 to maybe 5000.
    – Brent McFerrin
    Jun 8 '17 at 1:39















up vote
1
down vote

favorite












I am seeing a pretty bizarre issue while trying to pass an array from C++ to C#. I am using Marshal.Copy (specifically: https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx).



Problem: float array from C++ to C# is yielding a few NaN's in the resulting array.
(Note: I am working in the context of the Unity game engine)





Code



Example C++ code:



extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
std::vector<float> results;
std::vector<SOME_TYPE> key_points = <SOME_POINTS>

for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
results.push_back(static_cast<float>(iter->pt.x));
results.push_back(static_cast<float>(iter->pt.y));
}

*points = results.data();
*count = results.size();

//<Print results to csv here>

return true;
}


Example C# code:



[DllImport("NativePlugin")]
private static extern bool getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);

private static float getFloatArrayFromNative() {
IntPtr ptrResultItems = IntPtr.Zero;
int resultItemsLength = 0;
bool success = getSomeFloats (ref ptrResultItems, ref resultItemsLength);
float resultItems = null;
if (success) {
// Load the results into a managed array.
resultItems = new float[resultItemsLength];
Marshal.Copy (ptrResultItems
, resultItems
, 0
, resultItemsLength);

// <PRINT out resultItems to csv here>

return resultItems;
} else {
Debug.Log ("Something went wrong getting some floats");
return new float { -1, -2 };
}
}




Example Ouput:
Take the following example:
C++ output (print_out.csv):




123, 456, 789




C# output (print_out_cs.csv):




123, NaN, 789






I'm completely stumped on this one. I just don't understand why only some (roughly 7/100) floats are returning NaN. Does anyone have any advice/insight that might help?



Thanks!










share|improve this question
























  • Are those values real values? (the ones on the example)
    – Gusman
    Jun 7 '17 at 21:36












  • @Gusman yes, those are real values. The values can range from 0 to maybe 5000.
    – Brent McFerrin
    Jun 8 '17 at 1:39













up vote
1
down vote

favorite









up vote
1
down vote

favorite











I am seeing a pretty bizarre issue while trying to pass an array from C++ to C#. I am using Marshal.Copy (specifically: https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx).



Problem: float array from C++ to C# is yielding a few NaN's in the resulting array.
(Note: I am working in the context of the Unity game engine)





Code



Example C++ code:



extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
std::vector<float> results;
std::vector<SOME_TYPE> key_points = <SOME_POINTS>

for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
results.push_back(static_cast<float>(iter->pt.x));
results.push_back(static_cast<float>(iter->pt.y));
}

*points = results.data();
*count = results.size();

//<Print results to csv here>

return true;
}


Example C# code:



[DllImport("NativePlugin")]
private static extern bool getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);

private static float getFloatArrayFromNative() {
IntPtr ptrResultItems = IntPtr.Zero;
int resultItemsLength = 0;
bool success = getSomeFloats (ref ptrResultItems, ref resultItemsLength);
float resultItems = null;
if (success) {
// Load the results into a managed array.
resultItems = new float[resultItemsLength];
Marshal.Copy (ptrResultItems
, resultItems
, 0
, resultItemsLength);

// <PRINT out resultItems to csv here>

return resultItems;
} else {
Debug.Log ("Something went wrong getting some floats");
return new float { -1, -2 };
}
}




Example Ouput:
Take the following example:
C++ output (print_out.csv):




123, 456, 789




C# output (print_out_cs.csv):




123, NaN, 789






I'm completely stumped on this one. I just don't understand why only some (roughly 7/100) floats are returning NaN. Does anyone have any advice/insight that might help?



Thanks!










share|improve this question















I am seeing a pretty bizarre issue while trying to pass an array from C++ to C#. I am using Marshal.Copy (specifically: https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx).



Problem: float array from C++ to C# is yielding a few NaN's in the resulting array.
(Note: I am working in the context of the Unity game engine)





Code



Example C++ code:



extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
std::vector<float> results;
std::vector<SOME_TYPE> key_points = <SOME_POINTS>

for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
results.push_back(static_cast<float>(iter->pt.x));
results.push_back(static_cast<float>(iter->pt.y));
}

*points = results.data();
*count = results.size();

//<Print results to csv here>

return true;
}


Example C# code:



[DllImport("NativePlugin")]
private static extern bool getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);

private static float getFloatArrayFromNative() {
IntPtr ptrResultItems = IntPtr.Zero;
int resultItemsLength = 0;
bool success = getSomeFloats (ref ptrResultItems, ref resultItemsLength);
float resultItems = null;
if (success) {
// Load the results into a managed array.
resultItems = new float[resultItemsLength];
Marshal.Copy (ptrResultItems
, resultItems
, 0
, resultItemsLength);

// <PRINT out resultItems to csv here>

return resultItems;
} else {
Debug.Log ("Something went wrong getting some floats");
return new float { -1, -2 };
}
}




Example Ouput:
Take the following example:
C++ output (print_out.csv):




123, 456, 789




C# output (print_out_cs.csv):




123, NaN, 789






I'm completely stumped on this one. I just don't understand why only some (roughly 7/100) floats are returning NaN. Does anyone have any advice/insight that might help?



Thanks!







c# c++ unity3d interop






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 7 at 19:03









Programmer

74k1077139




74k1077139










asked Jun 7 '17 at 21:35









Brent McFerrin

50339




50339












  • Are those values real values? (the ones on the example)
    – Gusman
    Jun 7 '17 at 21:36












  • @Gusman yes, those are real values. The values can range from 0 to maybe 5000.
    – Brent McFerrin
    Jun 8 '17 at 1:39


















  • Are those values real values? (the ones on the example)
    – Gusman
    Jun 7 '17 at 21:36












  • @Gusman yes, those are real values. The values can range from 0 to maybe 5000.
    – Brent McFerrin
    Jun 8 '17 at 1:39
















Are those values real values? (the ones on the example)
– Gusman
Jun 7 '17 at 21:36






Are those values real values? (the ones on the example)
– Gusman
Jun 7 '17 at 21:36














@Gusman yes, those are real values. The values can range from 0 to maybe 5000.
– Brent McFerrin
Jun 8 '17 at 1:39




@Gusman yes, those are real values. The values can range from 0 to maybe 5000.
– Brent McFerrin
Jun 8 '17 at 1:39












2 Answers
2






active

oldest

votes

















up vote
4
down vote



accepted










Found few problems in your code:



1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer



std::vector<float> *results = new std::vector<float>(10);


but make sure to also declare a function that will free it on the C++ side.



2.The function parameter do not match.



Your C++:



getSomeFloats(float** points, int* count, CameraPose* pose)


Your C#:



getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);


You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.



3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.



You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.



This should do it:



#define DLLExport __declspec(dllexport)

extern "C"
{
DLLExport void fillArrayNative(float* data, int count, int* outValue)
{
}
}


4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.



5.Copy the result back to the array instead of assigning it.







Recommended method:



There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.



The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.



This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.



This is what you should be using:



C++:



#define DLLExport __declspec(dllexport)

extern "C"
{
DLLExport void fillArrayNative(float* data, int count, int* outValue)
{
std::vector<float> results;
for (int i = 0; i < count; i++)
{
//Fill the array
data[i] = results[i];
}
*outValue = results.size();
}
}


You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.



C#:



[DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
private static extern void fillArrayNative(IntPtr data, int count, out int outValue);

public unsafe void getFillArrayNative(float outArray, int count, out int outValue)
{
//Pin Memory
fixed (float* p = outArray)
{
fillArrayNative((IntPtr)p, count, out outValue);
}
}


Usage:



const int arraySize = 44500;
float arrayToFill = new float[arraySize];

void Start()
{
int length = arrayToFill.Length;
int filledAmount = 0;
getFillArrayNative(arrayToFill, length, out filledAmount);

//You can then loop through it with with the returned filledAmount
for (int i = 0; i < filledAmount; i++)
{
//Do something with arrayToFill[i]
}
}


This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.






share|improve this answer























  • Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
    – Brent McFerrin
    Jun 8 '17 at 1:32












  • There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
    – Programmer
    Jun 8 '17 at 1:49










  • @BrentMcFerrin Did it work?
    – Programmer
    Jun 9 '17 at 16:44










  • I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
    – Brent McFerrin
    Jun 14 '17 at 14:32




















up vote
0
down vote













The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).






share|improve this answer





















    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














     

    draft saved


    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f44423178%2fupdate-float-array-from-c-native-plugin%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    4
    down vote



    accepted










    Found few problems in your code:



    1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer



    std::vector<float> *results = new std::vector<float>(10);


    but make sure to also declare a function that will free it on the C++ side.



    2.The function parameter do not match.



    Your C++:



    getSomeFloats(float** points, int* count, CameraPose* pose)


    Your C#:



    getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);


    You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.



    3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.



    You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.



    This should do it:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    }
    }


    4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.



    5.Copy the result back to the array instead of assigning it.







    Recommended method:



    There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.



    The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.



    This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.



    This is what you should be using:



    C++:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    std::vector<float> results;
    for (int i = 0; i < count; i++)
    {
    //Fill the array
    data[i] = results[i];
    }
    *outValue = results.size();
    }
    }


    You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.



    C#:



    [DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
    private static extern void fillArrayNative(IntPtr data, int count, out int outValue);

    public unsafe void getFillArrayNative(float outArray, int count, out int outValue)
    {
    //Pin Memory
    fixed (float* p = outArray)
    {
    fillArrayNative((IntPtr)p, count, out outValue);
    }
    }


    Usage:



    const int arraySize = 44500;
    float arrayToFill = new float[arraySize];

    void Start()
    {
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
    //Do something with arrayToFill[i]
    }
    }


    This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.






    share|improve this answer























    • Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
      – Brent McFerrin
      Jun 8 '17 at 1:32












    • There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
      – Programmer
      Jun 8 '17 at 1:49










    • @BrentMcFerrin Did it work?
      – Programmer
      Jun 9 '17 at 16:44










    • I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
      – Brent McFerrin
      Jun 14 '17 at 14:32

















    up vote
    4
    down vote



    accepted










    Found few problems in your code:



    1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer



    std::vector<float> *results = new std::vector<float>(10);


    but make sure to also declare a function that will free it on the C++ side.



    2.The function parameter do not match.



    Your C++:



    getSomeFloats(float** points, int* count, CameraPose* pose)


    Your C#:



    getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);


    You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.



    3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.



    You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.



    This should do it:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    }
    }


    4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.



    5.Copy the result back to the array instead of assigning it.







    Recommended method:



    There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.



    The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.



    This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.



    This is what you should be using:



    C++:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    std::vector<float> results;
    for (int i = 0; i < count; i++)
    {
    //Fill the array
    data[i] = results[i];
    }
    *outValue = results.size();
    }
    }


    You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.



    C#:



    [DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
    private static extern void fillArrayNative(IntPtr data, int count, out int outValue);

    public unsafe void getFillArrayNative(float outArray, int count, out int outValue)
    {
    //Pin Memory
    fixed (float* p = outArray)
    {
    fillArrayNative((IntPtr)p, count, out outValue);
    }
    }


    Usage:



    const int arraySize = 44500;
    float arrayToFill = new float[arraySize];

    void Start()
    {
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
    //Do something with arrayToFill[i]
    }
    }


    This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.






    share|improve this answer























    • Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
      – Brent McFerrin
      Jun 8 '17 at 1:32












    • There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
      – Programmer
      Jun 8 '17 at 1:49










    • @BrentMcFerrin Did it work?
      – Programmer
      Jun 9 '17 at 16:44










    • I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
      – Brent McFerrin
      Jun 14 '17 at 14:32















    up vote
    4
    down vote



    accepted







    up vote
    4
    down vote



    accepted






    Found few problems in your code:



    1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer



    std::vector<float> *results = new std::vector<float>(10);


    but make sure to also declare a function that will free it on the C++ side.



    2.The function parameter do not match.



    Your C++:



    getSomeFloats(float** points, int* count, CameraPose* pose)


    Your C#:



    getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);


    You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.



    3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.



    You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.



    This should do it:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    }
    }


    4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.



    5.Copy the result back to the array instead of assigning it.







    Recommended method:



    There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.



    The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.



    This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.



    This is what you should be using:



    C++:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    std::vector<float> results;
    for (int i = 0; i < count; i++)
    {
    //Fill the array
    data[i] = results[i];
    }
    *outValue = results.size();
    }
    }


    You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.



    C#:



    [DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
    private static extern void fillArrayNative(IntPtr data, int count, out int outValue);

    public unsafe void getFillArrayNative(float outArray, int count, out int outValue)
    {
    //Pin Memory
    fixed (float* p = outArray)
    {
    fillArrayNative((IntPtr)p, count, out outValue);
    }
    }


    Usage:



    const int arraySize = 44500;
    float arrayToFill = new float[arraySize];

    void Start()
    {
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
    //Do something with arrayToFill[i]
    }
    }


    This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.






    share|improve this answer














    Found few problems in your code:



    1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer



    std::vector<float> *results = new std::vector<float>(10);


    but make sure to also declare a function that will free it on the C++ side.



    2.The function parameter do not match.



    Your C++:



    getSomeFloats(float** points, int* count, CameraPose* pose)


    Your C#:



    getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);


    You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.



    3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.



    You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.



    This should do it:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    }
    }


    4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.



    5.Copy the result back to the array instead of assigning it.







    Recommended method:



    There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.



    The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.



    This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.



    This is what you should be using:



    C++:



    #define DLLExport __declspec(dllexport)

    extern "C"
    {
    DLLExport void fillArrayNative(float* data, int count, int* outValue)
    {
    std::vector<float> results;
    for (int i = 0; i < count; i++)
    {
    //Fill the array
    data[i] = results[i];
    }
    *outValue = results.size();
    }
    }


    You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.



    C#:



    [DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
    private static extern void fillArrayNative(IntPtr data, int count, out int outValue);

    public unsafe void getFillArrayNative(float outArray, int count, out int outValue)
    {
    //Pin Memory
    fixed (float* p = outArray)
    {
    fillArrayNative((IntPtr)p, count, out outValue);
    }
    }


    Usage:



    const int arraySize = 44500;
    float arrayToFill = new float[arraySize];

    void Start()
    {
    int length = arrayToFill.Length;
    int filledAmount = 0;
    getFillArrayNative(arrayToFill, length, out filledAmount);

    //You can then loop through it with with the returned filledAmount
    for (int i = 0; i < filledAmount; i++)
    {
    //Do something with arrayToFill[i]
    }
    }


    This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Nov 7 at 18:57

























    answered Jun 7 '17 at 23:46









    Programmer

    74k1077139




    74k1077139












    • Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
      – Brent McFerrin
      Jun 8 '17 at 1:32












    • There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
      – Programmer
      Jun 8 '17 at 1:49










    • @BrentMcFerrin Did it work?
      – Programmer
      Jun 9 '17 at 16:44










    • I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
      – Brent McFerrin
      Jun 14 '17 at 14:32




















    • Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
      – Brent McFerrin
      Jun 8 '17 at 1:32












    • There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
      – Programmer
      Jun 8 '17 at 1:49










    • @BrentMcFerrin Did it work?
      – Programmer
      Jun 9 '17 at 16:44










    • I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
      – Brent McFerrin
      Jun 14 '17 at 14:32


















    Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
    – Brent McFerrin
    Jun 8 '17 at 1:32






    Thank you for the detailed response. I will look into your recommendation and report back if I get something working well. Oh, I have updated the C++ method signature as it was incorrect.
    – Brent McFerrin
    Jun 8 '17 at 1:32














    There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
    – Programmer
    Jun 8 '17 at 1:49




    There shouldn't be any problem. Let me know if there is any. Remember to start slow, test then add more code and test so that you will know exactly what's going on if it stops working.
    – Programmer
    Jun 8 '17 at 1:49












    @BrentMcFerrin Did it work?
    – Programmer
    Jun 9 '17 at 16:44




    @BrentMcFerrin Did it work?
    – Programmer
    Jun 9 '17 at 16:44












    I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
    – Brent McFerrin
    Jun 14 '17 at 14:32






    I was hoping to try your "recommended method" before reporting back, but haven't had the time. Nevertheless, even following the general advice from your first point was enough to get me by for now. I will report back with the results of the recommended method when I get around to it. Thanks for your help!
    – Brent McFerrin
    Jun 14 '17 at 14:32














    up vote
    0
    down vote













    The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).






    share|improve this answer

























      up vote
      0
      down vote













      The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).






      share|improve this answer























        up vote
        0
        down vote










        up vote
        0
        down vote









        The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).






        share|improve this answer












        The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Jun 7 '17 at 22:35









        1201ProgramAlarm

        16k42438




        16k42438






























             

            draft saved


            draft discarded



















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f44423178%2fupdate-float-array-from-c-native-plugin%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            這個網誌中的熱門文章

            Hercules Kyvelos

            Tangent Lines Diagram Along Smooth Curve

            Yusuf al-Mu'taman ibn Hud