Re: simplifying closures



Karl Nelson <kenelson ece ucdavis edu> writes:
> [...]
> 
> > But I'm saying there may only be one parent.
> 
> I was stating what the design plan was, just for clarification.  It
> wasn't really clear to others what the use of the lists where. (IMO)
>

No doubt, the original plan here has never been posted. We only know
it from talking to Tim in person when he was visiting here recently.
  
> That is a very big unless.  I don't see how you can solve the problem
> of the object which created the closure used up the invalid and free,
> and the connected object now wishs to add something.  It now
> must add an intermediate proxy which is twice the cost and then it
> needs to know how to chain.  It does not seem like a very nice solution.
>

But to what problem, that's my question.

There are thousands of GTK+ apps; I've myself written quite a few
lines of GTK+ code; I've never thought of using features like this.
 
> Note, that my original closure proposal was for a (invalid, dtor, marshal)
> with a data node chain to store the chains to get over the limitation.
> But data node chains take more memory than what he implemented because
> each data node was one pointer, the malloc overhead and data itself.
>

Sure. I'm willing to let the uncommon case be more expensive. 
 
> 
> > However, if you make the changes I've suggested (no closure reuse, one
> > piece of user data) I can't think of such a case. Can you give a
> > concrete example of a case where the library would supply a notifier,
> > and also the user, given my changes (single parent, single user data),
> > which could not be handled by chaining.
> 
> Where do you store the chains?
>

The chaining would be done by the closure user; "user" here would
pretty much mean language binding implementors, I would expect.

I notice throughout your mail you are taking GClosure to be a feature
C app programmers use frequently, no doubt based on the gtk--
experience. However no one has noticed a lack here in the C interface.
At least, I haven't heard anything about it reading all the user
lists. And your mail contained the first use case I've seen for that.

My understanding of GClosure has been that it's a simple abstraction
of connect_full(), in essence. We haven't put any discussion energy
into the end-programmer API. 
 
> Is treating closures like function pointers a worthy goal?  If so
> than what Tim is implementing is on target. Considering we are trying 
> to evaluate the use of all future glib and gtk+ users and not simply the 
> current use of gtk+.  I can't say any of us have the proper foresight
> to know for sure which is the right path.
> 

Thus the discussion. We need to try and pick the right path. Simply
designing the most flexible API does not result in the right path. 
 
> I still do not know if GtkAction will solve every case of reusable 
> closures.  Further, with GtkAction you are going to have to hold
> the closure in the GtkAction.  When you create an object from 
> the Action you will need to connect.  This is a clear case of 
> multiple objects.

You simply connect a handler which invokes the action, the action is
the user data. Works quite nicely; see Bonobo. 
 
> If closure are not copiable, you will need 2 closures to make the 
> connection to one item.  One in the menu to call the GAction, and
> the user supplied one in the GtkAction.  Result all GtkActions double
> the number of closures.  All GtkActions count as one of the multiple
> use cases.
> 

So you get two closures; this is IMO the Right Thing. It keeps the
code functionally separated and gives you nice firewalling and
modularity, with no complexity to worry about. I would consider the
extra memory here worth it. (Though, Tim says the signal system
doesn't always store a closure internally, and in this case the
connection to the menu item "activate" would just be a function
pointer I believe.)

> And I ask what would the user have to do in your system if they
> need to copy a closure?
>

They recreate the closure from the original callback/data pair. There
isn't a need to copy because you can always just chain, as in the
GtkAction case.

I'm taking the closure as a callable object abstracted over
programming language. i.e. it could be a Scheme function, a sigc++
slot, a C callback. Given that, when writing original-language code 
you always have the necessary information to recreate the closure.

You are taking closure as an end-programmer API for C. This is why we
disagree, in essence. 

> In libsigc++, the argument was simply this.  
> 
>    (1) A closure is an object.  
>    (2) A closure should be act like a function pointer.
>    (3) Function pointers can be copied.
>    (4) Closures can be derived.  
> 
>    (R1) You can't copy a closure directly because that would be a 
>         deep copy of a derived object.  ( 2 & 4)
>    (R2) Therefore, it must be a ref counted object.
>    (R3) Thus the process of binding it to an object must not be 
>         destructive, because function pointers can be used 
>         multiple times.  (3)
>    (R4) If it has only one invalid function then the connecting 
>         is destructive.
>    =>  We need multiple parents.
> 
> Are there any assumptions here you feel are invalid?

