FrenchZ has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I have some object with multiple inheritance :

class D : public B1, public B2

I made XS interface for B1,B2 and D classes, and I set ISA to match inheritance in the perl packages.

I have a function that have parameters from type B2 and I want to pass a D to this function.

int func(B2* obj1,B2* obj2){ return obj1 == obj2; } D* d_ptr = new D();<BR> B2* b2_ptr = dynamic_cast<B2*>(d_ptr);<BR> func(d_ptr,b2_ptr); --> return true

this works fine in full C++ but in the XS mapping the following process is in action: D* object is cast on a void* to be came a perl reference. then this void * is cast into a B2*.

here is my perlobject.map:

# "perlobject.map" Dean Roehrich, version 19960302<BR> #<BR> # TYPEMAPs<BR> #<BR> ############################################################<BR> OUTPUT<BR> # The Perl object is blessed into 'CLASS', which should be a<BR> # char* having the name of the package for the blessing.<BR> T_X_PTR<BR> sv_setref_pv( $arg,${ntype}_Package, (void*)$var );<BR> <p> ############################################################<BR> INPUT<BR> T_X_PTR<BR> if(sv_derived_from($arg,${ntype}_Package)) { <BR> IV tmp_ = SvIV((SV*)SvRV($arg));<BR> $var = ($type)tmp_;<BR> }<BR> else<BR> croak(\"$var is not of type %s\",${ntype}_Package);<BR>

if I compare the final B2* and the original D*, the address of the pointed object has changed due to the void* cast that do not work correctly with multiple inheritance.
This is a problem for me because I compare addresses of objects. so that the function return false!!

Do you know how to fix this?
Is there a safe way to map multiple inherited objects in perl ??

thanks in advance

Edit kudra, 2001-08-08 Added code tags

Replies are listed 'Best First'.
Re: XS typemap and C++ multiple inheritance
by John M. Dlugosz (Monsignor) on Aug 03, 2001 at 02:54 UTC
    OK, when you cast the D* to a void*, you must recover it by un-doing the exact same cast, and casting it to a D*. That's a general principle in C++.

    So, when you do the XS stuff, be consistant about which type of pointer you put into the void, so you get the same thing out. Then, having gotten out your standard form, then dynamic_cast it to the type you really wanted.

    —John

      Thank you John,
      Your analysys is the same than mine. But my problem is how to put into the void* my true type.

      I really don't see how. Except if the void* that I put in my T_X_PTR would be a little encapsualted C Struct like:
      enum MyType {D1_TYPE, B1_TYPE, B2_TYPE, D2_TYPE};
      struct XS_TypeInfo
      {
      void* info;
      enum MyType trueType;
      };

      But even with that means that I would have to rewrite my typemap. and to hard-code in it the decoding of the void* info depending on trueType.

      TIA,
      ~Xavier

        If they are not all related into a single concrete derived class, then yes that's the way to do it. Your question showed an example of multiple inheritance, though, so the fix there is to always be consistant as to what you cast it to before sticking it in the void*.

        —John

Re: XS typemap and C++ multiple inheritance
by rsteinke (Scribe) on Jun 15, 2002 at 15:59 UTC

    One way to do this would be to have a base object class, and use dynamic_cast<>(). You can do this even if you're wrapping a set of classes that don't already have common base class. Say you have classes Foo and Bar, and a class Baz which is derived from Foo and Bar. You then derive classes

    class Obj {...}; class MyFoo : public Foo, virtual public Obj {...}; class MyBar : public Bar, virtual public Obj {...}; class MyBaz : public Baz, virtual public Obj {...};

    You have to wrap the constructors, but otherwise these objects act just like your original classes. Be sure to give Obj a virtual destructor.

    Whenever you pass a blessed object to Perl, static_cast<>() it to Obj before casting to void.

    void *ptr = (void*) static_cast<Obj*>(foo);

    Reverse the cast when you get a method call

    Foo* foo = dynamic_cast<MyFoo*>((Obj*) ptr);
    The blessing into the correct class should even work automatically, but check the XSUB code to be sure.