当前位置: 首页 > news >正文

【CMake】循环——foreach(),while()

目录

一.foreach

1.1.常用基本示例

1.1.1.示例1

1.1.2.示例2

1.1.3.示例3——嵌套foreach

1.1.4.示例4——遮盖外部变量

1.1.5.示例5——break

1.1.6.示例6——continue

1.1.7.示例7——break和continue的结合

1.2.范围循环 (RANGE)

1.2.1.示例1

1.2.2.示例2

1.2.3.示例3——倒序循环

1.3.列表循环 (IN LISTS/ITEMS)

1.3.1.示例 1——最基础的列表遍历

1.3.2.示例2——IN LISTS 遍历多个列表

 1.3.3.示例3——IN LISTS + ITEMS

1.3.4.示例4——LISTS A 和 ITEMS ${A} 完全等价

1.4.压缩列表循环 (ZIP_LISTS) - 并行迭代

1.4.1.示例1

 1.4.2.示例2

1.4.2.示例3——长短不一

二.while()

2.1.示例1


一.foreach

对列表中的每个值评估一组命令。

基本语法

foreach(<loop_var> <items>)<commands>
endforeach()

其中 <items> 是一个由分号或空格分隔的项目列表。

foreach 和与之匹配的 endforeach 之间的所有命令会被记录下来但不会被立即调用。

一旦评估到 endforeach,记录的命令列表就会为 <items> 中的每个项目调用一次。

在每次迭代开始时,变量 <loop_var> 将被设置为当前项目的值。

<loop_var> 的作用域被限制在循环范围内。有关详细信息,请参见策略 CMP0124

命令 break() 和 continue() 提供了跳出正常控制流的方法。

出于历史遗留原因,endforeach() 命令允许一个可选的 <loop_var> 参数。如果使用,它必须是开头 foreach 命令参数的原样重复。


我们来好好讲讲

foreach(<loop_var> <items>)<commands>
endforeach()

CMake 处理 foreach 循环的过程可以分为两个明确的阶段:录制阶段执行阶段

阶段一:录制阶段 (Parsing/Recording Phase)

当 CMake 的解析器首次遇到 foreach( 这一行时,它会立刻进行以下操作:

  1. 识别循环结构:解析器识别出这是一个循环的开始,并读取循环变量名 <loop_var> 和项目列表 <items>

  2. 解析项目列表:将 <items> 这个字符串,无论是由空格还是分号分隔,解析成一个真正的列表(一个内部数组)。例如,将 "a b c" 或 "a;b;c" 解析成列表 ['a', 'b', 'c']

  3. 进入录制模式:从 foreach 命令的下一行开始,直到遇到与之匹配的 endforeach() 命令,CMake 解析器会逐行读取其中的所有命令,但不会执行它们

  4. 存储命令块:解析器将这些命令(<commands> 里的所有内容)原封不动地存储起来,作为一个等待被多次调用的命令块。同时,它也会记录下循环变量名和之前解析好的项目列表。

这个阶段的核心是:CMake 只是在进行“预习”,它记住了要循环做什么(命令块)、循环变量叫什么、以及要对哪些值(项目列表)进行循环。它还没有开始做任何实际的工作。

阶段二:执行阶段 (Execution Phase)

当 CMake 解析器遇到与 foreach 相匹配的 endforeach() 命令时,录制阶段结束,执行阶段立刻开始。此时,CMake 才会真正去运行这个循环。

foreach(<loop_var> <items>)<commands>
endforeach()

对于项目列表中的每一个元素,CMake 都会按顺序执行以下步骤:

  1. 设置循环变量:将当前迭代所处理的列表元素的值,赋予循环变量 <loop_var>。例如,在第一轮迭代中,<loop_var> 的值被设置为列表的第一个元素。

  2. 进入循环作用域:CMake 会创建一个新的变量作用域。在这个作用域内,循环变量 <loop_var> 被创建或赋值。

    • 遮盖(Shadowing):如果在外层作用域已经存在一个同名的变量,在当前循环作用域内,那个外层变量将变得不可访问。所有对 ${<loop_var>} 的引用都指向当前循环迭代所设置的新值。

  3. 执行命令块:CMake 将录制阶段存储起来的那个命令块,从头到尾完整地执行一遍。在执行这些命令时,任何使用 ${<loop_var>} 的地方都会被替换为当前循环变量的值。

  4. 处理控制流命令(可选)

    • 如果在执行命令块时遇到了 continue() 命令,CMake 会立即停止执行当前迭代中剩余的命令,直接跳转到步骤5,开始准备下一次迭代。

    • 如果在执行命令块时遇到了 break() 命令,CMake 会立即停止整个循环,跳转到步骤6,彻底结束整个 foreach 循环,不再进行任何后续迭代。

  5. 结束本次迭代:如果没有遇到 break(),当前迭代正常结束。CMake 会销毁当前循环作用域。伴随着作用域的销毁,当前设置的循环变量 <loop_var> 也随之被销毁。

  6. 循环终止或继续

    • 如果是因为 break() 而终止,循环到此完全结束。

    • 否则,CMake 会检查项目列表中是否还有下一个元素。如果有,则回到步骤1,开始下一次迭代,但注意,每次迭代都会创建一个全新的作用域。如果所有元素都已处理完毕,则整个 foreach 循环结束。

循环结束后

  • 当循环完全结束后,CMake 会继续执行 endforeach() 之后的命令。

  • 作用域恢复:由于循环自身的作用域已经结束,如果在循环之前存在同名的变量,那么现在那个变量的值将会重新变得可见和可访问,就像循环从未发生过一样。循环变量本身在循环外部是不存在的。

1.1.常用基本示例

1.1.1.示例1

 📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachDemo1)set(MY_LIST A B C)foreach(item ${MY_LIST})message(STATUS "当前 item: ${item}")
