Как вызвать неуправляемую функцию C ++ с std :: vector <> :: iterator в качестве параметра из C #? - PullRequest
0 голосов
/ 17 июля 2011

В неуправляемом C ++ у меня есть функция, которую я пытаюсь вызвать из C #. Эта функция C ++ выглядит следующим образом:

typedef std::vector<Point> Points;
typedef std::back_insert_iterator<Points> OutputIterator;

namespace MYNAMESPACE{
    DLLEXPORT OutputIterator convexHull(Points::iterator first, Points::iterator last,  OutputIterator result);
}

При вызове из C ++ функция используется следующим образом:

  Points points, result;

  points.push_back(Point(0,0));
  points.push_back(Point(10,0));
  points.push_back(Point(10,10));
  points.push_back(Point(6,5));
  points.push_back(Point(4,1));

  OutputIterator resultIterator = std::back_inserter(result);

  MYNAMESPACE::convexHull( points.begin(), points.end(), resultIterator);
  std::cout << result.size() << " points on the convex hull" << std::endl;

Я начал писать код на C #, но не знаю, какие типы я должен передавать:

[DllImport("unmanagedCode.dll", EntryPoint = "convexHull", CallingConvention = CallingConvention.StdCall)]
        public static extern ???<Point> convex_hull_2(???<Point> start, ???<Point> last, ???<Point> result);

Структура Point в C # просто:

struct Point{
    double x;
    double y;
}

Это случай передачи массива или списка точек?

У меня есть источник для C ++, и я могу вносить изменения в параметры функции; Будет ли другой тип параметров, которые будет проще вызвать из C #?

Ответы [ 2 ]

6 голосов
/ 17 июля 2011

Используйте класс-оболочку C ++ / CLI (поскольку у вас есть исходный код C ++, вы можете скомпилировать его вместе с существующим кодом в одну DLL).

P / invoke не предназначен для взаимодействия с C ++классы, и любая попытка сделать это чрезвычайно хрупкая.Шаблоны C ++ могут даже хуже.Не пытайся.Даже dllexport передача этой функции в другой код C ++ - ужасная идея, классы STL, такие как vector и vector::iterator, не должны передаваться через границы DLL.

Используйте /clr и позвольте VisualКомпилятор C ++ позаботится о деталях.

1 голос
/ 17 июля 2011

Передача типов C ++ через P / Invoke не будет работать. Вы не знаете их расположение, и ничто не гарантирует, что они не изменятся. P / Invoke действительно предназначен только для взаимодействия с C.

Один из вариантов - использовать C ++ / CLI вместо C ++. Это не будет переносимым (поддерживается только с VC ++ / Windows), но это может быть самое простое решение в зависимости от того, насколько большой ваш код C ++ уже.

Если вы хотите оставаться переносимым и использовать прямой P / Invoke из C #, лучше всего немного реорганизовать C ++ convexHull и предоставить новую функцию, вызываемую из C (и, следовательно, P / Invoke).

// C-safe struct.
struct Results
{
    Point *points;
    std::size_t num_points;
};

// Store the real results in a vector, but derive from the C-safe struct.
struct ResultsImpl : Results
{
    Points storage;
};

// convexHull has been refactored to take pointers
// instead of vector iterators.
OutputIterator convexHull(Point const *first, Point const *last,
    OutputIterator result);

// The exported function is callable from C.
// It returns a C-safe Results, not ResultsImpl.
extern "C" DLLEXPORT Results* convexHullC(Point const *points,
                                          std::size_t num_points)
{
    try
    {
        std::unique_ptr<ResultsImpl> r(new ResultImpl);

        // fill in r->storage.
        convexHull(points, points + num_points,
            std::back_inserter(r->storage));

        // fill in C-safe members.
        r->points = &r->storage[0];
        r->numPoints = &r->storage.size();

        return r.release();
    }
    catch(...)
    {
        // trap all exceptions!
        return 0;
    }
}

// needs to be called from C# to clean up the results.
extern "C" DLLEXPORT void freeConvexHullC(Results *r)
{
    try
    {
        delete (ResultsImpl*)r;
    }
    catch(...)
    {
        // trap all exceptions!
    }
}

А потом из C #:

[StructLayout(LayoutKind.Sequential)]
struct Point
{
    double x;
    double y;
}

[StructLayout(LayoutKind.Sequential)]
struct Results
{
    IntPtr points;
    IntPtr num_points;
}

[DllImport("unmanagedCode")]
IntPtr convexHullC(Point[] points, IntPtr pointCount);

[DllImport("unmanagedCode")]
void freeConvexHullC(IntPtr results);

Point[] ConvexHull(Point[] points)
{
    IntPtr pr = convexHull(points, new IntPtr(points.Length));

    if(pr == IntPtr.Zero)
    {
        throw new Exception("native error!");
    }

    try
    {
        Results r = Marshal.PtrToStructure(pr, typeof(Results));

        points = new Point[checked((int)(long)r.num_points)];

        for(int i = 0; i < points.Length; ++i)
        {
            points[i] = Marshal.PtrToStructure(
                r.points + Marshal.Sizeof(typeof(Point)) * i,
                typeof(Point));
        }

        return points;
    }
    finally
    {
        freeConvexHull(pr);
    }
}

Код не проверен!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...