opCast, c bindings and normal casts.

Steven Schveighoffer schveiguy at yahoo.com
Tue Jul 12 06:14:18 PDT 2011


On Sat, 09 Jul 2011 05:47:51 -0400, Johannes Pfau <spam at example.com> wrote:

> Hi,
>
> I have a wrapper for a "object aware" c library (cairo). Take for
> example two classes, Surface and a subclass, ImageSurface. Now this
> code has to be valid:
> -----------------------
> auto ptr = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 512, 512);
> Surface s = new Surface(ptr);
> ImageSurface imgs = cast(ImageSurface)s;
> -----------------------
>
> As D cannot know that 's' really should be an ImageSurface, I have
> implemented opCast to get this example working:
> -----------------------
> class Surface
> {
>     static Surface castFrom(Surface other)
>     {
>         return other;
>     }
>    T opCast(T)() if(isImplicitlyConvertible!(T, Surface))
>     {
>         return T.castFrom(this);
>     }
> }
> class ImageSurface : Surface
> {
>     static ImageSurface castFrom(Surface other)
>     {
>         auto type = cairo_surface_get_type(other.nativePointer);
>         if(type == cairo_surface_type_t.CAIRO_SURFACE_TYPE_IMAGE)
>         {
>             return new ImageSurface(other.nativePointer);
>         }
>         else
>             return null;
>     }
> }
> -----------------------
>
> This code works quite well. But it performs unnecessary calls to
> cairo_surface_get_type (and allocates unnecessary objects) for simple
> cases:
> -----------------------
> auto surface = new ImageSurface(Format.CAIRO_FORMAT_ARGB32, 400, 400);
> Surface tmp = cast(Surface)surface;
> ImageSurface test = cast(ImageSurface)as;
> -----------------------
>
> In this case, the first D object already is an ImageSurface so the
> custom opCast code isn't needed for the last line.
>
> So the question is: Is there some way to check in the opCast function
> if a normal D object cast would succeed and then just return it's
> result?

I think a factory method would work well here.

If you have a finite set of classes you are creating, a factory method can  
simply use a switch on the cairo_surfase_type, and you could even put it  
in Surface:

auto s = Surface.create(ptr); // automatically creates the correct derived  
class.

Then use dynamic cast to get to the expected derived class.

If you do not have a finite set of classes, you may be able to use runtime  
type info (a la object.factory).  But from your example, it seems like  
cairo defines an enum which encapsulates all classes.

Another option, judging from your code, if cairo's functions to create  
surface objects are specific to the derived type (i.e.  
cairo_image_surface_create => ImageSurface), then you could simply wrap  
the cairo functions.  Basically avoid calling the C creation routines  
outside the D class constructors.

-Steve


More information about the Digitalmars-d-learn mailing list