问题
I am trying to marshal to a std::string from a System::String^.
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);
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);
The compiler will throw the following error
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.
回答1:
auto structHandle = gcnew SomeStruct();
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.
The signature of the marshal_as<> overload you are trying to use is:
marshal_as<std::string,System::String^>(System::String ^const &)
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
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..
}
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.
来源:https://stackoverflow.com/questions/30529869/marshal-to-stdstring-from-systemstring-member-of-struct-handle