编组来自 class 成员的托管字符串
Marshalling managed string from class member
使用 marshal_cppstd.h
和 msclr::interop::marshal_as<>
我可以像这样将托管字符串编组为 std::string
:
String^ managed = "test";
std::string unmanaged = marshal_as<std::string>(managed);
现在,当 managed
是 class 的成员时,我正在编写代码,我在简单地执行此操作时遇到错误:
std::string unmanaged = marshal_as<std::string>(this->managed);
错误说:
no instance of overloaded function "marshal_as" matches the argument list
或作为编译器错误 C2665:
'msclr::interop::marshal_as': none of the 3 overloads could convert all the argument types
当我更改代码以使用辅助变量时,它起作用了:
String^ localManaged = this->managed;
std::string unmanabed = marshal_as<std::string>(localManaged);
这里一定有一些隐式转换,不是吗?为什么会发生这种情况以及如何使简单的单行工作?
是的,这是一条非常糟糕的错误消息,无法帮助您发现真正的问题。模板错误消息通常很难理解。它可以使用一些重现代码:
#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);
}
};
您必须查看输出 window 才能看到编译器努力寻找与参数类型匹配的 marshal_as<> 模板函数版本。您会看到它考虑了两个模板专业化,但不是您想要的那个。即:
template <class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type&);
_From_Type&
参数是麻烦制造者,请注意它是一个 unmanaged 引用,&
。与跟踪参考相反,%
。或者只是简单的 ^
就像 System::String.
这样的引用类型
很难看出,this->managed
与 copy
之间存在巨大差异。在像 Example^ 这样的对象中,this
指针不稳定。当此代码为 运行 时,它的值可能会更改,发生在触发垃圾收集时。大多数程序中的可能性不大,但不是零。当程序中的另一个线程从 GC 堆分配并触发收集时发生。那种事~一年一次。
如果在 marshal_as<>() 执行其工作时进行收集,那将是非常灾难性的。非托管引用在 GC 压缩堆后变得无效并指向垃圾。 C++/CLI 编译器不允许这种情况发生,因此它不会将 this->managed&
视为 _From_Type&
的有效替代。甚至从来不看它。模板特化也比不过,C2665是必然的结果
与copy
参数的最大区别是它的地址总是稳定的。以未优化的代码存储在堆栈帧中,通常在优化器完成后存储在 CPU 寄存器中。所以 copy&
是 _From_Type&
的有效替代,编译器可以毫无问题地生成模板代码。
因此,您找到的解决方法是完全有效的,也是实现此目的的最佳方法。如果编译器只是为我们做这件事就好了,但事实并非如此。别名问题也不好,有时您必须将值复制回来。关于编写 C++/CLI 代码以及混合托管代码和本机代码的结果,您必须了解的一些内容,总有一天您一定会再次遇到它。
使用 marshal_cppstd.h
和 msclr::interop::marshal_as<>
我可以像这样将托管字符串编组为 std::string
:
String^ managed = "test";
std::string unmanaged = marshal_as<std::string>(managed);
现在,当 managed
是 class 的成员时,我正在编写代码,我在简单地执行此操作时遇到错误:
std::string unmanaged = marshal_as<std::string>(this->managed);
错误说:
no instance of overloaded function "marshal_as" matches the argument list
或作为编译器错误 C2665:
'msclr::interop::marshal_as': none of the 3 overloads could convert all the argument types
当我更改代码以使用辅助变量时,它起作用了:
String^ localManaged = this->managed;
std::string unmanabed = marshal_as<std::string>(localManaged);
这里一定有一些隐式转换,不是吗?为什么会发生这种情况以及如何使简单的单行工作?
是的,这是一条非常糟糕的错误消息,无法帮助您发现真正的问题。模板错误消息通常很难理解。它可以使用一些重现代码:
#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);
}
};
您必须查看输出 window 才能看到编译器努力寻找与参数类型匹配的 marshal_as<> 模板函数版本。您会看到它考虑了两个模板专业化,但不是您想要的那个。即:
template <class _To_Type, class _From_Type>
inline _To_Type marshal_as(const _From_Type&);
_From_Type&
参数是麻烦制造者,请注意它是一个 unmanaged 引用,&
。与跟踪参考相反,%
。或者只是简单的 ^
就像 System::String.
很难看出,this->managed
与 copy
之间存在巨大差异。在像 Example^ 这样的对象中,this
指针不稳定。当此代码为 运行 时,它的值可能会更改,发生在触发垃圾收集时。大多数程序中的可能性不大,但不是零。当程序中的另一个线程从 GC 堆分配并触发收集时发生。那种事~一年一次。
如果在 marshal_as<>() 执行其工作时进行收集,那将是非常灾难性的。非托管引用在 GC 压缩堆后变得无效并指向垃圾。 C++/CLI 编译器不允许这种情况发生,因此它不会将 this->managed&
视为 _From_Type&
的有效替代。甚至从来不看它。模板特化也比不过,C2665是必然的结果
与copy
参数的最大区别是它的地址总是稳定的。以未优化的代码存储在堆栈帧中,通常在优化器完成后存储在 CPU 寄存器中。所以 copy&
是 _From_Type&
的有效替代,编译器可以毫无问题地生成模板代码。
因此,您找到的解决方法是完全有效的,也是实现此目的的最佳方法。如果编译器只是为我们做这件事就好了,但事实并非如此。别名问题也不好,有时您必须将值复制回来。关于编写 C++/CLI 代码以及混合托管代码和本机代码的结果,您必须了解的一些内容,总有一天您一定会再次遇到它。