C++托管到非托管转换

问题描述:

我已经完成了一些托管包装处理包装unmanged代码用于托管,但没有那么多,以另一种方式。C++托管到非托管转换

我正在运行一个实验,使用托管代码来查找目录并将它们返回到std向量中。长话短说,我正在搞下面的例子,并注意到一个问题。

#include "Helper.h" 

#include <msclr/marshal.h> 
#include <msclr/marshal_cppstd.h> 

using namespace msclr::interop; 

using namespace System; 

namespace CLIWrapper { 

    std::vector<std::string> Helper::GetDirs(const char* root) 
    { 
     std::vector<std::string> rval; 

     String^ path = gcnew System::String(root); 
     array<String^,1>^ dirs = System::IO::Directory::GetDirectories(path); 

     for (int i=0; i < dirs->Length; i++) 
     { 
     //this fails 
     std::string nativeString1(marshal_as<std::string>(dirs[i])); 

     //this fails as well 
     std::string nativeString2(marshal_as<std::string>((String^const)dirs[i])); 

     // this works 
     String ^mStr = dirs[i]; 
     std::string nativeString(marshal_as<std::string>(mStr)); 


     rval.push_back(nativeString); 
     } 

     return rval; 
    } 

} 

失败的 'nativeString1' 和 'nativeString2' 是: 错误C2665: 'msclr ::互操作:: marshal_as':无3个重载可以转换所有的参数类型

'nativeString2'如果您查看错误的详细信息,则会使用const,因为它在marshal_as的某个签名中列出。

问题是为什么'nativeString1'转换失败,但'nativeString'的工作?我的眼睛只是拒绝注意?

在响应线程出现之前:是的,我意识到这不是“最佳”解决方案,它不是平台独立的,等等。我试图专注于这个特定的错误。

+0

托管代码中的Const-correctness非常麻烦,CLR不管它。你的解决方法是一个体面的实用解决方案,额外的变量得到优化,所以没有什么可担心的。 – 2014-09-03 18:26:12

+0

这很有趣!我不知道这是因为dirs [i]不是一个常量引用,而mStr是。这里是germane函数的定义:'template inline std :: string marshal_as(System :: String^const&_from_obj)'。你可以尝试创建一个不断的参考,看看你是否有任何牵引力。 – 2014-09-03 18:47:14

+0

是的,我最初是用“this works”一节中的const定义做的,但发现它不是必需的。字符串^ const mStr = dirs [i]; – user3228938 2014-09-03 20:26:55

编译器不认为dirs[i]是一个字符串的常量引用(这对我来说也是令人惊讶的)。但是,你可以得到一个临时的常数参考值,而不使用% operator,这将增加该字符串的dirs[i]引用计数创建一个新的字符串句柄:

// this works as well 
auto nativeString1(marshal_as<std::string>(%*dirs[i])); 
+0

啊! '%'当然是参考指标!我已经使用了几次,但显然不足以记住它。我认为const的部分在这里是免费的,所以我认为他们签名的直接翻译变成:“对常量字符串的引用”。 – user3228938 2014-09-08 15:54:15

+0

虽然我想知道为什么签名没有被定义为“System :: String&const%”,因为参考定义的'&'在非托管端。 – user3228938 2014-09-08 16:03:06

+0

这是一个巧妙的把戏。我希望他们能解决这个问题。我发现这个问题可以追溯到VS2008。 – Niall 2014-11-12 08:13:26

这是由贾斯汀提到的签名引起的在一个评论,以智慧

template <> inline std::string marshal_as(System::String^ const & _from_obj) 

这是一个非常糟糕的事情要做。这是一个非跟踪指向Ssytem::String的跟踪指针。因为它是一个const引用,所以它可以绑定到一个临时对象,但因为它是一个非跟踪引用,所以它不能绑定到垃圾收集堆内的内存位置,因为gc堆上的对象可以四处移动。

应该已经能够通过身份投,它根据C++标准来解决这个问题,创建临时相同类型的。暂时不在gc堆上,因此所有 都可以。

不幸的是,有一些compiler bugs related to identity casts,因此,你实际上并没有得到一个临时的。

贾斯汀转换为跟踪引用并返回到跟踪指针是另一种创建临时指针的方法。不幸的是,他的回答中包含了一些关于参考数量的巨大数字。 .NET对象不被引用计数。

最重要的是,没有理由首先通过const引用传递该参数。跟踪指针很小,很容易复制。这是marshal_as作者的一个样式错误,因为它是一个错误。您可以将头文件更改为

template <> inline std::string marshal_as(System::String^ const _from_obj) 

没有破坏实施。

另一个解决将是使用跟踪的参考,像

template <> inline std::string marshal_as(System::String^ const % _from_obj) 

但同样,有没有点,因为传递的价值是如此便宜。

+0

VS2013.3遇到了这个问题,这个解释发生了什么最有意义(错误和所有)。 – Niall 2014-11-12 08:30:45

+0

你在.NET中引用计数的好处很多(对不起,它听起来像是巨无霸!)。 “%'状态”的文档“跟踪引用(%)的行为与普通C++引用(&)相似,只是当对象分配给跟踪引用时,对象的引用计数会增加。”我在互操作内部不清楚,但是认为参考计数是针对非管理方的。如果你可以澄清为什么%增加了真棒的裁判计数。 – 2014-11-13 02:35:03

+0

@JustinR .:可能你正在查看C++/CX而不是C++/CLI的文档吗? C++/CX访问了引用计数的WinRT对象,而不是使用基于可达性的世代垃圾收集的.NET对象,而不是参考计数。 – 2014-11-13 02:39:20