[cairo] API completeness of reference/user_data pairs

Behdad Esfahbod behdad at behdad.org
Sun Feb 4 14:47:28 PST 2007

On Mon, 2007-01-29 at 23:39 -0500, William Lahti wrote:
> Why does user data help bindings again? 

One thing: to assign a single wrapper object to one native object.
That has various desired effects.  Consider this construct (written for
pycairo, but applies to all bindings):

surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, 100, 100)
cr = cairo.Context (surface)
surface2 = cr.get_target ()

So, ideally you should not be able to tell between surface and surface2.
That's essentially what user data can buy you:  you attach the wrapper
object to the native object as user data and let the native object own
the wrapper.  Instead of keeping a separate ref count on the wrapper
object, you just redirect ref/unref to the native object.

This gives you the desired effect.  surface and surface2 in the above
example cannot be differentiated.  Some important uses of this are:

  - surface and surface2 will compare equal.  This can be achieved by
overloading the equality operator on it to compare the underlying native
object pointer, but that get out of control soon: imagine if all objects
had such a compare function.

  - You can subclass cairo.ImageSurface, create a context for your
subclass surface, and get the surface back from target and see it be of
your subclassed type, not ImageSurface.  Currently you cannot even
subclsas ImageSurface though.  I don't see why not.  PyGtk supports that
and it's quite essential there (to write widgets for example).

  - Similar to above, but with native subclasses...  Doesn't quite apply
to cairo though since cairo doesn't have an extendable object system,
but again, crucial in PyGtk.  Currently pycairo implements this using
things like cairo_surface_get_type() I think.  So when you call
cr.get_surface(), it uses cairo_surface_get_type() to decide what 

Here is a little test program:

import cairo

        class MySurface (cairo.ImageSurface):
        print "cairo.ImageSurface successfully subclassed. Cool!"
except TypeError:
        MySurface = cairo.ImageSurface
        print "cairo.ImageSurface couldn't be subclassed"
        print "continuing with cairo.ImageSurface"

surface = MySurface (cairo.FORMAT_ARGB32, 100, 100)
cr = cairo.Context (surface)
surface2 = cr.get_target ()

print "Surface was:", repr(surface)
print "Surface is: ", repr(surface2)
print "Wrapper object retained?", repr(surface) == repr(surface2)
print "Equality operator works or overloaded to work?", surface ==
print "Surface had type: ", repr(type(surface))
print "Surface has type: ", repr(type(surface2))
print "Types equal:      ", type(surface) == type(surface2)
if MySurface == cairo.ImageSurface:
        print "(No surprise: cairo.ImageSurface couldn't be subclassed)"


"Those who would give up Essential Liberty to purchase a little
 Temporary Safety, deserve neither Liberty nor Safety."
        -- Benjamin Franklin, 1759

More information about the cairo mailing list