views:

67

answers:

4

Hi!

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

+2  A: 

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.

JaredPar
+1  A: 

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

Hans Passant
A: 

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.

Kate Gregory
A: 

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

FuleSnabel