endforeach()

 接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.1.2.示例2

  📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachDemo1)foreach(color Red Green Blue)message(STATUS "颜色: ${color}")
endforeach()

 接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.1.3.示例3——嵌套foreach

  📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachDemo1)set(ROWS 1 2 3)
set(COLS A B)foreach(r ${ROWS})foreach(c ${COLS})message(STATUS "坐标: ${r}${c}")endforeach()
endforeach()

 接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.1.4.示例4——遮盖外部变量

  📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ShadowDemo)# 外层变量
set(VAR "OuterValue")
message(STATUS "循环前外层 VAR = ${VAR}")# 外层循环
foreach(VAR A B C)# 循环内部访问的是循环变量,不再是外层 VARmessage(STATUS "循环内部 VAR = ${VAR}")
endforeach()# 循环结束后,VAR 恢复为外层值
message(STATUS "循环结束后外层 VAR = ${VAR}")

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.1.5.示例5——break

   📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachBreakDemo)set(MY_LIST 1 2 3 4 5)foreach(item ${MY_LIST})if(item EQUAL 3)message(STATUS "遇到 3,结束整个循环")break()endif()message(STATUS "处理 item: ${item}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

item=3 时,break() 立即停止整个循环,不再进行任何后续迭代。

1.1.6.示例6——continue

    📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachContinueDemo)set(MY_LIST 1 2 3 4 5)foreach(item ${MY_LIST})if(item EQUAL 3)message(STATUS "遇到 3,跳过本次迭代")continue()endif()message(STATUS "处理 item: ${item}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

item=3 时,continue() 让循环直接跳到下一次迭代,后面的命令 (message(STATUS "处理 item: ${item}")) 被跳过。

1.1.7.示例7——break和continue的结合

    📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(SimpleNestedForeachDemo)set(OUTER_LIST 1 2)
set(INNER_LIST a b c)foreach(outer ${OUTER_LIST})message(STATUS "外层循环 outer=${outer}")foreach(inner ${INNER_LIST})# 内层 continueif(inner STREQUAL "b")message(STATUS "  内层循环遇到 b,跳过本次迭代")continue()endif()# 内层 breakif(inner STREQUAL "c")message(STATUS "  内层循环遇到 c,跳出内层循环")break()endif()message(STATUS "  内层循环处理 inner=${inner}")endforeach()
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

和我们之前学的是不是一样啊。

1.2.范围循环 (RANGE)

foreach(<loop_var> RANGE <stop>)

在此变体中,foreach 遍历数字 0, 1, ... 直到(并包括)非负整数 <stop>

foreach(<loop_var> RANGE <start> <stop> [<step>])

在此变体中,foreach 以 <step> 为步长,遍历从 <start> 开始直到最多 <stop> 的数字。

如果未指定 <step>,则步长大小为 1。

三个参数 <start><stop><step> 必须都是非负整数,且 <stop> 不得小于 <start>

否则您将进入未 documented 行为的危险区域,这些行为可能在未来的版本中更改。

1.2.1.示例1

    📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachRangeDemo1)foreach(i RANGE 5)message(STATUS "i = ${i}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.2.2.示例2

     📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachRangeDemo2)foreach(i RANGE 2 10 2)message(STATUS "i = ${i}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

  • 语法:RANGE <start> <stop> <step>

  • 步长为 2,从 2 到 10(包含 10)。

1.2.3.示例3——倒序循环

     📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachRangeDemo3)foreach(i RANGE 5 0 -1)message(STATUS "i = ${i}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

  • 可以用负步长实现倒序循环。

1.3.列表循环 (IN LISTS/ITEMS)

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])

