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

发布于 2024-09-24 16:20:03 字数 425 浏览 5 评论 0原文

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

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

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

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

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

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

感谢您的帮助!

本杰明

I have a C# front end and a C++ backend for performance reasons.
Now I would like to call a C++ function like for example:

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

What I'd like to have is a C# wrapper function like:

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

I could pass a flat array like Point[] to the unmanaged C++ dll, but the problem is, that I don't know how much memory to allocate, because I don't know the number of elements the function will return...

Is there an elegant way to handle this without having troubles with memory leaks?

Thanks for your help!

Benjamin

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

染火枫林 2024-10-01 16:20:03

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

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

[1] 进行 PInvoke。是的,在某些情况下您可以摆脱它,但这是例外,而不是规则。

The best solution here is to write a wrapper function in C which is limited to non-C++ classes. Non-trivial C++ classes are essentially unmarshable via the PInvoke layer [1]. Instead have the wrapper function use a more traditional C signature which is easy to PInvoke against

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

[1] Yes there are certain cases where you can get away with it but that's the exception and not the rule.

箜明 2024-10-01 16:20:03

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

The impedance mismatch is severe. You have to write a wrapper in the C++/CLI language so that you can construct a vector. An additional problem is Point, your C++ declaration for it is not compatible with the managed version of it. Your code ought to resemble this, add it to a class library project from the CLR node.

#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;
        }
    };
}

Do note the cost of copying the collection, this can quickly erase the perf advantage you might get out of writing the algorithm in native C++.

似梦非梦 2024-10-01 16:20:03

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

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

In order to reduce overhead from copying (if that does cause performance problems) it would be possible to write a C++/CLI ref class around std::vector<>. That way the c++ algorithm can work on c++ types and C# code can access the same data without excessive copying.

The C++/CLI class could implement operator[] and Count in order to avoid relying on IEnumerable::GetEnumerator ().

梦境 2024-10-01 16:20:03

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

Or write your wrapper in C++/CLI. Have it take a CLS-compliant type such as IEnumerable and then (sigh) copy each element into your vector, then call the PInvoke.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文