[Mono-list] Automatically reusing c++ libraries in c#

Neil Cawse neilcawse@hotmail.com
Thu, 5 Dec 2002 23:13:38 -0500


This is a multi-part message in MIME format.

------=_NextPart_000_02EB_01C29CB3.F14D4FD0
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D
class Shape {
public:
  Shape() {
    nshapes++;
  }
  virtual ~Shape() {
    nshapes--;
  };
  double  x, y;  =20
  void    move(double dx, double dy);
  virtual double area(void) =3D 0;
  virtual double perimeter(void) =3D 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 +=3D dx;
  y +=3D dy;
}

int Shape::nshapes =3D 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:
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D

#define DllExport   __declspec( dllexport )
extern "C" {
DllExport void delete_Shape(int jarg1) {
    Shape *arg1 =3D (Shape *) 0 ;
   =20
    arg1 =3D *(Shape **)&jarg1;=20
    delete arg1;
}

DllExport void set_Shape_x(int jarg1, double jarg2) {
    Shape *arg1 =3D (Shape *) 0 ;
    double arg2 ;
   =20
    arg1 =3D *(Shape **)&jarg1;=20
    arg2 =3D (double)jarg2;=20
    if (arg1) (arg1)->x =3D arg2;
   =20
}

DllExport double get_Shape_x(int jarg1) {
    double jresult =3D 0 ;
    Shape *arg1 =3D (Shape *) 0 ;
    double result;
   =20
    arg1 =3D *(Shape **)&jarg1;=20
    result =3D (double) ((arg1)->x);
   =20
    jresult =3D (double)result;=20
    return jresult;
}

DllExport void Shape_move(int jarg1, double jarg2, double jarg3) {
    Shape *arg1 =3D (Shape *) 0 ;
    double arg2 ;
    double arg3 ;
   =20
    arg1 =3D *(Shape **)&jarg1;=20
    arg2 =3D (double)jarg2;=20
    arg3 =3D (double)jarg3;=20
    (arg1)->move(arg2,arg3);
   =20
}

DllExport int get_Shape_nshapes() {
    int jresult =3D 0 ;
    int result;
   =20
    result =3D (int)Shape::nshapes;
   =20
    jresult =3D (int)result;=20
    return jresult;
}

.BLA BLA.

DllExport long SquareToShape(long jarg1) {
    long baseptr =3D 0;
    *(Shape **)&baseptr =3D *(Square **)&jarg1;
    return baseptr;
}
}