在此变体中,<lists> 是一个由空格或分号分隔的列表值变量(list-valued variables)的列表。foreach 命令遍历每个给定列表中的每个项目。ITEMS 关键字后面的 <items> 的处理方式与第一种 foreach 命令变体相同。形式 LISTS A 和 ITEMS ${A} 是等价的。

这么说大家肯定看不懂,我们来好好讲讲


我们先把这个完整的语法拆开看:

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])<commands>
endforeach()

这里有两个可选的关键部分:LISTS [<lists>] 和 ITEMS [<items>]。你可以只使用其中一个,也可以两个一起使用。

第一部分:LISTS [<lists>]

  • <lists> 是什么? 它是一个或多个列表变量名(List Variable Names)

  • 注意: 这里放的是变量的名字,而不是变量本身的值。你不需要在这里使用 ${}

  • 作用: 告诉 foreach 循环:“请去找到这些名字的变量,把它们里面所有的值都拿出来,作为要遍历的项目。”

举个例子:

假设你有两个变量:

set(MyListA a b c)
set(MyListB 1 2 3)

那么 LISTS MyListA MyListB 的意思就是:“请把变量 MyListA 里的所有值 (abc) 和变量 MyListB 里的所有值 (123) 都合并到一起。”

第二部分:ITEMS [<items>]

  • <items> 是什么? 它是一个或多个直接的值(Direct Values)

  • 作用: 告诉 foreach 循环:“除了从变量里拿值,我还要直接加入这些值。”

接着上面的例子:
ITEMS x y z 的意思就是:“在刚才那些值的基础上,再直接加上 xyz 这三个值。”


组合起来的工作原理

当 LISTS 和 ITEMS 一起使用时,foreach 循环的执行流程如下:

  1. 收集阶段(Collection Phase):在循环开始之前,CMake 会先做一件事:

    • 找到所有在 LISTS 后面指定的变量名(如 MyListAMyListB),获取这些变量的(即它们所代表的列表)。

    • 获取所有在 ITEMS 后面直接指定的值(如 xyz)。

    • 将上述所有值按顺序拼接成一个大的、总的列表

      • 上面例子的最终列表将是:abc123xyz

  2. 执行阶段(Execution Phase)

    • 接下来,循环就变得和最基本的 foreach 循环一模一样了。

    • CMake 会遍历这个刚刚生成的总的列表

    • 在每次迭代中,将列表中的当前值赋给循环变量 <loop_var>,然后执行一次循环体内的命令。

所以,这个复杂的语法最终的目的就是为了生成一个用于遍历的总列表


关键理解:等价形式

文档里说了一句非常关键的话:LISTS A 和 ITEMS ${A} 是等价的

我们来拆解一下这句话:

  • LISTS A:意思是“去找到名为 A 的变量,把它里面的值都拿来”。

  • ITEMS ${A}:意思是“直接把这些值拿来”。这里 ${A} 已经被展开(Expand) 了。如果 A 的值是 a b c,那么 ITEMS ${A} 就完全等价于 ITEMS a b c

因此,这两种写法最终会导致循环遍历完全相同的项目序列。 它们只是达到目的的两种不同路径而已。

写法

含义

foreach(var IN LISTS MyList)

“喂,循环,你去把变量 MyList 里的东西遍历一下。”

foreach(var IN ITEMS ${MyList})

“喂,循环,我直接把 MyList 里的东西给你,你遍历一下。”

