RE: TnyAccountTreeModel is now a TnyListIface



Hi Philip, 

>You are allowed to change the TnyIteratorIface to have a 
>_is_end property (tny_iterator_iface_is_end). You will have to 
>implement it for all current iterators (I think there's four 
>or five of them).
>
>That way, your sample pseudo code would work like this:
>
>TnyIteratorIface *iter = tny_list_iface_create_iterator 
>(list); while (!tny_iterator_iface_is_end (iter)) {
>	TnyMyType *instance = tny_iterator_iface_next (iter); } 
>g_object_unref (G_OBJECT (iter));
>
>The problem with "is_end", however, is that I only store the "first"
>node in the list instances and the "current" in the iterator instances.
>Knowing whether there's a next node is simply done by checking whether
>current->next isn't NULL.
>
>Checking whether current->next equals g_list_last (first) 
>takes a lot longer to check. So storing last would be 
>interesting. But when appending, removing and prepending ... 
>that last would have to be updated. And that would make 
>appending, prepending and removing slower.
>This is something you don't want (for the message header list 
>model, this would slowdown showing a summary of the headers 
>significantly).
>
>So ... or "is_last" is slow. Or "prepend, append and remove" are.

Well, 'is_last' is actually in the list, while the end element is
not. It's conceptually 'to the right' of the last element, and 
could be implemented by just putting a dummy element there;

Then: is_end simply means cursor->next == NULL

The iterator and list impl of course then need to be aware that
there is one dummy element.

Alternatively, there could be a simple boolean property in the iterator,
which tells if it's 'valid' or something like that, then I could
simply write my loops like

iter = tny_list_iface_create_iterator ( list );
while (tny_iterator_iface_is_valid (iter)) {	
	/* do something */
	tny_iterator_iface_next (iter);
}

now, compare that with the current way you need to write a loop:

iter = tny_list_iface_create_iterator ( list );

if (!tny_iterator_has_first (iter)) {
	/* nothing in the list?! */
	btw, iterator don't _have_ a 'first' -- they're just pointing
      to something in the list. 
} else {
	while (1) {
		/* do something with ither */
		
		if (!tny_iterator_iface_has_next (iter))
			break;

		tny_iterator_iface_next (iter);
	}
}


>Luckily you can decide per list which one is going to be slow! 
>;). And knowing the last if you once calculated the last, 
>doesn't always have to be very expensive.
>
>For example when appending, the last is always the new one. 
>When prepending the last always stays the same. And when 
>removing, you can check whether you removed the last. And if 
>so, the new last is
>last->prev.
>
>So I'm optimistic about the speed YOU will get out if it, Dirk-Jan :D

I will look into this...  hopefully this weekend. But my iterators
would look a bit different; so I guess we should agree about that.

There also a lot of operation which (like n_th), which belong in 
the list iface, not the iterator. A (bidirectional, random access)
iterator has to provide only.

- go-next
- go-previous
- get-item
- points-to-valid 

Also, do we really need to do locking? When will the same iterator
be used in different threads? And would it then not be better to
do the locking *there*, if we really need it?

In general, the new iterator-based API forces me to rewrite my
code into something that is a lot uglier. The GSList's were just
fine. (The "nice" thing about C is that without any changes, my
code still compiles without warnings. It just crashes...)

Now, I like iterators, but let's do it the right way then; I think
the link you used (the first one for google "iterator pattern"!)
is actually quite bad; but at least there they have the "IsDone"
function -- that would make life a lot easier (it's my points-to-valid).

Best wishes,
Dirk.



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