c ++管理到非托管转换

时间:2014-09-03 17:35:21

标签: c++ marshalling unmanaged managed

我已经完成了许多托管包装器,这些包装器处理包装在托管中使用的非托管代码,但不是那么多其他方式。

我正在运行的实验涉及使用托管代码查找目录并将其返回到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:&#39; msclr :: interop :: marshal_as&#39; :3个重载中没有一个可以转换所有参数类型

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

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

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

2 个答案:

答案 0 :(得分:4)

这是由Justin在评论中提到的签名引起的,

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

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

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

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

Justin转换为跟踪引用并返回跟踪指针是创建临时引用的另一种方式。不幸的是,他的答案中包含了一些关于引用计数的巨大误解。 .NET对象不能被引用计数。

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

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

不破坏实施。

另一种解决方法是使用跟踪参考,例如

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

但是,再次,没有必要,因为按价值传递是如此便宜。

答案 1 :(得分:3)

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

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