如何将托管结构转换为 C++/native

Rol*_*olf 2 c# interop unmanaged c++-cli

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

我正在玩 Mandelbrot 计算,并希望将计算核心卸载到本机 C++ 和 SSE2。这是工作,与 P/Invoke。现在我想更改为 IJW,以便更安全,因为我想了解它。但我接触 C++ 的表面已经是几十年前的事了。

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

Compute(int vectorSize, Complex[] points, double maxValue, int maxLoops, int[] result)

现在我使用 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);
};
Run Code Online (Sandbox Code Playgroud)

在 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;
}
Run Code Online (Sandbox Code Playgroud)

在这一点上我被卡住了 - 错误 C3821: 'points': managed type or function cannot be used in an unmanaged function

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

拜托,我不想将结构拆分为 double[] real, double[] 图像。这可能会导致更简单的解决方法,但我想知道如何正确地做到这一点。

Han*_*ant 5

这是托管代码的基石,禁止托管编译器对类型布局进行任何假设。只有这样,代码才能跨不同架构进行验证和类型安全。事实上,CLR 会玩弄它,有意地重新排序类型的成员,如果这会产生更好的布局。

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

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

但是您没有使用 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);
}
Run Code Online (Sandbox Code Playgroud)

这不是很好,但你会逃脱它,如果你想要快速的代码,那么你将不得不这样做。请记住,这绝不是对在所有情况下盲目投射指针的认可。惊喜确实存在,这个问题就是一个很好的例子。