问题
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
回答1:
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.
回答2:
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++.
回答3:
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 ().
回答4:
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.
来源:https://stackoverflow.com/questions/3772120/howto-call-an-unmanaged-c-function-with-a-stdvector-as-parameter-from-c