[DotGNU]Strategy for dealing with C++ virtual functions in a managed binding.
Neil Cawse
neilcawse at hotmail.com
Fri Dec 6 04:10:59 GMT 2002
Automatically reusing c++ libraries in c#
=========================================
Ive spent quite a bit of time researching how best to reuse the existing
great libraries written 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!
----- Original Message -----
From: "Adam Treat" <manyoso at yahoo.com>
To: "James Michael DuPont" <mdupont777 at yahoo.com>; <developers at dotgnu.info>
Cc: <kde-devel at kde.org>; <kde-core-devel at kde.org>
Sent: Tuesday, December 03, 2002 12:47 AM
Subject: Re: [DotGNU]Strategy for dealing with C++ virtual functions in a
managed binding.
> On Tuesday 03 December 2002 12:09 am, James Michael DuPont wrote:
> > --- Adam Treat <manyoso at yahoo.com> wrote:
> > > Hello All,
> > >
> > > I would like to pick the brains of the collective intelligence and
> > > come up
> > > with a good solid strategy for dealing with C++ virtual functions in
> > > a
> > > binding.
> >
> > /me ears pick up something interesting
> >
> > > Our new strategy is then to call the libqt.so directly using mangled
> > > method
> > > names in our DllImport attributes.
> > >
> > > This is accomplished by using a
> > > combination of 'nm' and 'cppfilt' during Qt# binding creation.
> >
> > I would like too see this code. /me thinks you can use libiberty and
> > the introspector or gcc_xml for this.
>
> Oh, we already have a strategy for mangling the names. A script and
> nm+cppfilt works nicely. I can produce this script for you tomorrow (it's
> late ;) We looked at libiberty for this, but it only provides a mechanism
> for 'demangling' and does not provide a way to 'mangle' given a string
> representing a function prototype.
>
> > > The
> > > idea is
> > > to use an instance pointer as an invisible first parameter and then,
> > > using
> > > the mangled name, call libqt directly.
> >
> > like the gnu libjava
>
> Oh? libjava uses this method? Is this part of gcj then?
>
> > > Oh, and our binding generator
> > > is also
> > > capable of an easy extension to bind any C++ lib in case anyone is
> > > interested
> > > in binding other C++ libraries ;)
> >
> > yes, let us see the basic axioms, we can build this in a standard
> > process.
> >
> > at this point, I would like to know why cannot you use the internal
> > call system? that has a binding generator as well, no?
>
> The binding generator is pretty flexible and easy to extend to other C++
> API's. We use a custom C++ header parser written in C# to produce an XML
> file representing the C++ API. This XML file is then processed through
our
> binding generator called 'binge'. What do you mean by 'use the internal
call
> system' ?? I have no idea what you are referring to nor do I understand
'has
> a binding generator as well, no' ... what has a binding generator as well?
>
> > > The preferred solution would
> > > be to
> > > somehow override the virtual table to call a managed function
> > > directly and
> > > then somehow forward this to the appropriate C# virtual function.
> >
> > you can grab pointers to virtual function entries,
> > and given an object and a vf pointer call the object.
> >
> > please send me more info,
> > I just might be able to help.
>
> Awesome! I don't know what kind of info would be helpful ... Our ideal
> solution would allow us to associate a delegate with a C# virtual function
> ... when the virtual function is implemented the binding would pass the
> delegate to a C glue method as a function pointer ... the C glue method
could
> then do some magic with the virtual table in libqt.so and install the
> delegate in place of the existing function pointer. I know how to do all
of
> this except play with the Qt vtable in native code ...
>
> Hope this info helps,
>
> Adam
> _______________________________________________
> Developers mailing list
> Developers at dotgnu.org
> http://www.dotgnu.org/mailman/listinfo/developers
>
>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<
More information about the kde-core-devel
mailing list