[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