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

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

问题描述

在尝试将数组从 C++ 传递到 C# 时,我看到了一个非常奇怪的问题.我正在使用 Marshal.Copy(特别是:https://msdn.microsoft.com/en-us/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 游戏引擎的上下文中工作)

代码

示例 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::vectorresults; 是在栈上声明的.函数返回时它将消失.将其声明为指针

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* 姿势,要么将 IntPtr 姿势 添加到 C# 端.

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() ); 而不是 loop 来复制数据.

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++ 本机插件更新浮点数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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