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

时间:2015-03-23 20:34:17

标签: c# c++-cli interop unmanaged

我试图理解这个"它只是工作" C#/ C ++互操作的神奇之处,但目前IT只是一个小问题。

我正在使用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);
};

并在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:&#39;点&#39;:托管类型或功能无法在非托管功能中使用

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

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

1 个答案:

答案 0 :(得分:4)

这是托管代码的基石,托管编译器禁止对类型布局做出任何假设。只有这样,代码才能在不同的体系结构中实现可验证和类型安全。事实上,CLR会使用它,有意识地重新排序类型的成员,如果它产生更好的布局。

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

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

但是你没有使用pinvoke marshaller和复杂类型的编组,这不是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