Re: GObjectClass->constructor revised, g_object_newv_with_data



On Tue, 2005-07-05 at 02:26 +0200, Tim Janik wrote:
> On Tue, 7 Jun 2005, Gustavo J. A. M. Carneiro wrote:
> 
> >  I'd like to propose an API enhancement to GObject, in order to solve
> > pygtk bugs 161177 and 123891 in a way that doesn't involve some "hack".
> >
> >  As people may or may not know, in PyGTK there is, for each GObject, a
> > corresponding "python wrapper" PyObject.  Normally, the PyObject is
> > create first, then __init__ is called, and this in turn calls the parent
> > __init__ which takes care of creating the GObject with g_object_newv.
> >
> >  Now, in bug 123891 we'd like to solve the "construction properties"
> > problem.  The only place where we may set construction properties is, as
> > you may know, in the GObjectClass->constructor method.
> 
> what do you mean with "we may set construction properties"?
> construction properties are supposed to be passed in as (name,value)
> pairs to g_object_newv(), and they'll be set by the GObject system
> automatically at the right time.

  Currently they'll be set by the GObject system at the wrong time, i.e.
before the python proxy object is attached to the GObject instance.  So
the idea to workaround this problem was to strip the construction
properties while calling standard GObject constructor, then attach the
python proxy, and only then set the construction properties.

> 
> >  Fine, let's
> > override GObject->constructor for PyGTK GObjects then.  But there's a
> > problem, here.
> 
> overriding the constructor is meant to be useful only for implementing 
> singletons or object construction that needs execution after all construct
> arguments are set. here's the initial proposal for the pupose of constructor():
>      http://mail.gnome.org/archives/gtk-devel-list/2000-August/msg00322.html
> though it's a bit dated it still carries a lot of relevant meat.
> 
> >  In the case described above, we already have a PyObject
> > for this GObject, and we need to "attach" the PyObject to the GObject
> > inside the construction, before setting any construction properties.
> 
> is this requirement in place, because you are actually talking about
> construction properties implemented in python?

  Yes.  Construction properties implemented in Python do not work yet
and we'd like to fix it at the same time (bug #123891).

> 
> > However, there is currently no way to pass the PyObject into the
> > constructor.  The idea of creating a special property to pass the data
> > into the constructor was mentioned, but this is an ugly hack we'd like
> > to avoid at all costs.
> 
> if you implement your construction properties in python and they need
> the PyObject attached already, this could also break for two reasons:
> - argument ordering, currently the construct parameters are set in the order
>    trhey are passed in to g_object_new(), so may not always have the PyObject
>    property coming first.
> - construct properties set from derived types, if an object derived from
>    your python objects sets a construct property in its _init() function
>    your PyObject wouldn't be setup yet (though depending on your support of
>    GInstanceInitFunc this may not be triggered by your binding)
> 
> >  Related to this is bug 161177, which aims to fix the problem of
> > creating custom python GObjects from a C g_object_new call.  For this to
> > work, we need to create the PyObject inside the GObject constructor.
> > And of course the constructor needs to "know" whether a PyObject already
> > exists or not.
> 
> i'm not sure i fully understand your problems here, so far i see
> three use cases:
> 1) you create a GObject derived C object and have a PyObject proxy
> 2) you create a GObject derived Python object and have a PyObject proxy
> 3) you create a GObject derived Python object and have no PyObject proxy
> 
> i suppose (1) is as easy as:
> static void
> python_proxy_create_gobject (PyObject       *pob,
>                               GType           object_type,
>                               guint           n_parameters,
>                               GParameter     *parameters)
> {
>    GObject *object = g_object_newv (object_type, n_parameters, parameters);
>    python_proxy_attach (pob, object);
> }

  If we attach the proxy after g_object_newv returns then it is already
too late to handle construction properties in python space.

