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!
c# c++ unity3d interop
add a comment |
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!
c# c++ unity3d interop
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
add a comment |
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!
c# c++ unity3d interop
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
c# c++ unity3d interop
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
add a comment |
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
add a comment |
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.
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
add a comment |
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).
add a comment |
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.
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
add a comment |
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.
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
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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).
add a comment |
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).
add a comment |
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).
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).
answered Jun 7 '17 at 22:35
1201ProgramAlarm
16k42438
16k42438
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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