2011-07-17 26 views
0

在非託管C++中我有一個函數,我試圖從C#調用。這個C++函數如下:如何使用std :: vector <> :: iterator作爲參數從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#代碼,但我不知道是什麼類型我要傳遞:在C#

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

Point結構就是:

struct Point{ 
    double x; 
    double y; 
} 

是傳遞數組還是點列表的情況?

我有源代碼到C++並可以對函數參數進行更改;會不會有更容易從C#調用的不同類型的參數?

回答

1

通過P/Invoke傳遞C++類型是行不通的。你不知道他們的佈局,也不保證他們不會改變。 P/Invoke實際上僅用於與C進行交互操作。

一種選擇是使用C++/CLI而不是C++。這不會是可移植的(只有VC++/Windows支持),但它可能是最簡單的解決方案,取決於你的C++代碼已經有多大。如果你想保持便攜性並使用C#中的直接P/Invoke,最好的辦法是略微重構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); 
    } 
} 

代碼沒有測試!

6

使用C++/CLI包裝類(因爲您有C++源代碼,您可以將它與現有代碼一起編譯到單個DLL中)。

P/invoke不是爲與C++類進行交互而設計的,任何嘗試這樣做都是非常脆弱的。 C++模板可能會更糟糕。不要嘗試。即使將該函數用於其他C++代碼也是一個可怕的想法,不應該通過DLL邊界傳遞諸如vectorvector::iterator之類的STL類。使用/clr並讓Visual C++編譯器負責細節。

相關問題