通常,使用 LISTS 是更推荐的方式,因为它更直接,不易出错。

1.3.1.示例 1——最基础的列表遍历

     📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachListsDemo1)set(MY_LIST a b c)foreach(item IN LISTS MY_LIST)message(STATUS "当前 item = ${item}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.3.2.示例2——IN LISTS 遍历多个列表

      📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachListsDemo3)set(LIST1 red green)
set(LIST2 blue yellow)foreach(color IN LISTS LIST1 LIST2)message(STATUS "颜色: ${color}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

 1.3.3.示例3——IN LISTS + ITEMS

      📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachListsItemsDemo)# 定义几个列表变量
set(LIST1 a b c)
set(LIST2 1 2 3)# foreach 同时使用 LISTS 和 ITEMS
foreach(item IN LISTS LIST1 LIST2 ITEMS x y z)message(STATUS "当前 item = ${item}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.3.4.示例4——LISTS AITEMS ${A} 完全等价

       📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)
project(ForeachListsVsItemsDemo)# 定义一个列表变量
set(MY_LIST a b c)# 使用 LISTS
message(STATUS "使用 LISTS:")
foreach(item IN LISTS MY_LIST)message(STATUS "  item = ${item}")
endforeach()# 使用 ITEMS
message(STATUS "使用 ITEMS:")
foreach(item IN ITEMS ${MY_LIST})message(STATUS "  item = ${item}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.4.压缩列表循环 (ZIP_LISTS) - 并行迭代

foreach(<loop_var>... IN ZIP_LISTS <lists>)

3.17 版本新增。

在此变体中,<lists> 是一个由空格或分号分隔的列表值变量的列表。foreach 命令同时遍历每个列表,并按如下方式设置迭代变量:

  • 如果只给定一个 loop_var,则它会设置一系列 loop_var_N 变量(如 num_0num_1)为相应列表中的当前项目;

  • 如果传递了多个变量名,它们的数量应与列表变量的数量匹配;

  • 如果任何列表较短,则相应的迭代变量在当前迭代中未定义。

我们这么看其实是有点小困难的,我们现在来好好讲讲


好的,这个语法是 foreach 循环中最高级的一个变体,它用于同步遍历多个列表它不是在遍历一个由所有列表拼接成的大列表,而是像拉链(Zip)一样,将多个列表的对应元素配对处理。我们把它彻底讲清楚。

核心思想:并行遍历

想象你有两个列表:

  • Names = Alice Bob Charlie

  • IDs = 1001 1002 1003

普通的 foreach 会先遍历完 Names 的所有元素,再遍历 IDs 的所有元素。而 ZIP_LISTS 的目的是让你在同一次迭代中同时访问到这两个列表的第一个元素Alice 和 1001),下一次迭代访问第二个元素Bob 和 1002),以此类推。

它得名于像“拉链”一样将两个列表咬合在一起。

Names:  Alice    Bob    Charlie|       |        |
IDs:    1001    1002     1003

语法分解和两种模式

foreach(<loop_var>... IN ZIP_LISTS <lists>)

这里的 <lists> 和之前一样,是一个或多个列表变量名(不需要 ${})。

这个语法根据你提供的 <loop_var> 的数量,有两种截然不同的工作模式:

模式一:单个循环变量(自动索引模式)

语法: foreach(<single_var> IN ZIP_LISTS <lists>)

执行流程:

  1. 录制阶段:CMake 解析器识别该语法结构,记录下需要循环执行的命令块。其中,<lists> 可以包含一个或多个列表变量名,例如 ListA ListB ListC

  2. 执行阶段

    • 循环的总迭代次数由 <lists> 中长度最长的那个列表决定,我们将其长度记为 N

    • 在每一次迭代中(例如第 i 次迭代,i 从 0 开始),CMake 会执行以下操作:

      • 不会直接将一个值赋给变量 <single_var>

      • 取而代之的是,它会自动创建一组新的变量。这组变量的命名规则为:<single_var>_0<single_var>_1<single_var>_2, ...,变量的数量与 <lists> 中提供的列表数量相同。

      • 每个新变量的值有其明确的对应关系:<single_var>_M 的值来自于 <lists> 中第 M+1 个列表的第 i 个元素

        • 具体来说,<single_var>_0 对应第一个列表的第 i 个元素。

        • <single_var>_1 对应第二个列表的第 i 个元素。

        • 依此类推,<single_var>_M 对应第 M+1 个列表的第 i 个元素。

