[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