Question

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

Was it helpful?

Solution

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.

OTHER TIPS

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++.

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 ().

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top