Skip site navigation (1)Skip section navigation (2)
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>