From f1003afdfbd98d4f82daa9aa011c9a2e9fc4e7ba Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Mon, 18 Dec 2023 14:18:16 +0100 Subject: [PATCH v7 1/3] Add macros for looping through a list without needing a ListCell Many usages of the foreach macro only use the ListCell* variable to get the content of the cell. This adds macros that simplify iteration code for that common use case. These macros automatically declare a loop variable of the relevant type in the scope of the loop and store the content of the cell in this variable. --- src/include/nodes/pg_list.h | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index 529a382d284..e73e399f59d 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -452,6 +452,63 @@ for_each_cell_setup(const List *lst, const ListCell *initcell) return r; } +/* + * Convenience macros that loop through a list of pointers/ints/oids/xids + * without needing a "ListCell *". Instead it only needs a name for the loop + * variable (and for foreach_ptr the underlying type of the pointer). This + * loop variable is declared automatically within the scope of the loop. + * + * Unlike with foreach() it's not possible to detect if an early "break" + * occured by checking the value of the loop variable at the end of the loop, + * because it isn't declared outside the loop. If you need this, it's + * recommended to use foreach() instead or manually track if a break occured by + * using a boolean flag variable called e.g. "found". + * + * The caveats for foreach() apply equally here. + */ +#define foreach_ptr(type, var, lst) foreach_internal(type, *, var, lst, lfirst) +#define foreach_int(var, lst) foreach_internal(int, , var, lst, lfirst_int) +#define foreach_oid(var, lst) foreach_internal(Oid, , var, lst, lfirst_oid) +#define foreach_xid(var, lst) foreach_internal(TransactionId, , var, lst, lfirst_xid) + +/* + * The internal implementation of the above macros. Do not use directly. + * + * This macro actually generates two for loops instead of one. The outer loop + * is very much a hack. We really only want a single loop. The only reason the + * outer loop is created is because we want to declare two loop variables of + * different types and the C standard doesn't allow that. So instead we create + * two loops that both declare a variable of a different type. Then we make + * sure the actual loop body is still executed the intended number of times, by + * making sure it only does a single iteration. Any optimizing compiler should + * be able to optimize away the outer loop, since it's trivial to unroll the + * loop body since it's statically known that it only iterates once. + */ +#define foreach_internal(type, pointer, var, lst, func) \ + for (type pointer var = 0, pointer var##__iterate_only_once_outer = (type pointer) 1; \ + var##__iterate_only_once_outer; \ + var##__iterate_only_once_outer = 0) \ + for (ForEachState var##__state = {(lst), 0}; \ + (var##__state.l != NIL && \ + var##__state.i < var##__state.l->length && \ + (var = func(&var##__state.l->elements[var##__state.i]), true)); \ + var##__state.i++) + +/* + * foreach_node - + * The same as foreach_ptr, but when assertions are enabled it verifies that + * the element is of the specified node type (using its nodeTag()). + */ +#define foreach_node(type, var, lst) \ + for (type * var = 0, *var##__outerloop = (type *) 1; \ + var##__outerloop; \ + var##__outerloop = 0) \ + for (ForEachState var##__state = {(lst), 0}; \ + (var##__state.l != NIL && \ + var##__state.i < var##__state.l->length && \ + (var = lfirst_node(type, &var##__state.l->elements[var##__state.i]), true));\ + var##__state.i++) + /* * forboth - * a convenience macro for advancing through two linked lists base-commit: 4908c5872059c409aa647bcde758dfeffe07996e -- 2.34.1