# Wrapping control structures with macros

**URL:** https://discourse.cmake.org/t/wrapping-control-structures-with-macros/15834
**Category:** Code
**Created:** [September 16, 2026, 8:28am UTC](https://discourse.cmake.org/t/wrapping-control-structures-with-macros/15834 "2026-09-16T08:28:27Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![MathiasMagnus](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mathiasmagnus/32/431_2.png) [@MathiasMagnus](https://discourse.cmake.org/u/MathiasMagnus)
#### Post date: [September 16, 2026, 8:28am UTC](https://discourse.cmake.org/t/wrapping-control-structures-with-macros/15834/1 "2026-09-16T08:28:27Z")

</div>

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:

```cmake
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:

```auto
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?

```cmake
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](https://cmake.org/cmake/help/latest/command/macro.html#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.
