Update Float Array from C++ Native Plugin

Update float array from C++ native plugin

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.

Update Vector3 array from C++ native plugin

Do I need to manually convert float[] to UnityEngine.Vector3[]? Or is
there a simpler and faster way that directly do this?

Yes, you have to do that manually but there is a better way to do this.

I will assume that you need to modify a Vector3 on the native side then return the result. There is no need to use float array for this. Just create a Vector3 struct on the C++ side then use pointer to pass it between C++ and C#. Do not return the Vector3 array from C++, create it on the C# side, then pass it to the C++ function to modify and apply the changes to the argument.

C++:

This requires that you enable the unsafe keyword in Unity.

struct Vector3
{
float x;
float y;
float z;
};

then your function:

extern "C" void UpdateVectorArray(Vector3* vecArray, int vecSize)
{
for(int i = 0; i < vecSize; ++i)
{
//Simply modify each Vector reference
Vector3 &vec = vecArray[i];
vec.x = 11;
vec.y = 20;
vec.z = 55;
}
}

C#:

[DllImport("Plugin Name")]
static unsafe extern void UpdateVectorArray(Vector3* vecArray, int vecSize);

void UpdateVectorArray(Vector3[] vecArray)
{
unsafe
{
//Pin array then send to C++
fixed (Vector3* vecPtr = vecArray)
{
UpdateVectorArray(vecPtr, vecArray.Length);
}
}
}

Usage:

Get vertices from model, send to C++ and modify it the re-assign the modified mesh:

void Start()
{
Mesh mesh = GetComponent<MeshFilter>().mesh;

Vector3[] vertices = mesh.vertices;
UpdateVectorArray(vertices);

//Re-assign the modified mesh
mesh.vertices = vertices;
mesh.RecalculateBounds();
}

To avoid using the unsafe keyword in Unity use the [In, Out] attribute.

[DllImport("Plugin Name")]
static extern void UpdateVectorArray([In, Out] Vector3[] vecArray, int vecSize);

void Start()
{
Mesh mesh = GetComponent<MeshFilter>().mesh;

Vector3[] vertices = mesh.vertices;
UpdateVectorArray(vertices, vertices.Length);

mesh.vertices = vertices;
mesh.RecalculateBounds();
}

The C++ side remains the-same. You can also use the GCHandle to pin the array and avoid using the unsafe keyword but the unsafe keyword solution is better and faster.

c# how to make a very long float array usable

Generally, text editors don't handle very long lines well. Simply split the statement up into multiple lines.

Sending float* from Objective C to C#

Let me describe how I implement callback from Objective C to C#.

Inside UnityBridge.h and UnityBridge.mm:

typedef void (*UnityCommandCallback) ();

#ifdef __cplusplus
extern "C" {
#endif
void ConnectCallback(UnityCommandCallback callbackMethod);
void CallMethod();
#ifdef __cplusplus
}
#endif

#import "UnityBridge.h"
//THis is the implementation of methods in the header file
//A static variable to hold our function pointer
static UnityCommandCallback LastCallBack = NULL;
void ConnectCallback(UnityCommandCallback callbackMethod)
{
LastCallBack = callbackMethod;//a simple assignment using the connect method

}

void CallMethod()
{
if(LastCallBack != NULL)
LastCallBack(/*objectName, methodName, parameter*/);
}

So this LastCallBack memorises the function to call back whenever the event happens at Objective C side.

Inside BLEObject.h, BLEObject.m:

On top of BLEObject.m, there is a decalaration.

extern void CallMethod();

So whenever, there is an event, you call this CallMethod().
Since the last call back function is memorised, it will always go back to the same place you want in C#.

Inside C#:
You have this

public class BLEBridge : MonoBehaviour {

public delegate void UnityCallbackDelegate(/*IntPtr objectName, IntPtr methodName, IntPtr parameter*/);
[DllImport ("__Internal")]
public static extern void ConnectCallback(UnityCallbackDelegate callbackMethod);
[MonoPInvokeCallback(typeof(UnityCallbackDelegate))]
private static void ManagedTest()
{
Debug.Log("IT WORKS!!!!");
}
}

During the initialisation of the C# Object, we call this

public static void TestSendMsg()
{
ConnectCallback (ManagedTest);
}

So Objective C side knows