举个例子:

set(Names Alice Bob Charlie)
set(IDs 1001 1002 1003)foreach(person IN ZIP_LISTS Names IDs)message("Name: ${person_0}, ID: ${person_1}")
endforeach()

输出:

Name: Alice, ID: 1001
Name: Bob, ID: 1002
Name: Charlie, ID: 1003
  • 在第一次迭代中,CMake 自动创建了 person_0(值为 Alice)和 person_1(值为 1001)。

  • 你通过 person_0 和 person_1 来访问并行列表中的当前元素。

模式二:多个循环变量(一一对应模式)

语法: foreach(<var1> <var2> <var3> ... IN ZIP_LISTS <lists>)

执行流程:

  1. 录制阶段:CMake 解析器会检查关键字 IN ZIP_LISTS 前面的变量名数量。

  2. 执行阶段

    • 循环的迭代次数同样由 <lists> 中最长的列表决定。

    • 在每一次迭代中,CMake 会按顺序将 <lists> 中每个列表的当前元素,直接赋给你提供的循环变量。

      • 第一个列表的当前元素 -> <var1>

      • 第二个列表的当前元素 -> <var2>

      • 第三个列表的当前元素 -> <var3>

      • ...以此类推。

举个例子(实现和上面同样的功能):

set(Names Alice Bob Charlie)
set(IDs 1001 1002 1003)foreach(name id IN ZIP_LISTS Names IDs)message("Name: ${name}, ID: ${id}")
endforeach()

输出:

Name: Alice, ID: 1001
Name: Bob, ID: 1002
Name: Charlie, ID: 1003
  • 这种模式更直观,你直接为每个列表指定了一个有意义的变量名(nameid),而不是使用自动生成的索引名(person_0person_1)。


处理列表长度不一致的情况

这是 ZIP_LISTS 的一个关键行为。文档指出:“如果任何列表较短,则相应的迭代变量在当前迭代中未定义。”

举个例子:

set(ListA a b c d e) # 最长,有5个元素
set(ListB 1 2 3)     # 较短,只有3个元素
set(ListC X Y)       # 最短,只有2个元素foreach(a_val b_val c_val IN ZIP_LISTS ListA ListB ListC)message("A: ${a_val}, B: ${b_val}, C: ${c_val}")
endforeach()

输出:

A: a, B: 1, C: X    # 第一次迭代,所有变量都有值
A: b, B: 2, C: Y    # 第二次迭代,所有变量都有值
A: c, B: 3, C:      # 第三次迭代!ListC 用完了,所以 ${c_val} 是未定义的(空字符串)
A: d, B: , C:       # 第四次迭代,ListB 和 ListC 都用完了
A: e, B: , C:       # 第五次迭代,只有 ListA 还有值
  • 因为 ListA 最长(5个元素),所以循环会执行 5 次。

  • 当某个列表的元素被取完后,对应那个列表的循环变量将变成“未定义”。在CMake中,引用一个未定义的变量通常会得到一个空字符串(""),这可能会在后续逻辑中导致错误。

  • 因此,在使用 ZIP_LISTS 时,你通常需要确保列表长度一致,或者在循环体内使用 if(DEFINED ...) 来检查变量是否有效,以避免错误。

1.4.1.示例1

       📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(ForeachZipListsDemo)# 定义两个等长的列表
