如何从C#中使用std :: vector<> :: iterator作为参数调用非托管C ++函数?
在非托管C ++中,我有一个函数正在尝试从C#调用.此C ++函数如下:
In unmanaged C++ I have a function which I'm trying to call from C#. This C++ function is as follows:
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 ++调用时,该函数的用法如下:
When called from C++, the function is used as follows:
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#代码,但是我不知道应该传递什么类型:
I've started writing the C# code, but I've no idea what types I should be passing:
[DllImport("unmanagedCode.dll", EntryPoint = "convexHull", CallingConvention = CallingConvention.StdCall)]
public static extern ???<Point> convex_hull_2(???<Point> start, ???<Point> last, ???<Point> result);
C#中的Point结构只是:
The Point structure in C# is just:
struct Point{
double x;
double y;
}
是否要传递数组或Point of List?
Is it a case of passing an array or List of Point?
我有C ++的源代码,可以更改函数参数;是否会有其他类型的参数,这些参数会更容易从C#调用?
I have the source to the C++ and can make changes to the function parameters; would there be a different type of parameters which would be easier to call from C#?
通过P/Invoke传递C ++类型是行不通的.您不知道它们的布局,也不能保证它们不会改变. P/Invoke实际上仅是为了与C互操作.
Passing C++ types through P/Invoke is not going to work. You don't know their layout and nothing guarantees they won't change. P/Invoke is really only meant for inter-operating with C.
一种选择是使用C ++/CLI而不是C ++.这不会是可移植的(仅VC ++/Windows支持),但这可能是最简单的解决方案,具体取决于您的C ++代码的大小.
One option is to use C++/CLI instead of C++. This won't be portable (only supported with VC++/Windows), but it might be the easiest solution depending on how large your C++ code is already.
如果您想保持可移植性并使用C#的直接P/Invoke,最好的选择是稍微重构C ++ convexHull
并提供一个可从C调用的新函数(并因此调用P/Invoke).
If you want to remain portable and use straight P/Invoke from C#, your best bet is to slightly refactor the C++ convexHull
and provide a new function callable from C (and thus 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);
}
}
代码未经测试!