From: Charlie S. <cf...@in...> - 2006-02-12 20:52:24
|
Okay, I'll give this a try. Yesterday evening I implemented the way you suggested, and ran into an interesting issue. It conflicts with Ruby object tracking. I saw this because the library uses std::vectors of pointers to Geometry objects and children. The way the object tracking works is take the C++ pointer and look up the Ruby object. Thus, the static method to do the downcast will run, but then ConvertPtr will check the list of previously returned Ruby objects and give you back the old one since the C++ object (pointer) is of course the same. Aack! I think the fix is check the type of the returned Ruby object - if it is different then create a new Ruby object, and associate the ownership of the C++ with that new object. You of course need to keep the old object around but remove its ownership of the C++ object. That's not perfect because you now have 2 Ruby objects pointing to the same C++ object (which is how the Ruby bindings works without object tracking), but I don't see a way around that. So I'll update the object tracking to do this...just bummed that 1.28 got released yesterday because its a just few minute fix (hopefully!). Charlie Marcelo Matus wrote: > Sorry again, here, simpler version, we don't need to add the > dynamic_cast operation to derived types. > > Marcelo > > > Charlie Savage wrote: > >> Thanks Marcelo, >> >> Very helpful. >> Makes me wonder if there could be a more elegant way? Maybe a >> template method on the Geometry class, so at least the downcast >> method only has to be written once? >> >> Are there any plans to have SWIG generate this sort of code - at >> least the downcast method/s ? >> >> Thanks, >> >> Charlie >> >> >> >> Marcelo Matus wrote: >> >>> you need to add the dynamic cast operations for all the Geometry object >>> >>> %extend Circle { >>> static Circle *dcast(Geometry *g) { return dynamic_cast<Circle >>> *>(g); } >>> } >>> >>> %extend Point { >>> static Point *dcast(Geometry *g) { return dynamic_cast<Point >>> *>(g); } >>> } >>> >>> .... >>> >>> then use the infamous 'switch' operation, but implemented in Ruby >>> (here in pseudo C/Python): >>> >>> def unpackGeometry(geom_string): >>> geom = GeomFactory.new.parseGeometry(geom_string) >>> circle = GeomFactory.Circle.dcast(geom) >>> if (circle != Qnil) return circle >>> point = GeomFactory.Point.dcast(geom) >>> if (point != Qnil) return point >>> .... >>> return Qnil >>> >>> >>> and use it as >>> >>> obj = unpackGeometry(geom_string) >>> >>> then 'obj' will be what you expect, ie, a circle, or a point, etc. >>> >>> And of course, that is assuming Geometry has a virtual >>> member/descructor. >>> >>> class Geometry { >>> public: >>> virtual ~Geometry() {} >>> } >>> >>> >>> Marcelo >>> >>> >>> Charlie Savage wrote: >>> >>>> Hi everyone, >>>> >>>> Was wondering what the best way to handle this issue with C++ >>>> inheritance (seem like an obvious question, but didn't dig anything >>>> up with Google or the mailing list): >>>> >>>> class GeomFactory { >>>> Geometry* parseGeometry(char* geom_serialized_to_string) {} >>>> } >>>> >>>> class Geometry { >>>> Geometry() {} >>>> } >>>> >>>> class Point: public Geometry { >>>> Point() {} >>>> } >>>> >>>> class Line: public Geometry { >>>> Line() {} >>>> } >>>> >>>> Etc...including polygon, multipoint, multipolygon, geometry >>>> collection, etc. >>>> >>>> So from Ruby: >>>> >>>> geom_string = '<serialized form of geometry>' >>>> geom = GeomFactory.new.parseGeometry(geom_string) >>>> >>>> The class of geom will always be geom, not point, not line, not >>>> polygon. Of course, the underlying C++ object is really a point, >>>> line, etc. so the wrapper Ruby object is incorrect. >>>> How do I get the correct Ruby wrapper object - i.e., a Ruby point >>>> for a C++ point, a Ruby line for a C++ line, etc. >>>> >>>> Charlie >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> Question about C++ inheritance (I'm running into an issue with C++ >>>> inheritance and haven't managed to find anWas wondering if >>>> any...@li... >>> >>> >>> >>> >>> > > ------------------------------------------------------------------------ > > /* > Implement a more natural wrap for factory methods, for example, if > you have: > > %inline > { > struct Geometry { > enum GeomType{ > POINT, > CIRCLE > }; > > virtual ~Geometry() {} > virtual int draw() = 0; > static Geometry *create(GeomType i); > }; > > struct Point : Geometry { > int draw() { return 1; } > double width() { return 0.5; } > }; > > struct Circle : Geometry { > int draw() { return 1; } > double radius() { return 1.5; } > }; > } > > %{ > Geometry *Geometry::create(GeomType i) { > switch (i) { > case POINT: return new Point(); > case CIRCLE: return new Circle(); > default: return 0; > } > } > %} > > You can use the %factory_method as follows: > > %newobject Geometry::create; > %factory_method(Geometry::create, Geometry, Point, Circle); > > and Geometry::create will return a 'Point' or 'Circle' instance > instead of the plain 'Geometry' type. > > circle = Geometry.create(Geometry.CIRCLE) > r = circle.radius() > > */ > %define %_factory_dispatch(Type){ > Type *type = dynamic_cast<Type *>($1); > if (type) return SWIG_NewPointerObj(%as_voidptr(type),$descriptor(Type *), $owner); > }%enddef > > %define %factory_method(Method,Base,Types...) > %typemap(out,noblock=1) Base *Method { > %formacro(%_factory_dispatch, Types) > return SWIG_NewPointerObj(%as_voidptr($1),$descriptor, $owner); > }%enddef > |