Re: GObjectClass->constructor revised, g_object_newv_with_data



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.

 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?

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

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

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.

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


PS: patch is in bug 305560

thanks for providing a patch, though i think we should
take a different approach ;)

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

---
ciaoTJ



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