从C ++本机插件更新float数组 [英] Update float array from C++ native plugin

查看:70
本文介绍了从C ++本机插件更新float数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试将数组从C ++传递到C#时,我看到一个非常奇怪的问题.我正在使用Marshal.Copy(具体是: https://msdn.microsoft.com/zh-CN/library/a53bd6cz(v=vs.110).aspx ).

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).

问题:从C ++到C#的浮点数组在结果数组中产生了几个NaN. (注意:我在Unity游戏引擎的上下文中工作)

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)

代码

示例C ++代码:

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

示例C#代码:

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


示例输出: 请看以下示例: C ++输出(print_out.csv):


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

123、456、789

123, 456, 789

C#输出(print_out_cs.csv):

C# output (print_out_cs.csv):

123,NaN,789

123, NaN, 789


我完全迷住了这个.我只是不明白为什么只有一些(大约7/100)浮点数返回NaN.是否有人有任何建议/见解可能会有所帮助?


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?

谢谢!

推荐答案

在您的代码中发现了一些问题:

Found few problems in your code:

1 . std::vector<float> results;在堆栈上声明.函数返回时它将消失.将其声明为指针

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

但请确保还声明一个将在C ++端释放它的函数.

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

2 .功能参数不匹配.

您的C ++:

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

您的C#:

getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);

您必须从C ++一侧删除CameraPose* pose或在C#一侧添加IntPtr pose.

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

3 . UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API的使用.

您不需要.当您要使用Unity的内置功能(例如 GL.IssuePluginEvent .在这种情况下,您将不使用它.

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.

这应该做到:

#define DLLExport __declspec(dllexport)

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

4 .C#具有垃圾回收器,可以在内存中四处移动变量.如果要从C ++端对其进行修改,则必须固定C#数组.您只需要固定C#数组即可.另一种选择是在C ++端分配数组,将其返回C#,将其复制到C#端的临时变量,然后在C ++端将其删除.

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 .将结果复制回数组,而不是分配给数组.

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

推荐方法:

仅有许多方法可以做到这一点,其中有些非常慢.如果要使用Marshal.Copy,则必须在C ++端分配数组,否则会遇到一些未定义的行为.

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.

最快和最有效的方法是在C#端将数组分配为全局变量.将数组及其长度传递给本机端.还传递第三个参数,C ++可以使用第三个参数来告诉C#已更新或写入的索引量.

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.

这比创建新数组,将其复制到C#变量然后在每次调用该函数时销毁它要好得多.

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

这是您应该使用的:

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();
    }
}

您还可以使用:std::copy ( data, data+count, results.begin() );而不是循环来复制数据.

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);
    }
}

用法:

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]
    }
}

这只是一个示例,它比我以前使用的所有其他方法都快.避免使用与Marshal.Copy当前相同的方式进行操作.如果您仍想按自己的方式进行操作或使用Marshal.Copy,则此处是需要分配的适当方法,在每次调用中复制数据并取消分配内存.

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.

这篇关于从C ++本机插件更新float数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