[API] GObject watched closures



hi,

i just noticed a function missing from gobject.[hc]:

GSList* g_object_list_watched_closures (GObject *object);

for code portions which deal with closures that use objects
as data, aside from signal connections, this is required
to allow disconnections, i.e., provided we have a notification
source:

void foo_notify_connect (GClosure *closure);
void foo_notify_disconnect (GClosure *closure);

then, the (func, data) API will look like:

void foo_object_connect (GObject *object,
                         gpointer func,
                         gpointer data,
                         gpointer destroy_func)
{
  GClosure *closure = g_cclosure_new (func, object, destroy_func);
  
  g_object_watch_closure (object, closure); /* binds closure life time to object */
  foo_notify_connect (closure);
}

void foo_object_disconnect (GObject *object,
                            gpointer func,
                            gpointer data)
{
  GSList *node, *slist = g_object_list_watched_closures (object);
  
  for (node = slist; node; node = node->next)
    {
      GClosure *closure = node->data;
      
      if (closure->data == data && closure->func == func)
        {
          g_closure_invalidate (closure);
          foo_notify_disconnect (closure);
          g_slist_free (slist);
          return;
        }
    }
  g_slist_free (slist);
  
  g_warning ("Couldn't find %p(%p)", func, data);
}

an argument could be made, that foo_object_connect() should maintain
a list of constructed closures on object as object data, so
foo_object_disconnect() could find the required closure from
that list. however, this list needs to be cleaned up once the object
or closure dies, which is essentially duplicating the logic (and data)
already implemented behind g_object_watch_closure().

note that the signal system, which also notifies via closure invokation,
implements its own, very special hackery to work around this problem, by
letting:

gulong   g_signal_handler_find                (gpointer           instance,
                                               GSignalMatchType   mask,
                                               guint              signal_id,
                                               GQuark             detail,
                                               GClosure          *closure,
                                               gpointer           func,
                                               gpointer           data);

peek at the ->callback and ->data members of the closures it keeps internally,
if this functions is invoked as

g_signal_handler_find (object, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
                       0, 0, NULL,
                       func, data);
[g_signal_handler_find() has the same API and is the backend function being
used behind functions like g_signal_handlers_block*() or
g_signal_handlers_disconnect*()]

i don't think the same hackery should be forced on every implementation
that uses closures as object callbacks. here's an example for code
notification components ala foo_notify_connect()/foo_notify_disconnect()
(which of course doesn't implement the same hackery):

void bonobo_ui_component_add_listener_full   (BonoboUIComponent  *component,
                                              const char         *id,
                                              GClosure           *closure);
void bonobo_ui_component_remove_listener_by_closure (BonoboUIComponent  *component,
                                                     GClosure           *closure);

since GClosure is our new canonical mechanism for callback maintenance,
writing code like foo_notify_connect()/foo_notify_disconnect() is actually
recommended now, and thus convenience wrappers like foo_object_connect()
and foo_object_disconnect() are also likely to appear. so in order for
people to be able to actually implement foo_object_disconnect() variants,
i think we should add g_object_list_watched_closures() to GObject, despite
the API freeze, comments are aprechiated.

---
ciaoTJ




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