如何使用 std::vector 作为 C# 的参数调用非托管 C++ 函数?

Posted

技术标签:

【中文标题】如何使用 std::vector 作为 C# 的参数调用非托管 C++ 函数?【英文标题】:Howto call an unmanaged C++ function with a std::vector as parameter from C#? 【发布时间】:2010-09-22 17:46:39 【问题描述】:

出于性能原因,我有一个 C# 前端和一个 C++ 后端。 现在我想调用一个 C++ 函数,例如:

void findNeighbors(Point p, std::vector<Point> &neighbors, double maxDist);

我想要的是一个 C# 包装函数,例如:

List<Point> FindNeigbors(Point p, double maxDist);

我可以将像 Point[] 这样的平面数组传递给非托管 C++ dll,但问题是,我不知道要分配多少内存,因为我不知道函数将返回的元素数量...

有没有一种优雅的方法来处理这个问题而不会出现内存泄漏问题?

感谢您的帮助!

本杰明

【问题讨论】:

【参考方案1】:

这里最好的解决方案是用 C 编写一个包装函数,该函数仅限于非 C++ 类。非平凡的 C++ 类本质上是不可通过 PInvoke 层 [1] 进行编组的。而是让包装函数使用更传统的 C 签名,这很容易针对 PInvoke 进行攻击

void findNeigborsWrapper(
  Point p,
  double maxDist, 
  Point** ppNeighbors,
  size_t* pNeighborsLength)

[1] 是的,在某些情况下您可以侥幸逃脱,但这是例外而非规则。

【讨论】:

【参考方案2】:

阻抗不匹配严重。你必须用 C++/CLI 语言编写一个包装器,这样你才能构造一个向量。另一个问题是 Point,您的 C++ 声明与它的托管版本不兼容。您的代码应该与此类似,将其从 CLR 节点添加到类库项目中。

#include <vector>

using namespace System;
using namespace System::Collections::Generic;

struct Point  int x; int y; ;
void findNeighbors(Point p, std::vector<Point> &neighbors, double maxDist);

namespace Mumble 

    public ref class Wrapper
    
    public:
        List<System::Drawing::Point>^ FindNeigbors(System::Drawing::Point p, double maxDist) 
            std::vector<Point> neighbors;
            Point point; point.x = p.X; point.y = p.Y;
            findNeighbors(point, neighbors, maxDist);
            List<System::Drawing::Point>^ retval = gcnew List<System::Drawing::Point>();
            for (std::vector<Point>::iterator it = neighbors.begin(); it != neighbors.end(); ++it) 
                retval->Add(System::Drawing::Point(it->x, it->y));
            
            return retval;
        
    ;

请注意复制集合的成本,这会很快消除您在使用原生 C++ 编写算法时可能获得的性能优势。

【讨论】:

【参考方案3】:

为了减少复制的开销(如果确实会导致性能问题),可以围绕 std::vector 编写 C++/CLI 引用类。这样,c++ 算法就可以在 c++ 类型上工作,而 C# 代码可以访问相同的数据而无需过度复制。

C++/CLI 类可以实现 operator[] 和 Count 以避免依赖 IEnumerable::GetEnumerator ()。

【讨论】:

【参考方案4】:

或者用 C++/CLI 编写你的包装器。让它采用符合 CLS 的类型,例如 IEnumerable,然后(叹气)将每个元素复制到您的向量中,然后调用 PInvoke。

【讨论】:

以上是关于如何使用 std::vector 作为 C# 的参数调用非托管 C++ 函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何在编译时获得多维 std::vector 的深度?

如何使用 Python 列表在 C++ 中使用 SWIG 分配 std::vector?

Swig:将 std::vector<unsigned char> 传递给从 c++ 生成的 c# 函数

将 std::vector 作为 numpy 数组返回给 python

使用初始化列表作为值时插入 std::list<std::vector<int>> 失败?

使用 std::vector 作为标头中的输入参数定义函数的原型