Yes. 1) I don't think means much; 2) I don't agree with, for the above
reason (it is a language binding internal, so who cares how it acts).

> I think that if closures don't act like function pointers they
> will feel quite restricting and thus be likely require difficult
> kludges for get around the limitation.   Therefore, closures will
> be basically useless to C coders.   
>

Right, I agree with that. I guess I'm arguing that they _should_ be
useless to C coders. ;-)
 
> Then you missed the point.  (in psuedo code)
> 
>   struct MyData {
>     GtkWidget *w1,*w2;
>   };
> 
>   GClosure* make_closure(GtkWidget *w1, GtkWidget *w2)
>     {  
>       MyData *d=my_data_new(w1,w2)'
>       GClosure *c=g_closure_new(&function,d,&g_closure_free_data);
>       g_closure_add_dependency(closure,w1);
>       g_closure_add_dependency(closure,w2);
>     }
> 
> Here we have a fairly simple case in which the data is a structure which
> holds two objects, if either of them are destroyed we want to 
> invalidate the closure.
>

OK, so this is the first concrete use case in C so far posted from you
or Tim. ;-)

I would say: if we just proposed this API out of the blue, with no
reference to language bindings, I wouldn't go for it. I don't see the
point.
 
> But you would be best to discuss this with Tim.  I am not
> a very strong proponent of multiple data objects.

Right, ideally Tim will be participating here of course.
 
> You are completely avoiding specifying what you would do in the 
> specific cases I mentioned.  I really don't care whether you see
> them as common or not.  If they are realistically possible and
> your system doesn't cover them, then I want to know how you intend
> to cover them. 

I _don't_ intend to cover the cases, that's what it comes down to. 
I'm proposing that GTK+ punt this issue, at least for now.

(Note that the simpler API is a subset of the complex one; especially
if we were to make closure an opaque object we could move to the
complex system in future versions. But, I don't expect anyone to go
for that.)
 
> The problem I see with chaining without copying or cloning is that
> you end up with a stew of proxies all of which you must handle 
> dependencies between.  
>

Only if multiple code sections add user data. If they don't, then 
you simply have one proxy from one code section, chaining to user data
that section has control over. 
 
> This is a reasonably large pool of disjoint reasons.
> Because they wanted to copy signals to make their code simpler.  
> Because C++ STL requires that objects in lists be copiable.  Because
> they needed to transfer ownership of a closure from one place to
> another in an event queue.  Because it would allow simpler binding
> of keys, menus, and buttons in a XML builder for gtk--. 
>

OK. None of that applies to C that I can see.

> These are very fuzzy arguments.  Maintainence time, implementation
> complexity, etc are not quantitative.

And? They are still pretty important.

> This system is very similar to the libsigc++ in this regard.
> I think the idea is that this closure system is to be a largely
> closed system.  It is extendable by deriving but for all C users
> it should be a black box.   
>

I don't see black box here. I see a different interface. We can have:

g_closure_new (callback, data, dnotify, inotify, preinvoke, postinvoke);
g_closure_set_dnotify();
g_closure_set_inotify();
etc.

or:

g_closure_new (callback);
g_closure_add_dnotify ();
g_closure_remove_dnotify();
g_closure_add_inotify ();
g_closure_remove_inotify();
etc. 

which is a pretty different interface. And as I've said, I haven't
seen any discussion of interface, or C use-cases. I've only seen "well
we need to handle this that and the other cases."

I think the right approach is to outline the API, then outline the
implementation. What surprised Owen and I, I'm guessing, is that we
expected simply an abstraction of connect_full() so that language
bindings would be able to uniformly handle foreach(), signal
connections, main loop, etc. But the eventual API is apparently
targetted to a general-purpose C-language programming feature, which I
at least don't see the motivation for. What is the problem or user
request it addresses? What's the benefit of the complexity?

> assertion is flawed.  You are saying no one has yet requested
> closures be copiable when closures don't exist.  
>

Let me clarify: I'm saying no one has requested an abstraction of C
function/user_data pairs. I haven't seen the motivation for _using
closures in C applications at all_.

Havoc




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