c# - 从 C++ native 插件更新 float 组

标签 c# c++ unity3d interop

我在尝试将数组从 C++ 传递到 C# 时遇到一个非常奇怪的问题。我正在使用 Marshal.Copy(特别是:https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx)。

问题:从 C++ 到 C# 的 float 组在生成的数组中产生一些 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):

123, 456, 789

C# 输出(print_out_cs.csv):

123, NaN, 789


我完全被这个难住了。我只是不明白为什么只有一些(大约 7/100) float 返回 NaN。有没有人有任何可能有帮助的建议/见解?

谢谢!

最佳答案

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

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

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

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

2.函数参数不匹配

你的 C++:

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

你的 C#:

getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);

您要么必须删除 CameraPose* pose从 C++ 端或添加 IntPtr pose到 C# 端。

3UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API的使用.

你不需要那个。当您想使用 Unity 的内置函数(例如 GL.IssuePluginEvent )时使用此选项。 .在这种情况下,您没有使用它。

应该这样做:

#define DLLExport __declspec(dllexport)

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

4.C# 有一个垃圾收集器,可以在内存中移动变量。如果你想从 C++ 端修改它,你必须固定 C# 数组。您只需要固定 C# 数组。另一种选择是在 C++ 端分配数组,将其返回给 C#,将其复制到 C# 端的临时变量,然后在 C++ 端删除它。

5.将结果复制回数组而不是赋值。



推荐方法:

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

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

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

这是你应该使用的:

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() );而不是循环复制数据。

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然后 here是执行此操作的适当方法,它需要在每次调用中分配、复制数据和取消分配内存。

关于c# - 从 C++ native 插件更新 float 组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44423178/

相关文章:

c# - 从代码访问 Expression.DebugView

C# MetroTile 背景色/前色在鼠标进入/离开期间不改变

c++ - 未找到 libFLAC 符号和 vtable 错误

c++ - 在 vector 中找到最近的点

java - Unity AndroidJavaObject 无法正常工作而不显示错误

c# - 为什么 Enumerable.OrderBy<TSource, TKey> 方法在不使用比较器时工作得更快

c++ - 全局对象与单例模式

c# - 为什么我没有被注册为客户?

c# - 仅在一侧缩放游戏对象

c# - Entity Framework : Unknown column in 'where clause