从System :: String ^从结构句柄成员向std :: string封送 [英] Marshal to std::string from System::String^ member of struct handle
问题描述
我正试图从System :: String ^编组为std :: string.
I am trying to marshal to a std::string from a System::String^.
通常,这可以通过marshal_as<T>
模板完成,即
Usually this can be done with the marshal_as<T>
template i.e.
System::String ^managedString = "test";
std::string stdString = marshal_as<std::string>(managedString);
但是,如果String ^是引用(即引用)所访问的结构的一部分,则
But if the String^ is part of a struct that is accessed by reference i.e.
value struct SomeStruct {
String ^managedString;
};
auto structHandle = gcnew SomeStruct();
structHandle->managedString = "test";
std::string stdString = marshal_as<std::string>(structHandle->managedString);
编译器将引发以下错误
错误C2665:'msclr :: interop :: marshal_as':3个重载都不 可以转换所有参数类型
error C2665: 'msclr::interop::marshal_as' : none of the 3 overloads could convert all the argument types
但是,如果该结构不是句柄,而是实际的结构,则可以正常工作.
However if the struct is not a handle, but the actual struct, it works fine.
推荐答案
auto structHandle = gcnew SomeStruct();
这是问题开始的地方. structHandle 引用指向SomeStruct的盒装副本.由于SomeStruct是值类型,因此需要将其装箱,该结构的副本存储在GC堆上.
This is where the problem started. The structHandle reference points to a boxed copy of SomeStruct. It needs to be boxed because SomeStruct is a value type, the copy of the struct is stored on the GC heap.
您尝试使用的marshal_as<>重载的签名是:
The signature of the marshal_as<> overload you are trying to use is:
marshal_as<std::string,System::String^>(System::String ^const &)
const&
是问题,您无法获取对该成员的非托管引用,其地址不稳定(不是const),因为垃圾收集器可以在执行marshal_as<的同时移动对象.当marshal_as现在取消引用不再存在的对象时,这将导致灾难.
The const&
is the problem, you cannot get an unmanaged reference to the member, its address is not stable (not const) since the garbage collector can move the object while marshal_as<> is executing. That would cause disaster when marshal_as now dereferences an object that's no longer there.
一种解决方法是在尝试转换引用之前,将引用从盒装对象中复制出来:
A workaround is to copy the reference out of the boxed object before you try to convert it:
structHandle->managedString = "test";
String^ refCopy = structHandle->managedString;
std::string stdString = marshal_as<std::string>(refCopy); // fine
但这只是针对您代码中实际问题的一种破解方法.存在值类型以提高代码效率,从而允许将结构存储在堆栈帧或CPU寄存器中.就像本地C ++对象一样.通过将结构装箱,您将失去这一优势.换句话说,如果您不打算将value struct
视为值,则毫无意义地声明它.正确使用它进行正确的修复:
But that's just a hack around the real problem in your code. Value types exist to make code efficient, allowing structures to be stored on the stack frame or a CPU register. Just like a native C++ object. You are throwing that advantage away by boxing the struct. Or in other words, there just wasn't any point in declaring a value struct
if you are not going to treat it as a value. Use it properly for the correct fix:
SomeStruct value;
value.managedString = "test";
auto result = marshal_as<std::string>(value.managedString); // fine
或者,如果您在函数参数上错误地使用^来获得引用,则重写为:
Or if you got the reference by mistakenly using ^ on a function argument then rewrite to:
void SomeFunction(SomeStruct% arg) {
auto nativeString = marshal_as<std::string>(arg.managedString);
'' etc..
}
请注意使用%而不是^通过引用传递变量.但是不要那样做,值类型的要点是复制值比必须取消引用指针来获取值要便宜.确保您的值类型不太大,最多不能包含4个字段.如果较大,则应使用引用类型.
Note the use of % instead of ^ to pass a variable by reference. But don't do that, the point of a value type is that it is cheaper to copy the value than having to dereference a pointer to obtain the value. Make sure your value type is not too big, it should not have more than 4 fields. If it is bigger then you should use a reference type.
这篇关于从System :: String ^从结构句柄成员向std :: string封送的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!