|
Ive spent quite a bit of time researching how best to reuse the existing
great libraries written in c++ - in c#. I was looking at some really good cross
platform gui stuff for use in cross platform projects, for example. WxWindows
(www.wxwindows.org) and swt (part of
Eclipse www.eclipse.org).
C# is very good at accessing c libraries through pinvoke (using extern), but reusing c++ libraries are notoriously difficult (see other posts for info on different ways mangling is done and all the issues) One option was using MS Visual C++ managed extensions to expose the functions to the managed world. Directly porting the existing c++ code like this looks like a lot of work! Adding a managed piece that then passes through the calls to the unmananged library would work but there were issues. C++ supports having mixed dll's or exe's - ie you can embed managed and unmanaged code together. This works fine in a windows environment but without understanding the exact mechanics of how ms .net allows this, I suspect that it�s a problem for MONO/DotGNU. The other option would be my c# program calling a managed c++ dll (which will/should run under DotGNU/Mono) calling an unmanaged dll. Yuck! Other posts I read pointed me to swig. www.swig.org this is a cross platform app that builds addons to allow c++ libraries to be used from java apps, Perl, Python etc. etc. But no .net This stuff is pretty clever, it�s a partial compiler, including precompiler and parser that then builds the necessary c code to be added and compiled into your c++ dll and the java jni code (in the case of java). It also builds a wrapper around these jni calls so that you can use your object in java in exactly the same way as the c++ object. V. cool stuff. So I grabbed the cvs and hacked the java stuff to see if I could get some decent results for .net. Now there is plenty of work left and ill need weeks before I find enough time to finish but basically - as an example - this is what it does at the moment: Very simple c++ object example
============================== class Shape { public: Shape() { nshapes++; } virtual ~Shape() { nshapes--; }; double x, y; void move(double dx, double dy); virtual double area(void) = 0; virtual double perimeter(void) = 0; static int nshapes; }; class Circle : public Shape {
private: double radius; public: Circle(double r) : radius(r) { }; virtual double area(void); virtual double perimeter(void); }; class Square : public Shape {
private: double width; public: Square(double w) : width(w) { }; virtual double area(void); virtual double perimeter(void); }; #define M_PI 3.14159265358979323846 /* Move the shape to a new location */
void Shape::move(double dx, double dy) { x += dx; y += dy; } int Shape::nshapes = 0;
double Circle::area(void) {
return M_PI*radius*radius; } double Circle::perimeter(void) {
return 2*M_PI*radius; } double Square::area(void) {
return width*width; } double Square::perimeter(void) {
return 4*width; } RUN .net SWIG, produces:
============================ #define DllExport __declspec( dllexport )
extern "C" { DllExport void delete_Shape(int jarg1) { Shape *arg1 = (Shape *) 0 ; arg1 = *(Shape **)&jarg1; delete arg1; } DllExport void set_Shape_x(int jarg1, double jarg2) {
Shape *arg1 = (Shape *) 0 ; double arg2 ; arg1 = *(Shape **)&jarg1; arg2 = (double)jarg2; if (arg1) (arg1)->x = arg2; } DllExport double get_Shape_x(int jarg1) {
double jresult = 0 ; Shape *arg1 = (Shape *) 0 ; double result; arg1 = *(Shape **)&jarg1; result = (double) ((arg1)->x); jresult = (double)result; return jresult; } DllExport void Shape_move(int jarg1, double jarg2, double jarg3)
{
Shape *arg1 = (Shape *) 0 ; double arg2 ; double arg3 ; arg1 = *(Shape **)&jarg1; arg2 = (double)jarg2; arg3 = (double)jarg3; (arg1)->move(arg2,arg3); } DllExport int get_Shape_nshapes() {
int jresult = 0 ; int result; result = (int)Shape::nshapes; jresult = (int)result; return jresult; } �BLA BLA�
DllExport long SquareToShape(long jarg1) {
long baseptr = 0; *(Shape **)&baseptr = *(Square **)&jarg1; return baseptr; } } AND
The Matching c# code: ===================== using System.Runtime.InteropServices; class examplePINVOKE { [DllImport("example")] public static extern void delete_Shape(int jarg1); [DllImport("example")] public static extern void set_Shape_x(int jarg1, double jarg2); [DllImport("example")] public static extern double get_Shape_x(int jarg1); �BLA BLA� [DllImport("example")]
public static extern int CircleToShape(int jarg1); [DllImport("example")] public static extern int SquareToShape(int jarg1); } AND
using System;
public class Shape : IDisposable { private int swigCPtr; protected bool swigCMemOwn; protected Shape(int cPtr, bool cMemoryOwn) {
swigCMemOwn = cMemoryOwn; swigCPtr = cPtr; } protected Shape() : this(0, false) {
} public virtual void Dispose() {
delete(); } protected void delete() {
if(swigCPtr != 0 && swigCMemOwn) { examplePINVOKE.delete_Shape(swigCPtr); swigCMemOwn = false; } swigCPtr = 0; } protected static long getCPtr(Shape obj) {
return (obj == null) ? 0 : obj.swigCPtr; } public void setX(double x) {
examplePINVOKE.set_Shape_x(swigCPtr, x); } public double getX() {
return examplePINVOKE.get_Shape_x(swigCPtr); } �BLA BLA�
} So if you got down this far ;) now what you have is:
using a single command you can reuse all the great c++ libraries in c#. Sounds good?! I think this route is the most robust and sensible way of reuse. Do you
agree? Will this be useful?
I need to finish the work and then convince Dave to add this into his Swig project. Swig offers pretty complete support of c++ including templates, virtual methods - take a look at the website. Quite a lot of work remains to make sure we are writing decent c# code, to finish off and thoroughly test. I havnt had the time to even try it on a c++ library bigger than the example above - but I will! Neil 416 436 6345
|
