[cairo] Re: cairo c++ bindings

Benoit Perrot benoit at lrde.epita.fr
Wed Nov 23 08:06:24 PST 2005


Hi everyone,

> > here's a preview
> >
> > http://downloads.paniq.org/cairo.hpp
>
> Excellent. I encourage everyone to take a look and think about how the
> bindings should look.

I'm pretty new to cairo, but I've worked on a small, quite different
C++ wrapper and I'm a little surprised of the points I read here.
I hope my questions/remarks can interest some of you ;)

> - It's desirable to hide the C headers from the C++ headers. Among other
>   things, that means:
>   - using numbers instead of enum values for the C++ enum values, as in
>     gtkmm.

Isn't there a risk to have the C++ enum values desynchronized with the
C ones by doing that?

>   - not deriving from the C struct unless it's really necessary.

It seems to me that deriving from the C struct is highly dangerous:
what happen if, for a reason or another the compiler decides to add
informations before the "relevant" data block?
Like in:

class Matrix : public cairo_matrix_t
{
  void init_identity()
  {
    cairo_matrix_init_identity(this);
  }
};

What happen if the data that cairo_matrix_init_identity is expecting
are not at the same address as this? (let imagine a pointer to a
potential virtual table...)

>   - forward-declaring C struct types and using them as little as possible
>     in headers. #include the definition in the .cc file.

But a wrapper should come with the smallest possible overhead, so most
of the methods should be hinted as inline, making the #include
<cairo.h> in the .hh necessary. (It is also needed to ensure the
synchronization of the C and C++ enumerations).

> - Non-copyable classes should have a private, undefined, copy constructor.
> - The operator * and operator = overrides are a little odd. I'd prefer
>   explicit conversion, and that's the style used by gtkmm.

In my opinion, a nice cairo C++ wrapper should help a developper by
hidding him implementation details, such as the necessity of
explicitely calling "cairo_*_reference" at each object copy.
I think that it could also limit/simplify future memory management issues.


Consider the following snippet:

namespace cairo
{

  class surface
  {
  public:
    explicit surface (cairo_surface_t *cairo_surface):
      cairo_surface_ (cairo_surface)
    {}

    surface::surface (const &s):
      cairo_surface_ (s.cairo_surface_)
    {
      cairo_surface_reference (s.cairo_surface_);
    }

    surface &
    surface::operator= (const &rhs)
    {
      cairo_surface_destroy (cairo_surface_);
      cairo_surface_ = rhs.cairo_surface_;
      cairo_surface_reference (cairo_surface_);
    }

    ~surface ()
    {
      cairo_surface_destroy (cairo_surface_);
      cairo_surface_ = 0;
    }

  protected:
    cairo_surface_t *cairo_surface_;
  };
}

There:

- The copy constructors dereference the current surface and
  reference the new one automaticaly

- The destructor dereference the cairo_surface_t automaticaly

- The explicit constructor allows developper to bind easily
  with existing/external C code:

void
foo ()
{
  cairo::surface s(c_function_creating_a_cairo_surface());

  // [Operate on s]

  // s::~surface is automatically called, the shared surface
  // is dereferenced.
}


What do you think?

-- 
Benoit Perrot




More information about the cairo mailing list