> 
> for (2), you derived from some GObject type, so you get to provide your own
> GInstanceInitFunc and can do something like:
> 
> static __thread PyObject *current_proxy = NULL;
> 
> static void
> python_gobject_init (GObject    *object,
>                       GTypeClass *g_class)
> {
>    PyObject *pob = current_proxy;
>    current_proxy = NULL;
>    python_proxy_attach (pob, object);
> }

[ Wow, didn't know about __thread for TLS.  Is this standard and
portable? ]

  OK, using TLS for this is a hack.  We have a patch using this
approach, but it's not "nice" at all.

> 
> static void
> python_proxy_create_python_object (PyObject       *pob,
>                                     GType           python_gobject_type,
>                                     guint           n_parameters,
>                                     GParameter     *parameters)
> {
>    g_assert (current_proxy == NULL);
>    current_proxy = pob;
>    GObject *object = g_object_newv (python_gobject_type, n_parameters, parameters);
> }
> 
> and the extension to cover (3):
> 
> static PyObject*
> create_python_gobject             (GType           python_gobject_type,
>                                     guint           n_parameters,
>                                     GParameter     *parameters)
> {
>    PyObject *pob = python_proxy_create();
>    python_proxy_create_python_object (pob, python_gobject_type,
>                                       n_parameters, parameters);
>    return pob;
> }
> 
> >  Another problem is that GObject->constructor currently does not allow
> > chaining to parent class in some situations.  For objects implemented in
> > C, we normally have something like:
> >
> > static GtkBinClass *parent_class = NULL;
> >
> >  This is fine in one-source-file-per-gobject case, since you can you
> > the global variable which is local to the type.  In case of pygtk, the
> > same C function has to be used for all types, and there is no way to
> > obtain the "parent class" from the constructor.
> 
> since you don't create a new constructor for every new python object,
> you could get at the relevant parent type in your implementation by
> walking while (is_python_type (type)) type = g_type_parent(type); but
> you should be handling your proxy setup in _init() anyway as explained
> above.

  This breaks when deriving Python -> C -> Python, for example.

> 
> >  The proposed solution involves possibly deprecating
> > GObjectClass->constructor, and adding a new
> > GObjectClass->constructor_full with more parameters, and adding a new
> > API g_object_newv_with_data to create GObjects while passing some data
> > to the constructor.
> >
> >  Comments welcome.
> 
> ok, got some:
> - i don't think the API changes are neccessary, as far as i understood your
>    problems, you can apply the solutions for 1-3 above.

  The solutions 1-3 above are hacks.  If GObject had the proper
infrastructure, we wouldn't need TLS at all.

> - i'm very reluctant to *changing* GObject API to that degree at this point,
>    i.e. deprecating the constructor or virtual functions etc.
>    (in general, i'm fine with the idea of API additions though)

  Don't deprecate constructor, then, but at least support calling
constructor_full if available instead of constructor.

> - constructor_full() would have been an awfull name, "_full" doesn't carry any
>    sensible semantic content for API function names.

  Does constructor_fixed sound better? ;)

> - using a GData* location pointer as a generic data container is an abuse of
>    the GData API, to put it politely ;)
>    using gpointer data; like you get it with signal handlers would have been
>    good enough, since any structure can be referenced by that data pointer.

  Using GData* is safer than just gpointer.  If you look at
cross-language inheritance scenario, with gpointer, you risk passing to
a parent constructor a PyObject, and if the parent constructor just
happens to a perl thing expecting a perl structure then you get an
interesting crash.

> 
> >
> > PS: patch is in bug 305560
> 
> thanks for providing a patch, though i think we should
> take a different approach ;)

  At least my conscience is cleared.  I tried to keep pygtk code
minimally readable.  If anyone asks, I'll blame you.. ;-)

  Anyway, thanks for looking into this.

  Regards.

-- 
Gustavo J. A. M. Carneiro
<gjc inescporto pt> <gustavo users sourceforge net>
The universe is always one step beyond logic.




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]