У меня есть проект C #, который использует классы C ++ из библиотеки. Классы C # на самом деле являются обертками для классов C ++, они предоставляют функциональность C ++ клиентскому коду C #.
Во многих местах классы значений C ++ преобразуются в оболочки C # и обратно.
Во время обзора кода я нашел два способа преобразования классов: через reinterpret_cast (см. Оператор *) и через pin_ptr (см. MultiplyBy);
Как видите, у нативного и управляемого классов есть три «двойных» поля, поэтому кто-то использовал reinterpret_cast;
Во многих местах классы копируются из C # в C ++ с использованием memcpy:
memcpy (& NativePointInstance, & ManagedPointIntance, sizeof (double) * 3);
Я слышал от одного разработчика, что reinterpret_cast может быть безопасным в некоторых случаях, когда мы работаем с классами значений C #.
Вопрос в следующем:
Когда безопасно использовать reinterpret_cast для классов значений C #, а когда нет?
Каков наиболее правильный способ преобразования указателей в этом случае - как в операторе * или как в MultiplyBy, или другой альтернативе?
Может кто-нибудь подробно объяснить, что происходит в MultiplyBy (), как работает этот трюк?
Насколько я понял, проблема может быть вызвана тем, что оптимизатор может изменить порядок полей, GC может реорганизовать кучу, а выравнивание полей может отличаться для управляемого и собственного кода.
// this is C++ native class
class NativePoint
{
public:
double x;
double y;
double z;
NativePoint(double x, double y, double z)
{
this->x = x;
this->y = y;
this->z = z;
}
NativePoint operator * (int value)
{
return NativePoint(x * value, y * value, z * value);
}
};
// this class managed C++ class
[StructLayout(LayoutKind::Sequential)]
public value class ManagedPoint
{
internal:
double x;
double y;
double z;
ManagedPoint(const NativePoint& p)
{
x = p.x;
y = p.y;
z = p.z;
}
public:
static ManagedPoint operator * (ManagedPoint a, double value)
{
return ManagedPoint((*reinterpret_cast<NativePoint*>(&(a))) * value);
}
ManagedPoint MultiplyBy(double value)
{
pin_ptr<ManagedPoint> pThisTmp = &*this;
NativePoint* pThis = reinterpret_cast<NativePoint*>(&*pThisTmp);
return ManagedPoint(*pThis * value);
}
};
// this should be called from C# code, or another .NET app
int main(array<System::String ^> ^args)
{
NativePoint p_native = NativePoint(1, 1, 1);
ManagedPoint p = ManagedPoint(p_native);
Console::WriteLine("p is {" + p.x + ", " + p.y + ", " + p.z + "}");
ManagedPoint p1 = p * 5;
Console::WriteLine("p1 is {" + p1.x + ", " + p1.y + ", " + p1.z + "}");
ManagedPoint p2 = p.MultiplyBy(5);
Console::WriteLine("p2 is {" + p2.x + ", " + p2.y + ", " + p2.z + "}");
Console::ReadLine();
return 0;
}