关于c#:如何将托管结构体转换为C ++ / native

How to get a managed struct into C++/native

我试图理解这一点,它对C/C++交互操作有魔力,但目前只是一场噩梦。

我正在玩曼德尔布罗特计算,想卸载计算核心到本机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类库,并将其放入头文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
#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:"点":托管类型或函数不能在非托管函数中使用

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

我不想把这个结构分解成一个双[]实的,双[]imag。这可能会导致一个更简单的解决方法,但我想知道如何正确地进行。


这是托管代码的基础,禁止托管编译器对类型布局进行任何假设。只有这样,代码才能在不同的体系结构中进行验证和类型安全。事实上,clr也在玩弄它,故意重新排序类型的成员,如果这样可以产生更好的布局。

因此,托管复杂结构不能转换为类似的NativeComplex,编译器不能简单地假设这些类型在任何方面都是相同的。它强制您将数组从一个array复制到一个NativeComplex[],一次一个元素和一个成员。

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

但是,您没有使用pJoCK封送器和复杂类型封送处理,这不是C++互操作(AKA IJW)的内置特性。您必须自己调用它:

1
2
3
4
5
6
7
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);
}

这不好看,但你会逃脱的,如果你想要快速的代码,那么你必须。请记住,这在任何情况下都不是盲目投出指针的背书。惊喜确实存在,这个问题就是一个很好的例子。