git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] for_each_string_list_item(): behave correctly for empty list

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Sep 19, 2017, 06:51 UTC
Message-ID
<dab2d555-7e09-4eb3-19b8-cab085626bbe@alum.mit.edu>
In-Reply-To
<CAGZ79kYXDhcVXd2C-x6e=o7jYdKqV22DY45c7E2TeuhKLfn26w@mail.gmail.com>
On 09/19/2017 02:08 AM, Stefan Beller wrote:
Show 28 quoted lines
>> I am hoping that this last one is not allowed and we can use the
>> "same condition is checked every time we loop" version that hides
>> the uglyness inside the macro.
> 
> By which you are referring to Jonathans solution posted.
> Maybe we can combine the two solutions (checking for thelist
> to not be NULL once, by Jonathan) and using an outer structure
> (SZEDERs solution) by replacing the condition by a for loop,
> roughly (untested):
> 
> #define for_each_string_list_item(item,list) \
> -       for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
> +    for (; list; list = NULL)
> +        for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
> 
> as that would not mingle with any dangling else clause.
> It is also just one statement, such that
> 
>     if (bla)
>       for_each_string_list_item {
>         baz(item);
>       }
>     else
>       foo;
> 
> still works.
> 
> Are there downsides to this combined approach?

On the plus side, it's pleasantly devious; I wouldn't have thought of using a `for` loop for the initial test. But it doesn't work as written, because (1) we don't need to guard against `list` being NULL, but rather `list->items`; and (2) we don't have the liberty to set `list = NULL` (or `list->items = NULL`, because `list` is owned by the caller and we shouldn't modify it.

The following is a bit closer:
#define for_each_string_list_item(item,list) \
	for (item = (list)->items; item; item = NULL) \
        	for (; item < (list)->items + (list)->nr; ++item)
But I think that also fails, because a callsite that does
	for_each_string_list_item(myitem, mylist)
		if (myitem.util)
			break;

would expect that `myitem` is still set after breaking out of the loop, whereas the outer `for` loop would reset it to NULL.

If `break` were an expression we could do something like
#define for_each_string_list_item(item,list) \
	for (item = (list)->items; item; break) \
        	for (; item < (list)->items + (list)->nr; ++item)

So I think we're still left with the suggestions of Jonathan or Gábor. Or the bigger change of initializing `string_list::items` to point at an empty sentinal array (similar to `strbuf_slopbuf`) rather than NULL. Personally, I think that Jonathan's approach makes the most sense, unless somebody wants to jump in an implement a `string_list_slopbuf`.

By the way, I wonder if any open-coded loops over `string_lists` make the same mistake as the macro?

Michael
Previous: Stefan BellerNext: SZEDER Gábor
Message 27 of 29 in “for_each_string_list_item(): behave correctly for empty list”
  1. for_each_string_list_item(): behave correctly for empty listMichael Haggerty, Sep 15, 2017
  2. Jonathan NiederSep 15, 2017
  3. Michael HaggertySep 16, 2017
  4. SZEDER GáborSep 16, 2017
  5. Michael HaggertySep 17, 2017
  6. Kaartic SivaraamSep 19, 2017
  7. Junio C HamanoSep 20, 2017
  8. Jonathan NiederSep 20, 2017
  9. Junio C HamanoSep 20, 2017
  10. Jonathan NiederSep 20, 2017
  11. Junio C HamanoSep 20, 2017
  12. for_each_string_list_item: avoid undefined behavior for empty listJonathan Nieder, Sep 20, 2017
  13. Junio C HamanoSep 20, 2017
  14. Michael HaggertySep 20, 2017
  15. Kaartic SivaraamSep 20, 2017
  16. doc: camelCase the config variables to improve readabilityKaartic Sivaraam, Sep 20, 2017
  17. Andreas SchwabSep 20, 2017
  18. Jonathan NiederSep 20, 2017
  19. Andreas SchwabSep 20, 2017
  20. Junio C HamanoSep 21, 2017
  21. Andreas SchwabSep 21, 2017
  22. Kaartic SivaraamSep 20, 2017
  23. Junio C HamanoSep 17, 2017
  24. Michael HaggertySep 17, 2017
  25. Junio C HamanoSep 18, 2017
  26. Stefan BellerSep 19, 2017
  27. Michael HaggertySep 19, 2017
  28. SZEDER GáborSep 19, 2017
  29. SZEDER GáborSep 19, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.