set(NUMBERS 1 2 3)
set(LETTERS A B C)# 使用 ZIP_LISTS 同时遍历
foreach(pair IN ZIP_LISTS NUMBERS LETTERS)message(STATUS "当前 NUMBERS=${pair_0}, LETTERS=${pair_1}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

 1.4.2.示例2

         📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(ForeachZipListsDemo)set(NUMBERS 1 2 3 4)
set(LETTERS A B C D)
set(COLORS Red Green Blue Yellow)foreach(num letter color IN ZIP_LISTS NUMBERS LETTERS COLORS)message(STATUS "num=${num}, letter=${letter}, color=${color}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

1.4.2.示例3——长短不一

        📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.17)
project(ForeachZipListsDemo)set(NUMBERS 1 2 3 4)
set(LETTERS A B C)
set(COLORS Red Green)foreach(num letter color IN ZIP_LISTS NUMBERS LETTERS COLORS)message(STATUS "num=${num}, letter=${letter}, color=${color}")
endforeach()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果

二.while()

大家可以去官网看看:while — CMake 4.1.1 Documentation

当条件为真时,评估一组命令

while(<condition>)<commands>
endwhile()

在 while 和与之匹配的 endwhile() 之间的所有命令会被记录下来但不会立即调用。一旦评估到 endwhile(),只要 <condition> 条件为真,就会调用所记录的命令列表。

<condition> 条件的语法和评估逻辑与 if() 命令中详细描述的完全相同。

break() 和 continue() 命令提供了跳出正常控制流的方法。

出于历史遗留原因,endwhile() 命令允许一个可选的 <condition> 参数。如果使用该参数,它必须与开头 while 命令的参数完全一致。


这个while循环大家都不陌生吧,我们现在就只需要知道

<condition> 条件的语法和评估逻辑与 if() 命令中详细描述的完全相同。

1.常量表达式(Constant Expressions)

当 if() 命令中的参数是一个常量(即一个被直接写出的值,而非变量引用)时,其真假判断遵循明确的规则:

  • 评估为真(True)的常量:包括 1ONYESTRUEY 以及任何非零的数字(无论是整数如 2-1,还是浮点数如 0.53.14)。这些值均表示逻辑真。

  • 评估为假(False)的常量:包括 0OFFNOFALSENIGNORENOTFOUND空字符串"")以及任何以后缀 -NOTFOUND 结尾的字符串(例如 MyPackage-NOTFOUND)。这些值均表示逻辑假。

一个重要特征是这些命名的布尔常量(如 ON/OFF, YES/NO)是不区分大小写的。因此,onOnONyesYesYES 都会被视为真。

如果所提供的参数不符合上述任何特定常量,CMake 则不会将其视为常量,而是会尝试将其解释为一个变量名称或一个字符串。

2.变量形式(if(<variable>))

如果参数是一个未被引号包围的名称,CMake 会将其视为一个变量名,并执行变量扩展。其判断逻辑为:

  • :当且仅当该变量已经被定义,并且其扩展后的值不属于上面1.1节里面说的“假常量”列表。

  • :在以下两种情况下评估为假:

    1. 该变量已被定义,但其值是一个“假常量”。包括 0OFFNOFALSENIGNORENOTFOUND空字符串"")以及任何以后缀 -NOTFOUND 结尾的字符串(例如 MyPackage-NOTFOUND)。这些值均表示逻辑假。

    2. 该变量未被定义。访问一个不存在的变量会将其值视为空字符串(一个假常量),因此同样返回假。

重要限制

宏参数(Macro Parameters):在宏(macro)内部,宏的参数不是传统意义上的 CMake 变量,因此不能以 if(<parameter>) 的形式进行测试。应使用 if(${parameter}) 进行变量扩展后再判断其值。

环境变量(Environment Variables):不能使用 if(ENV{some_var}) 来测试环境变量是否存在或为其值是否为真。ENV{some_var} 是一个特殊的引用语法,整个表达式 ENV{...} 本身不会被识别为一个变量名。测试环境变量的正确方法是先将其值赋给一个普通变量,例如 set(MY_ENV_VAR "$ENV{SOME_VAR}"),然后再使用 if(MY_ENV_VAR) 进行判断。

3.字符串形式(if(<string>))

带引号的字符串在 CMake 中通常始终被视为“假”,除非满足以下任一条件:

  • 字符串本身是公认的真常量之一,包括:1ONYESTRUEY,以及任何非零数值(整数如 2-1,或浮点数如 0.53.14)。这些值在逻辑判断中均代表“真”。

  • 另一种情况则与策略 CMP0054 有关,具体说明如下:

