[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> </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> Shape() {<BR> =20
nshapes++;<BR> }<BR> virtual ~Shape() =
{<BR> =20
nshapes--;<BR> };<BR> double x, y; =
<BR> =20
void move(double dx, double dy);<BR> virtual =
double=20
area(void) =3D 0;<BR> virtual double perimeter(void) =3D =
0;<BR> =20
static int nshapes;<BR> };</DIV>
<DIV> </DIV>
<DIV>class Circle : public Shape {<BR>private:<BR> double=20
radius;<BR>public:<BR> Circle(double r) : radius(r) { };<BR> =
virtual=20
double area(void);<BR> virtual double perimeter(void);<BR>};</DIV>
<DIV> </DIV>
<DIV>class Square : public Shape {<BR>private:<BR> double=20
width;<BR>public:<BR> Square(double w) : width(w) { };<BR> =
virtual=20
double area(void);<BR> virtual double =
perimeter(void);<BR>};<BR>#define=20
M_PI 3.14159265358979323846</DIV>
<DIV> </DIV>
<DIV>/* Move the shape to a new location */<BR>void Shape::move(double =
dx,=20
double dy) {<BR> x +=3D dx;<BR> y +=3D dy;<BR>}</DIV>
<DIV> </DIV>
<DIV>int Shape::nshapes =3D 0;</DIV>
<DIV> </DIV>
<DIV>double Circle::area(void) {<BR> return =
M_PI*radius*radius;<BR>}</DIV>
<DIV> </DIV>
<DIV>double Circle::perimeter(void) {<BR> return =
2*M_PI*radius;<BR>}</DIV>
<DIV> </DIV>
<DIV>double Square::area(void) {<BR> return =
width*width;<BR>}</DIV>
<DIV> </DIV>
<DIV>double Square::perimeter(void) {<BR> return =
4*width;<BR>}</DIV>
<DIV> </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> </DIV>
<DIV>#define DllExport __declspec( dllexport )<BR>extern "C" =
{<BR>DllExport void delete_Shape(int jarg1) {<BR> =
Shape *arg1=20
=3D (Shape *) 0 ;<BR> <BR> arg1 =3D =
*(Shape=20
**)&jarg1; <BR> delete arg1;<BR>}</DIV>
<DIV> </DIV>
<DIV>DllExport void set_Shape_x(int jarg1, double jarg2) =
{<BR> =20
Shape *arg1 =3D (Shape *) 0 ;<BR> double arg2=20
;<BR> <BR> arg1 =3D *(Shape =
**)&jarg1;=20
<BR> arg2 =3D (double)jarg2; <BR> if =
(arg1)=20
(arg1)->x =3D arg2;<BR> <BR>}</DIV>
<DIV> </DIV>
<DIV>DllExport double get_Shape_x(int jarg1) {<BR> =
double=20
jresult =3D 0 ;<BR> Shape *arg1 =3D (Shape *) 0=20
;<BR> double result;<BR> =20
<BR> arg1 =3D *(Shape **)&jarg1; =
<BR> =20
result =3D (double) ((arg1)->x);<BR> =
<BR> =20
jresult =3D (double)result; <BR> return =
jresult;<BR>}</DIV>
<DIV> </DIV>
<DIV>DllExport void Shape_move(int jarg1, double jarg2, double jarg3)=20
{<BR> Shape *arg1 =3D (Shape *) 0 =
;<BR> double=20
arg2 ;<BR> double arg3 ;<BR> =20
<BR> arg1 =3D *(Shape **)&jarg1; =
<BR> arg2=20
=3D (double)jarg2; <BR> arg3 =3D (double)jarg3;=20
<BR> (arg1)->move(arg2,arg3);<BR> =
<BR>}</DIV>
<DIV> </DIV>
<DIV>DllExport int get_Shape_nshapes() {<BR> int =
jresult =3D 0=20
;<BR> int result;<BR> =
<BR> =20
result =3D (int)Shape::nshapes;<BR> =
<BR> =20
jresult =3D (int)result; <BR> return =
jresult;<BR>}</DIV>
<DIV> </DIV>
<DIV>=85BLA BLA=85</DIV>
<DIV> </DIV>
<DIV>DllExport long SquareToShape(long jarg1) {<BR> =
long=20
baseptr =3D 0;<BR> *(Shape **)&baseptr =3D =
*(Square=20
**)&jarg1;<BR> return baseptr;<BR>}<BR>}</DIV>
<DIV> </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> =20
[DllImport("example")]<BR> public static extern void =
delete_Shape(int=20
jarg1);<BR> [DllImport("example")]<BR> public static extern =
void=20
set_Shape_x(int jarg1, double jarg2);<BR> =
[DllImport("example")]<BR> =20
public static extern double get_Shape_x(int =
jarg1);<BR> <BR> =85BLA=20
BLA=85</DIV>
<DIV> </DIV>
<DIV> [DllImport("example")]<BR> public static extern int=20
CircleToShape(int jarg1);<BR> [DllImport("example")]<BR> =
public=20
static extern int SquareToShape(int jarg1);<BR>}</DIV>
<DIV> </DIV>
<DIV>AND</DIV>
<DIV> </DIV>
<DIV>using System;<BR>public class Shape : IDisposable {<BR> =
private int=20
swigCPtr;<BR> protected bool swigCMemOwn;</DIV>
<DIV> </DIV>
<DIV> protected Shape(int cPtr, bool cMemoryOwn) =
{<BR> =20
swigCMemOwn =3D cMemoryOwn;<BR> swigCPtr =3D =
cPtr;<BR> =20
}</DIV>
<DIV> </DIV>
<DIV> protected Shape() : this(0, false) {<BR> }</DIV>
<DIV> </DIV>
<DIV> public virtual void Dispose() {<BR> =20
delete();<BR> }</DIV>
<DIV> </DIV>
<DIV> protected void delete() {<BR> if(swigCPtr =
!=3D 0=20
&& swigCMemOwn) {<BR> =20
examplePINVOKE.delete_Shape(swigCPtr);<BR> =
swigCMemOwn =3D false;<BR> }<BR> =
swigCPtr =3D=20
0;<BR> }</DIV>
<DIV> </DIV>
<DIV> protected static long getCPtr(Shape obj) =
{<BR> =20
return (obj =3D=3D null) ? 0 : obj.swigCPtr;<BR> }</DIV>
<DIV> </DIV>
<DIV> public void setX(double x) {<BR> =20
examplePINVOKE.set_Shape_x(swigCPtr, x);<BR> }</DIV>
<DIV> </DIV>
<DIV> public double getX() {<BR> return=20
examplePINVOKE.get_Shape_x(swigCPtr);<BR> }</DIV>
<DIV> </DIV>
<DIV>=85BLA BLA=85<BR>}</DIV>
<DIV> </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> </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> </DIV>
<DIV><BR>Neil</DIV>
<DIV>416 436 6345</DIV>
<DIV></DIV></BODY></HTML>
------=_NextPart_000_02EB_01C29CB3.F14D4FD0--