Marshal to std::string from System::String^ member of struct handle

爱⌒轻易说出口 提交于 2019-12-12 13:27:06

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!