`ManagedTest()` is the function to call back whenever an event happens at Objective C side.

That is how I implement Callback from Objective C going through C++ interface to C# Unity side.

Pass large arrays of structs from C# unity script to C++ dll using GCHandle crashes after C++ function execution

The problem is probably in the return type bool... C++ bool is 1 byte, C# bool is 4 byte.

Try:

[DllImport(DLL, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool PopulateVerts([In] IntPtr verts, int numOfVertices);

And then there is a problem with the length of buffer:

extern "C" EXPORT_API bool PopulateVerts(SimpleVector3* verts, int numofVert) {
char buffer[250];
for (int i = 0; i < numofVert; i++) {
_snprintf(buffer, 250, "x %f , y %f , z %f \n nx %f , ny %f , nz %f ", (verts->Vx),
(verts->Vy), (verts->Vz), (verts->Nx), (verts->Ny), (verts->Nz));
// See the security note at https://msdn.microsoft.com/en-us/library/2ts7cx93(v=vs.71).aspx
buffer[sizeof(buffer) - 1] = '\0';

Debug(buffer);
if(i < (numofVert-1)){
verts++;
}
}
return true;
}

Passing byte array from C++ unmanaged dll to C# unity

There are many ways to return byte arrays from C# and below is one of them. The memory allocation and de-allocation are both done in C++. You must call the function to free the memory from C#. I made the example very simple so that you can easily integrate it in your current code.

IntPtr is the key in this answer.

C++:

char* getByteArray() 
{
//Create your array(Allocate memory)
char * arrayTest = new char[2];

//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;

//Return it
return arrayTest;
}

int freeMem(char* arrayPtr){
delete[] arrayPtr;
return 0;
}

C#:

[DllImport("Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();

[DllImport("Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);

//Test
void Start() {
//Call and return the pointer
IntPtr returnedPtr = getIntArray();

//Create new Variable to Store the result
byte[] returnedResult = new byte[2];

//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);

//Free native memory
freeMem(returnedPtr);

//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}

How to properly marshal strings from Unity to C/C++?

1.No.

2.You have to do that yourself.

3.Yes

If you allocate memory inside a function on the C or C++ side, you must free it. I don't see any code allocating memory on the side but I assume you left that part. Also, do not return a variable declared on the stack to C#. You will end up with undefined behavior including crashes.

Here is a C++ solution for this.

For the C solution:

char* getByteArray() 
{
//Create your array(Allocate memory)
char * arrayTest = malloc( 2 * sizeof(char) );

//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;

//Return it
return arrayTest;
}

int freeMem(char* arrayPtr){
free(arrayPtr);
return 0;
}

The only difference is that the C version uses malloc and free function to allocate and de-allocate memory.

C#:

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);

//Test
void Start()
{
//Call and return the pointer
IntPtr returnedPtr = getIntArray();

//Create new Variable to Store the result
byte[] returnedResult = new byte[2];

//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);

//Free native memory
freeMem(returnedPtr);

//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}

Note that this is only a test that uses char with 2 characters only. You can make the size of the string dynamic by adding a out int outValue parameter to the C# function then adding int* outValue parameter to the C function. You can then write to this parameter on the C side the size of the character is and access that size from the C# side.

This size can then be passed to the last argument of the Marshal.Copy function and remove the current hard-coded 2 value limit. I will leave this for you to do but if confused, see this post for example of that.


The better solution is to pass StringBuilder to the native side then write to it. The bad side is that you have to declare the size of the StringBuilder on time.

C++:

void __cdecl  _GetLookupStatus (char* data, size_t size) 
{
strcpy_s(data, size, "Test");
}

C#:

[DllImport("__Internal", CallingConvention = CallingConvention.Cdecl)]
public static extern int _GetLookupStatus(StringBuilder data, int size);

//Test
void Start()
{
StringBuilder buffer = new StringBuilder(500);
_GetLookupStatus (buffer, buffer.Capacity);
string result = buffer.ToString();
}

If you are looking for the fastest way then you should use char array on the C# side, pin it on C# side then send it to C as IntPtr. On the C side, you can use strcpy_s to modify the char array. That way, no memory is allocated on the C side. You are just re-using the memory of the char array from C#. You can see the float[] example at the end of the answer here.



Related Topics



Leave a reply



Submit