AND
The Matching c# code:
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
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);
=20
 .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 =3D cMemoryOwn;
    swigCPtr =3D cPtr;
  }

  protected Shape() : this(0, false) {
  }

  public virtual void Dispose() {
    delete();
  }

  protected void delete() {
    if(swigCPtr !=3D 0 && swigCMemOwn) {
      examplePINVOKE.delete_Shape(swigCPtr);
      swigCMemOwn =3D false;
    }
    swigCPtr =3D 0;
  }

  protected static long getCPtr(Shape obj) {
    return (obj =3D=3D 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
------=_NextPart_000_02EB_01C29CB3.F14D4FD0
Content-Type: text/html;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type =
content=3Dtext/html;charset=3Diso-8859-1>
<META content=3D"MSHTML 6.00.2800.1126" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY id=3DMailContainerBody=20
style=3D"PADDING-LEFT: 10px; FONT-WEIGHT: normal; FONT-SIZE: 10pt; =
COLOR: #000000; BORDER-TOP-STYLE: none; PADDING-TOP: 15px; FONT-STYLE: =
normal; FONT-FAMILY: Verdana; BORDER-RIGHT-STYLE: none; =
BORDER-LEFT-STYLE: none; TEXT-DECORATION: none; BORDER-BOTTOM-STYLE: =
none"=20
leftMargin=3D0 topMargin=3D0 acc_role=3D"text" CanvasTabStop=3D"true"=20
name=3D"Compose message area"><?xml:namespace prefix=3D"v" =
/><?xml:namespace prefix=3D"o" />
<DIV>Ive spent quite a bit of time researching how best to reuse the =
existing=20
great libraries written in c++ - in c#. I was looking at some really =
good cross=20
platform gui stuff for use in cross platform projects, for example. =
WxWindows=20
(<A href=3D"http://www.wxwindows.org">www.wxwindows.org</A>) and swt =
(part of=20
Eclipse <A href=3D"http://www.eclipse.org">www.eclipse.org</A>).<BR>C# =
is very=20
good at accessing c libraries through pinvoke (using extern), but =
reusing c++=20
libraries are notoriously difficult (see other posts for info on =
different ways=20
mangling is done and all the issues)<BR>One option was using MS Visual =
C++=20
managed extensions to expose the functions to the managed world. =
Directly=20
porting the existing c++ code like this looks like a lot of work! Adding =
a=20
managed piece that then passes through the calls to the unmananged =
library would=20
work but there were issues.<BR>C++ supports having mixed dll's or exe's =
- ie you=20
can embed managed and unmanaged code together. This works fine in a =
windows=20
environment but without understanding the exact mechanics of how ms .net =
allows=20
this, I suspect that it=92s a problem for MONO/DotGNU. The other option =
would be=20
my c# program calling a managed c++ dll (which will/should run under=20
DotGNU/Mono) calling an unmanaged dll. Yuck!<BR>Other posts I read =
pointed me to=20
swig. <A href=3D"http://www.swig.org">www.swig.org</A> this is a cross =
platform=20
app that builds addons to allow c++ libraries to be used from java apps, =
Perl,=20
Python etc. etc. But no .net<BR>This stuff is pretty clever, it=92s a =
partial=20
compiler, including precompiler and parser that then builds the =
necessary c code=20
to be added and compiled into your c++ dll and the java jni code (in the =
case of=20
java). It also builds a wrapper around these jni calls so that you can =
use your=20
object in java in exactly the same way as the c++ object.<BR>V. cool =
stuff. So I=20
grabbed the cvs and hacked the java stuff to see if I could get some =
decent=20
results for .net. Now there is plenty of work left and ill need weeks =
before I=20
find enough time to finish but basically - as an example - this is what =
it does=20
at the moment:</DIV>
<DIV>&nbsp;</DIV>
<DIV>Very simple c++ object =
example<BR>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D<BR>class=20
Shape {<BR>public:<BR>&nbsp; Shape() {<BR>&nbsp;&nbsp;&nbsp;=20
nshapes++;<BR>&nbsp; }<BR>&nbsp; virtual ~Shape() =
{<BR>&nbsp;&nbsp;&nbsp;=20
nshapes--;<BR>&nbsp; };<BR>&nbsp; double&nbsp; x, y;&nbsp;&nbsp; =
<BR>&nbsp;=20
void&nbsp;&nbsp;&nbsp; move(double dx, double dy);<BR>&nbsp; virtual =
double=20
area(void) =3D 0;<BR>&nbsp; virtual double perimeter(void) =3D =
0;<BR>&nbsp;=20
static&nbsp; int nshapes;<BR>&nbsp; };</DIV>
<DIV>&nbsp;</DIV>
<DIV>class Circle : public Shape {<BR>private:<BR>&nbsp; double=20
radius;<BR>public:<BR>&nbsp; Circle(double r) : radius(r) { };<BR>&nbsp; =
virtual=20
double area(void);<BR>&nbsp; virtual double perimeter(void);<BR>};</DIV>
<DIV>&nbsp;</DIV>
<DIV>class Square : public Shape {<BR>private:<BR>&nbsp; double=20
width;<BR>public:<BR>&nbsp; Square(double w) : width(w) { };<BR>&nbsp; =
virtual=20
double area(void);<BR>&nbsp; virtual double =
perimeter(void);<BR>};<BR>#define=20
M_PI 3.14159265358979323846</DIV>
<DIV>&nbsp;</DIV>
<DIV>/* Move the shape to a new location */<BR>void Shape::move(double =
dx,=20
double dy) {<BR>&nbsp; x +=3D dx;<BR>&nbsp; y +=3D dy;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>int Shape::nshapes =3D 0;</DIV>
<DIV>&nbsp;</DIV>
<DIV>double Circle::area(void) {<BR>&nbsp; return =
M_PI*radius*radius;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>double Circle::perimeter(void) {<BR>&nbsp; return =
2*M_PI*radius;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>double Square::area(void) {<BR>&nbsp; return =
width*width;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>double Square::perimeter(void) {<BR>&nbsp; return =
4*width;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>RUN .net SWIG, =
produces:<BR>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D</DIV>
<DIV>&nbsp;</DIV>
<DIV>#define DllExport&nbsp;&nbsp; __declspec( dllexport )<BR>extern "C" =

{<BR>DllExport void delete_Shape(int jarg1) {<BR>&nbsp;&nbsp;&nbsp; =
Shape *arg1=20
=3D (Shape *) 0 ;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; arg1 =3D =
*(Shape=20
**)&amp;jarg1; <BR>&nbsp;&nbsp;&nbsp; delete arg1;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>DllExport void set_Shape_x(int jarg1, double jarg2) =
{<BR>&nbsp;&nbsp;&nbsp;=20
Shape *arg1 =3D (Shape *) 0 ;<BR>&nbsp;&nbsp;&nbsp; double arg2=20
;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; arg1 =3D *(Shape =
**)&amp;jarg1;=20
<BR>&nbsp;&nbsp;&nbsp; arg2 =3D (double)jarg2; <BR>&nbsp;&nbsp;&nbsp; if =
(arg1)=20
(arg1)-&gt;x =3D arg2;<BR>&nbsp;&nbsp;&nbsp; <BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>DllExport double get_Shape_x(int jarg1) {<BR>&nbsp;&nbsp;&nbsp; =
double=20
jresult =3D 0 ;<BR>&nbsp;&nbsp;&nbsp; Shape *arg1 =3D (Shape *) 0=20
;<BR>&nbsp;&nbsp;&nbsp; double result;<BR>&nbsp;&nbsp;&nbsp;=20
<BR>&nbsp;&nbsp;&nbsp; arg1 =3D *(Shape **)&amp;jarg1; =
<BR>&nbsp;&nbsp;&nbsp;=20
result =3D (double) ((arg1)-&gt;x);<BR>&nbsp;&nbsp;&nbsp; =
<BR>&nbsp;&nbsp;&nbsp;=20
jresult =3D (double)result; <BR>&nbsp;&nbsp;&nbsp; return =
jresult;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>DllExport void Shape_move(int jarg1, double jarg2, double jarg3)=20
{<BR>&nbsp;&nbsp;&nbsp; Shape *arg1 =3D (Shape *) 0 =
;<BR>&nbsp;&nbsp;&nbsp; double=20
arg2 ;<BR>&nbsp;&nbsp;&nbsp; double arg3 ;<BR>&nbsp;&nbsp;&nbsp;=20
<BR>&nbsp;&nbsp;&nbsp; arg1 =3D *(Shape **)&amp;jarg1; =
<BR>&nbsp;&nbsp;&nbsp; arg2=20
=3D (double)jarg2; <BR>&nbsp;&nbsp;&nbsp; arg3 =3D (double)jarg3;=20
<BR>&nbsp;&nbsp;&nbsp; (arg1)-&gt;move(arg2,arg3);<BR>&nbsp;&nbsp;&nbsp; =

<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>DllExport int get_Shape_nshapes() {<BR>&nbsp;&nbsp;&nbsp; int =
jresult =3D 0=20
;<BR>&nbsp;&nbsp;&nbsp; int result;<BR>&nbsp;&nbsp;&nbsp; =
<BR>&nbsp;&nbsp;&nbsp;=20
result =3D (int)Shape::nshapes;<BR>&nbsp;&nbsp;&nbsp; =
<BR>&nbsp;&nbsp;&nbsp;=20
jresult =3D (int)result; <BR>&nbsp;&nbsp;&nbsp; return =
jresult;<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>=85BLA BLA=85</DIV>
<DIV>&nbsp;</DIV>
<DIV>DllExport long SquareToShape(long jarg1) {<BR>&nbsp;&nbsp;&nbsp; =
long=20
baseptr =3D 0;<BR>&nbsp;&nbsp;&nbsp; *(Shape **)&amp;baseptr =3D =
*(Square=20
**)&amp;jarg1;<BR>&nbsp;&nbsp;&nbsp; return baseptr;<BR>}<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>AND<BR>The Matching c# =
code:<BR>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D<=
BR>using=20
System.Runtime.InteropServices;<BR>class examplePINVOKE {<BR>&nbsp;=20
[DllImport("example")]<BR>&nbsp; public static extern void =
delete_Shape(int=20
jarg1);<BR>&nbsp; [DllImport("example")]<BR>&nbsp; public static extern =
void=20
set_Shape_x(int jarg1, double jarg2);<BR>&nbsp; =
[DllImport("example")]<BR>&nbsp;=20
public static extern double get_Shape_x(int =
jarg1);<BR>&nbsp;<BR>&nbsp;=85BLA=20
BLA=85</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; [DllImport("example")]<BR>&nbsp; public static extern int=20
CircleToShape(int jarg1);<BR>&nbsp; [DllImport("example")]<BR>&nbsp; =
public=20
static extern int SquareToShape(int jarg1);<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>AND</DIV>
<DIV>&nbsp;</DIV>
<DIV>using System;<BR>public class Shape : IDisposable {<BR>&nbsp; =
private int=20
swigCPtr;<BR>&nbsp; protected bool swigCMemOwn;</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; protected Shape(int cPtr, bool cMemoryOwn) =
{<BR>&nbsp;&nbsp;&nbsp;=20
swigCMemOwn =3D cMemoryOwn;<BR>&nbsp;&nbsp;&nbsp; swigCPtr =3D =
cPtr;<BR>&nbsp;=20
}</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; protected Shape() : this(0, false) {<BR>&nbsp; }</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; public virtual void Dispose() {<BR>&nbsp;&nbsp;&nbsp;=20
delete();<BR>&nbsp; }</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; protected void delete() {<BR>&nbsp;&nbsp;&nbsp; if(swigCPtr =
!=3D 0=20
&amp;&amp; swigCMemOwn) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=20
examplePINVOKE.delete_Shape(swigCPtr);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; =

swigCMemOwn =3D false;<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; =
swigCPtr =3D=20
0;<BR>&nbsp; }</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; protected static long getCPtr(Shape obj) =
{<BR>&nbsp;&nbsp;&nbsp;=20
return (obj =3D=3D null) ? 0 : obj.swigCPtr;<BR>&nbsp; }</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; public void setX(double x) {<BR>&nbsp;&nbsp;&nbsp;=20
examplePINVOKE.set_Shape_x(swigCPtr, x);<BR>&nbsp; }</DIV>
<DIV>&nbsp;</DIV>
<DIV>&nbsp; public double getX() {<BR>&nbsp;&nbsp;&nbsp; return=20
examplePINVOKE.get_Shape_x(swigCPtr);<BR>&nbsp; }</DIV>
<DIV>&nbsp;</DIV>
<DIV>=85BLA BLA=85<BR>}</DIV>
<DIV>&nbsp;</DIV>
<DIV>So if you got down this far ;) now what you have is:<BR>using a =
single=20
command you can reuse all the great c++ libraries in c#.<BR>Sounds =
good?!</DIV>
<DIV>&nbsp;</DIV>
<DIV>I think this route is the most robust and sensible way of reuse. Do =
you=20
agree? Will this be useful?<BR>I need to finish the work and then =
convince Dave=20
to add this into his Swig project. Swig offers pretty complete support =
of c++=20
including templates, virtual methods - take a look at the website. Quite =
a lot=20
of work remains to make sure we are writing decent c# code, to finish =
off and=20
thoroughly test. I havnt had the time to even try it on a c++ library =
bigger=20
than the example above - but I will!</DIV>
<DIV>&nbsp;</DIV>
<DIV><BR>Neil</DIV>
<DIV>416 436 6345</DIV>
<DIV></DIV></BODY></HTML>

------=_NextPart_000_02EB_01C29CB3.F14D4FD0--