【问题标题】:Marshalling managed string from class member从类成员编组托管字符串
【发布时间】:2016-12-23 09:48:09
【问题描述】:

使用 marshal_cppstd.hmsclr::interop::marshal_as<> 我可以将托管字符串编组为 std::string,如下所示:

String^ managed = "test";
std::string unmanaged = marshal_as<std::string>(managed);

现在,当managed 是我正在编写代码的类的成员时,我在执行以下操作时会出错:

std::string unmanaged = marshal_as<std::string>(this->managed);

错误提示:

没有重载函数“marshal_as”的实例与参数列表匹配

或者编译器错误C2665:

'msclr::interop::marshal_as': 3 个重载都不能转换所有参数类型

当我更改代码以使用辅助变量时,它可以工作:

String^ localManaged = this->managed;
std::string unmanabed = marshal_as<std::string>(localManaged);

这里肯定有一些隐式转换,不是吗?为什么会发生这种情况以及如何使简单的单线工作?

【问题讨论】:

  • @LucasTrzesniewski 不确定这是否是编译器错误,此处记录了类似的观察结果:stackoverflow.com/questions/30529869/… 虽然我使用的是一个类,但这可能是相关的。
  • 哦对了,我没有发现签名采用System::String ^const &amp; 参数,而&amp; 阻止您使用对可移动对象的引用。绝对不是编译器错误,并且在给定方法签名的情况下完全有意义。所以,是的,给自己写一个带有System::String^ 参数的辅助方法。

标签: c++-cli


【解决方案1】:

是的,这是一条非常糟糕的错误消息,并不能帮助您发现真正的问题。模板错误消息通常很难理解。它可以使用一些重现代码:

#include "stdafx.h"
#include <string>
#include <msclr\marshal_cppstd.h>

using namespace System;
using namespace msclr::interop;

ref class Example {
    String^ managed;
public:
    void test() {
        auto bad  = marshal_as<std::string>(this->managed);   // C2665
        auto copy = this->managed;
        auto good = marshal_as<std::string>(copy);
    }
};

您必须在“输出”窗口中查看编译器正在努力寻找与参数类型匹配的 marshal_as 模板函数的版本。您会看到它考虑了两种模板专业化,但不是您想要的。即:

template <class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type&);

_From_Type&amp; 参数是麻烦制造者,注意它是一个非托管 引用,&amp;。与跟踪参考相反,%。或者只是简单的 ^ 作为像 System::String 这样的引用类型。

很难看出引用this-&gt;managedcopy 之间存在巨大差异。在像 Example^ 这样的对象中,this 指针不稳定。它的值可以在此代码运行时更改,发生在触发垃圾收集时。大多数程序中的可能性不大,但不是零。当程序中的另一个线程从 GC 堆分配并触发收集时发生。每年发生一次的那种事情。

如果收集发生在 marshal_as() 正在做它的工作时,那将是非常灾难性的。非托管引用在 GC 压缩堆后变得无效并指向垃圾。 C++/CLI 编译器不能让这种情况发生,因此它不会将this-&gt;managed&amp; 视为_From_Type&amp; 的有效替代品。甚至从不看它。模板专精也比不上,C2665是必然结果。

copy 参数最大的区别在于它的地址始终是稳定的。在未优化代码中存储在堆栈帧中,通常在优化器完成后存储在 CPU 寄存器中。所以copy&amp; _From_Type&amp; 的有效替代品,编译器可以毫无问题地生成模板代码。

因此,您找到的解决方法是完全有效的,也是执行此操作的最佳方法。如果编译器只是为我们这样做会很好,但事实并非如此。别名问题也不好,有时您必须将值复制回来。只是一些关于编写 C++/CLI 代码的知识以及混合托管代码和本机代码的结果,你肯定有一天会再次遇到它。

【讨论】:

  • 汉斯,谢谢!我一直在努力理解发生了什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-19
  • 2012-11-19
  • 1970-01-01
  • 1970-01-01
  • 2012-07-20
相关资源
最近更新 更多