我们来仔细讲解

  • 在 CMake 4.0 之前的版本中,如果策略 CMP0054 未被显式设置为 NEW(即处于 OLD 行为下),当引号内的字符串内容恰好是一个已定义变量的名称时,CMake 会执行一种被称为“未引号参数自动变量扩展”的行为。这意味着 if("${some_var}") 和 if(some_var) 可能会因为变量 some_var 的值不同而产生不同的结果,而 if("some_var") 甚至可能被解释为去查询一个名为 some_var 的变量。这种行为非常容易导致混淆和错误。

  • 自 CMake 4.0 起,策略 CMP0054 默认变为 NEW,此行为被禁止。在 NEW 行为下,引号内的内容永远被视为字符串值,而不会自动被当作变量名再次展开。因此,if("some_text") 在任何情况下都只判断字符串 "some_text" 是否为真常量,而不会去查找一个名为 some_text 的变量。这是推荐的做法,它能带来更清晰、更可预测的逻辑。

2.1.示例1

        📂 项目目录结构

demo/
└── CMakeLists.txt

🔹 demo/CMakeLists.txt

cmake_minimum_required(VERSION 3.18)project(whileDemo)# 1 初始化变量
set(i 1)# 2 当i <= 5 时 就输出i的值
while(i LESS_EQUAL 5)message(STATUS "i = ${i}")math(EXPR i "${i} +1") # i++
endwhile()

  接下来我们就来搭建我们的项目

rm -rf build && mkdir build && cd build && cmake ..

运行结果


文章转载自:

http://6luu5hj8.frcxx.cn
http://2AV2TBex.frcxx.cn
http://0jDlqO1X.frcxx.cn
http://PxhtZxf4.frcxx.cn
http://93jaHTNo.frcxx.cn
http://TKHQ6NUC.frcxx.cn
http://GrbMTba5.frcxx.cn
http://Q1apGojv.frcxx.cn
http://aqLFmeL1.frcxx.cn
http://BcdAr22c.frcxx.cn
http://HkBbBBav.frcxx.cn
http://mh7WBvSh.frcxx.cn
http://Gl5C2cy9.frcxx.cn
http://h8JgetDW.frcxx.cn
http://woyWkGwJ.frcxx.cn
http://PUZ1PPxX.frcxx.cn
http://vDWSJHCa.frcxx.cn
http://2wCtEDBg.frcxx.cn
http://CwiXySrH.frcxx.cn
http://ongDeLdr.frcxx.cn
http://qmemLhSk.frcxx.cn
http://cCWs1Qr0.frcxx.cn
http://42izvWmG.frcxx.cn
http://v0OQoV6a.frcxx.cn
http://FKLfNOzG.frcxx.cn
http://gFM9vini.frcxx.cn
http://N4djLyCh.frcxx.cn
http://iYxGItTg.frcxx.cn
http://WXrWngtr.frcxx.cn
http://9Hiody2d.frcxx.cn
http://www.dtcms.com/a/381808.html

相关文章:

  • 对比Java学习Go——函数、集合和OOP
  • AI时代的内容创作革命:深度解析xiaohongshu-mcp项目的技术创新与实战价值
  • 3-11〔OSCP ◈ 研记〕❘ WEB应用攻击▸存储型XSS攻击
  • 贪心算法应用:配送路径优化问题详解
  • 神经网络稀疏化设计构架中的网络剪枝技术:原理、实践与前沿探索
  • p5.js 绘制 3D 椭球体 ellipsoid
  • Qt中自定义控件的三种实现方式
  • leetcode34(环形链表)
  • Jupyter Notebook 介绍、安装及使用
  • 高并发场景下限流算法实践与性能优化指南
  • 基于stm32的智能井盖系统设计(4G版本)
  • 考研408计算机网络第36题真题解析(2021-2023)
  • 【Linux系统】单例式线程池
  • FreeSWITCH一键打包Docker镜像(源码编译)
  • POI和EasyExcel
  • 力扣-单调栈想法
  • 芯片厂常用的溶液—TMAH全方位介绍
  • Leetcode sql 50 ~5
  • 《大数据之路1》笔记2:数据模型
  • python小项目——学生管理系统
  • 格密码--从FFT到NTT(附源码)
  • HTML中css的基础
  • 软考中级习题与解答——第六章_计算机硬件基础(2)
  • UDP 深度解析:传输层协议核心原理与套接字编程实战
  • MySQL在Ubuntu 20.04 环境下的卸载与安装
  • 相机几何 空间点到像素平面转换
  • 基础算法模板
  • 智能学习辅助系统-部门管理开发
  • 01数据结构-初探动态规划
  • 数据结构 -- 反射、枚举以及lambda表达式