Re: GObjectClass->constructor revised, g_object_newv_with_data



On Tue, 5 Jul 2005, Gustavo J. A. M. Carneiro wrote:

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.

i'd rather say, you are trying to setup your proxy at the wrong time, i.e.
after _init() which is too late. if you properly setup your proxy in _init()
construct properties will work fine out of the box.

 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.

right this'd just be a "workaround". trying to kludge around the problems
you created yourself with not setting up the proxy in _init() ;)

 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.

no, this is case (1), i.e. you are creating a C Object and have nothing todo
with its construction properties, e.g. a GtkButton. so attaching the python
proxy to the fully constructed object is good enough.

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.

i've not seen a patch doing this in bugzilla. and no, you need to check
at configure *and* at runtime whether __thread is available since support
for it may be broken depending on your glibc version, gcc version and
kernel version (the latter is what might make it break at runtime).
it's just a concise way of writing g_private_get() and g_private_set()
however so it can be fully portable for you.

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.

not if you put appropriate logic into your _init() function, suppose you
derive: GObject -> PyA -> ObjC -> PyB
then _init() of PyA should properly setup the python proxy for your instance
(either demand create a proxy or take it from TLS), when ObjC sets construcotr properties from its _init() function, their python implementations
will just work and _init() of PyB should just figure that the object already
has a python proxy.

 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.

you may call them anyway you want, they have significant advantages over all patches i've seen in bugzilla regarding the python bugs however:
- they are a *lot* smaller than anything you have in bugzilla (basically, the
  problem with all your patches is that they try to use the constructor in one
  way or the other which relly is the wrong place to attach the proxy, it's
  a guarantee for future problems).
- they solve ObjC setting python construct properties in _init() which none
  of your patches does.
- they don't require modification of the GObject API or GObject implementations

- 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.

if you have no idea what constructor is being called, you probably shouldn't
pass in data to newv_with_data() anyway. but this dicussion is pointless, attaching the proxy in the constructor is wrong in the first place, you need to do your work in _init() as early as possible, since construct properties
may be set from _init() functions (that's the reason g_object_set() has the
object_in_construction() check and allows construction properties).

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.. ;-)

i'd expect the few lines i posted are much more readable than anything
you have now (special casing of construct properties, modification
of GParameter ararys, etc. etc...)

so i'm quite willing to take the blame for that ;)

 Anyway, thanks for looking into this.


---
ciaoTJ



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