Date: Thu, 3 Jan 2002 10:46:48 +0100 (CET) From: Harti Brandt <brandt@fokus.gmd.de> To: Matthew Dillon <dillon@apollo.backplane.com> Cc: Julian Elischer <julian@elischer.org>, Poul-Henning Kamp <phk@critter.freebsd.dk>, Stephen McKay <mckay@thehub.com.au>, John Baldwin <jhb@FreeBSD.ORG>, <cvs-all@FreeBSD.ORG>, <cvs-committers@FreeBSD.ORG>, Julian Elischer <julian@FreeBSD.ORG>, Greg Lehey <grog@FreeBSD.ORG> Subject: Re: cvs commit: src/share/man/man3 queue.3 Message-ID: <20020103103234.N7957-100000@beagle.fokus.gmd.de> In-Reply-To: <200201030919.g039J8v63350@apollo.backplane.com>
next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, 3 Jan 2002, Matthew Dillon wrote:
MD>
MD>:...
MD>:> There are types of lists/queues which doesn't naturally end with
MD>:> that condition. That is why we have the FOO_EMPTY() clause, although
MD>:> people tend to also rely on
MD>:>
MD>:> if (FOO_FIRST(...) == NULL)
MD>:> /* nothing to do */
MD>:
MD>:Since TAILQ_FIRST== NULL is a termination (it's empty)
MD>:condition (as is TAILQ_NEXT() == NULL)
MD>:it makes PERFECT sense that a completed set of iterations ends up
MD>:with the pointer being NULL.
MD>:
MD>:Sometimes I reckon you are bloody minded just for the sheer fun of it..
MD>
MD> The issue here is to extend an abstraction up to the point where
MD> the compiler can easily optimize it.
MD>
MD> Most of the lists I use are implemented circularly internally,
MD> allowing addhead, addtail, insertbefore, insertafter, and remove
MD> functions to be implemented with no conditionals. In this case
MD> the terminator is a pointer back to the list head.
MD>
MD> Now consider a MYLIST_EMPTY() macro verses a MYLIST_GETHEAD()
MD> macro. MYLIST_GETHEAD() would have to check for the terminating
MD> condition (a conditional) and return NULL if it's true and the head
MD> node if it is false. MYLIST_EMPTY() simply checks the terminating
MD> condition and returns a boolean. Now consider this code:
MD>
MD>
MD> #define MYLIST_GETHEAD(list) ((list->head == (node *)list) ? NULL : lsit->head)
MD>
MD> #define MYLIST_EMPTY(list) (list->head == (node *)list)
MD>
MD>
MD> if (MYLIST_GETHEAD(list) == NULL) {
MD> ... empty list ...
MD> }
MD>
MD> vs
MD>
MD> if (MYLIST_EMPTY(list)) {
MD> ... empty list ...
MD> }
MD>
MD> The first code block winds up generating two unoptimizable conditionals
MD> (the C compiler can't optimize the NULL case because it can't assume
MD> that the else case won't return NULL, so it cannot propogate the
MD> conditional to the higher level IF statement). Thus you wind up
MD> with two conditionals.
MD>
MD> The second code block generates one easily optimizable conditional.
MD> The MYLIST_EMPTY() conditional, returning a bool, becomes the
MD> condition used by the IF statement.
MD>
MD> So a XXX_LIST_EMPTY() macro can in fact produce much, much better
MD> code for certain types of lists, and will produce code that is no worse
MD> then XXX_LIST_HEAD() for lists that are naturally NULL terminated.
MD>
MD> This makes XXX_LIST_EMPTY() a useful macro and a useful abstraction
MD> to have.
Well the point is, that we are not talking about MYLIST_*, but about
TAILQ_FIRST. While you are certainly right if you are going to design a
general abstract list interface, we are talking here about a concrete data
structure with know behaviour. man 3 queue already contains a table of
which of the list structures give you which behaviour and the programmer
should choose the structure that fits best his needs. So documenting the
fact that *_FIRST returns NULL for those list structures for which it
does, doesn't break anything and gives the programer even more hints for
his decision. And documenting the fact that *_FOREACH leaves the pointer
NULL does the same. If you are going to implement a new structure which
breaks this, give it another name, but leave the existing structures as
they are.
If you require the programmer to write:
if (LIST_EMPTY(...))
foo;
else {
p = LIST_FIRST(...);
while (p != NULL) {
if (test(p))
break;
p = LIST_NEXT(p, ...)
}
if (p == NULL)
foo;
else
bar;
}
instead of:
LIST_FOREACH(p, ....)
if (test(p))
break;
if (p == NULL)
bar;
else
baz;
they will rather roll there own implementations.
A agree however, that *_FOREACH would better be named *_FOR.
harti
--
harti brandt, http://www.fokus.gmd.de/research/cc/cats/employees/hartmut.brandt/private
brandt@fokus.fhg.de
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe cvs-all" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020103103234.N7957-100000>
