2

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

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

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

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

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

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

谢谢你的帮助!

本杰明

4

4 回答 4

3

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

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

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

于 2010-09-22T17:55:30.593 回答
1

阻抗失配严重。你必须用 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++ 中编写算法时可能获得的性能优势。

于 2010-09-22T18:03:06.363 回答
1

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

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

于 2010-09-22T18:23:43.870 回答
0

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

于 2010-09-22T18:06:16.017 回答