I’m implementing the processor of tests defined in JSON which are then turned into CTest definitions. (Whether this processor runs at configure-time or ctest-time doesn’t matter for now.) As such, there’s a non-negligable amount of JSON processing going on. To maintain readability and keep lines of code and complexity at bay, I wrote a few missing JSON utilities, similar in spirit to list(FILTER ...) list(TRANSFORM ...)et al.
My problem is writing the simplest of all is not possible: json_foreach(). A range-based for loop over JSON arrays parsed from some input. Ideally the utility call-site should look like:
string(JSON FOOS GET "${FOOS_JSON}")
json_array_foreach(FOO FOOS)
# do whatever with FOO
endforeach()
Those who know CMake might know the problem coming:
CMake Error at xyz.cmake:46 (endmacro):
[cmake] Flow control statements are not properly nested.
Functions being functions, one does expect flow control to be properly nested in such cases, but why macros? If flow control such as return() and continue() act on the enclosing scope, why must foreach() et al. be subject to a different standard?
macro(json_array_foreach ELEM INPUT)
json_array_index_range(INPUT_RANGE "${${INPUT}}") # another util
foreach(I RANGE ${INPUT_RANGE})
string(JSON ${ELEM} GET "${${INPUT}}" ${I})
endmacro()
But then comes the error message. When not using any wrappers, this is 2 lines of constant noise for every loop over JSON arrays, very easy to mess up dereference count depending on INPUT being a local variable or a function argument.
The Macro vs. Function section states:
A macro is executed as if the macro body were pasted in place of the calling statement.
I would argue that this sentiment should dictate macros behave wrt. flow control in general. Without it a wrapper could do not more than take the loop body as a function/macro and call cmake_language(CALL FUNC) inside, but that renders the call-site having to define the loop body up front and via a different 2 lines of unnecessary script noise (macro()/endmacro()). This isn’t an issue for the FILTER/TRANSFORM-like utilities where user-provided functions are already a thing.