c# - 如何将托管结构放入 C++/native 中

标签 c# c++-cli interop unmanaged

我试图理解 C#/C++ 互操作的“它就是有效”的魔力,但目前这只是一场噩梦。

我正在使用 Mandelbrot 计算,并希望将计算核心卸载到 native C++ 和 SSE2。使用 P/Invoke 可以实现这一点。现在我想换成 IJW,为了类型更安全,因为我想理解它。但当我触及 C++ 的表面时,已经是几十年前的事了。

我有一个struct Complex { double real;双像; } 保存 Mandelbrot 循环的起始值,我想调用这样的函数:

计算(int vectorSize,Complex[]点,double maxValue,int maxLoops,int[]结果)

现在我使用 VS Express 2013 创建了一个 CLR 类库,并将其放入头文件中:

public value struct Complex
{
    double real;
    double imag;
};

public ref class Computations
{
public:
    static void Basic(int vectorSize, array<Complex,1>^ points, double maxRadius, int maxLoops, array<int,1>^ result);
};

class NativeComputations
{
public:
    static void Basic(int vectorSize, Complex* points, double maxRadius, int maxLoops, int* result);
};

在 CPP 文件中:

#pragma managed
void Mandelbrot::Computations::Basic(int vectorSize, array<Complex,1>^ points, double maxRadius, int maxLoops, array<int,1>^ result)
{
    pin_ptr<Complex> pPoints = &points[0];
    pin_ptr<int> pResult = &result[0];
    NativeComputations::Basic(vectorSize, pPoints, maxRadius, maxLoops, pResult);
}

#pragma unmanaged
void Mandelbrot::NativeComputations::Basic(int vectorSize, Complex* points, double maxRadius, int maxLoops, int* result)
{
    double foo = points[0].real;
}

此时我陷入了困境 - 错误 C3821:'points':托管类型或函数不能在非托管函数中使用

所以我需要使用一些非托管的东西。我可以重复我的代码并声明一个 ComplexNative 结构(通过省略“value”关键字)。这是可行的,但是重复代码?即使是这样,将 Complex[] 转换为固定的 ComplexNative* 需要什么?

拜托,我不想将结构拆分为 double[] real、double[] imag。这可能会带来更简单的解决方法,但我想知道如何正确执行。

最佳答案

这是托管代码的基石,禁止托管编译器对类型布局做出任何假设。这是代码在不同架构之间可验证且类型安全的唯一方法。事实上,CLR 会利用它,有意地对类型的成员进行重新排序(如果这能产生更好的布局)。

因此,托管 Complex 结构无法转换为类似的 NativeComplex,编译器根本无法假设这些类型在任何方面都是相同的。这迫使您从 array<Complex> 复制数组。到 NativeComplex[] ,一次一个元素和一个成员。

嗯,这很不愉快。但你可以作弊。这样做并不是完全不合理,无论如何 native 代码都是不可验证的。并且你的结构声明有一个特殊的属性,它是一个blittable类型。这是一个昂贵的词,意味着 CLR 没有充分的理由实际选择不同的布局。 pinvoke 编码器需要知道结构是否实际上是 blittable 也是在运行时确定的。他的主要工作是做你想做的事情,从托管程序调用 native 代码并在必要时转换函数参数。

但是您没有使用 pinvoke 编码器,并且像这样的复杂类型编码不是 C++ Interop(又名 IJW)的内置功能。您必须自己调用它:

void Mandelbrot::Computations::Basic(int vectorSize, array<Complex,1>^ points, double maxRadius, int maxLoops, array<int,1>^ result)
{
    pin_ptr<Complex> pPoints = &points[0];
    NativeComplex* pNative = (NativeComplex*)pPoints;    // cheat
    pin_ptr<int> pResult = &result[0];
    NativeComputations::Basic(vectorSize, pNative, maxRadius, maxLoops, pResult);
}

这不太漂亮,但你会逃脱它,如果你想要快速的代码那么你就必须这样做。请记住,这绝不是对在所有情况下盲目转换指针的认可。惊喜确实存在,一个很好的例子是this question .

关于c# - 如何将托管结构放入 C++/native 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29219839/

相关文章:

c# - Xamarin IOS 堆栈 View

c# - Entity Framework : Dynamically generate multiple OR condition on JOIN

.net - 快速释放本地对象

python - 通过已注册的 TLB 从 python 访问未注册的 COM 对象

c# - 有人可以用 C# 解释 map-reduce 吗?

c# - 关于so​​ckets的listening和backlog的问题

.net - 如何清除 C++/CLI 中的事件订阅?

c++-cli - 如何通过 C++/CLI 将模板从 C++ 公开到 C#

c# - Windows Phone 7 消息或图库事件

将 C 函数和 DLL 结构类型正确转换/编码为 C#