6. 列表操作:list()命令的增删改查、排序、反转、去重等操作

各位同学,今天我们来聊聊 CMake 里一个非常实用的命令——list()

说实话,我刚接触 CMake 那会儿,觉得变量就是个存字符串的东西。后来项目越来越复杂,需要管理一堆源文件、一堆库路径、一堆编译选项……这时候才发现,没有列表操作,简直寸步难行。

list() 命令,说白了就是 CMake 给咱们提供的“数组工具箱”。它支持增、删、改、查、排序、反转、去重……基本上你能想到的操作,它都有。

好,咱们直接开干。

6.1 列表的创建与基本操作

在 CMake 里,列表其实就是一个用分号 ; 分隔的字符串。比如:

set(MY_LIST a b c d)
# 等价于 set(MY_LIST a;b;c;d)

但直接用 set()list() 命令。

6.1.1 获取列表长度

list(LENGTH MY_LIST len)
message("列表长度: ${len}")

这个 LENGTH 子命令会把长度存到 len 变量里。我在项目中经常用它来判断列表是否为空——如果长度为 0,就跳过某些处理逻辑。

6.1.2 获取列表中的元素

list(GET MY_LIST 0 2 first third)
message("第一个元素: ${first}")
message("第三个元素: ${third}")

注意,索引从 0 开始。GET 后面可以跟多个索引,一次性取出多个元素。

6.1.3 查找元素位置

list(FIND MY_LIST "b" index)
message("b 的位置: ${index}")

如果找不到,index 会被设为 -1。嗯,这个设计很符合直觉。

6.2 增删改操作

这部分是我用得最多的。尤其是动态拼接源文件列表的时候。

6.2.1 追加元素

list(APPEND MY_LIST "e" "f")
# MY_LIST 现在变成 a;b;c;d;e;f

APPEND 是在列表末尾添加元素。如果你想把元素插入到开头,用 PREPEND(CMake 3.15+ 支持)。

6.2.2 插入元素到指定位置

list(INSERT MY_LIST 2 "x")
# 在索引 2 的位置插入 x,MY_LIST 变成 a;b;x;c;d;e;f

我曾经在重构一个大型项目时,需要把新加的源文件插入到列表中间某个位置,INSERT 帮了大忙。

6.2.3 删除元素

list(REMOVE_ITEM MY_LIST "b")
# 删除所有值为 "b" 的元素

list(REMOVE_AT MY_LIST 0 2)
# 删除索引 0 和 2 的元素

list(REMOVE_DUPLICATES MY_LIST)
# 去重

这里有个坑:REMOVE_ITEM 是按值删除,REMOVE_AT 是按索引删除。别搞混了。我曾经在代码里写错了,结果删了一堆不该删的东西……嗯,调试了半天。

6.2.4 修改元素

list(POP_FRONT MY_LIST)
# 弹出第一个元素

list(POP_BACK MY_LIST)
# 弹出最后一个元素

这两个命令会把弹出的值存到指定变量里,比如 list(POP_FRONT MY_LIST head)head 就是被弹出的元素。

小技巧:如果你只是想获取第一个或最后一个元素,而不想删除它,可以用 list(GET ...) 配合索引 0 或 -1。

6.3 排序、反转与去重

这三个操作,说白了就是“整理数据”。

6.3.1 排序

list(SORT MY_LIST)
# 默认按字母顺序升序排列

list(SORT MY_LIST COMPARE STRING)
# 明确指定按字符串比较

list(SORT MY_LIST COMPARE FILE_BASENAME)
# 按文件名排序(去掉路径前缀)

排序时还可以指定 CASE(大小写敏感)和 ORDER(升序/降序)。比如:

list(SORT MY_LIST ORDER DESCENDING)
# 降序排列

我个人习惯在生成源文件列表后,先排个序,这样编译输出也更有规律。

6.3.2 反转

list(REVERSE MY_LIST)
# 把列表顺序完全颠倒过来

这个命令很简单,但很实用。比如你有一个按时间顺序排列的日志文件列表,想从最新的开始处理,反转一下就行。

6.3.3 去重

list(REMOVE_DUPLICATES MY_LIST)
# 删除重复元素,保留第一次出现的那个

去重操作会改变列表顺序。如果你需要保持原有顺序,建议先去重再排序。

注意:去重是基于字符串完全匹配的。如果两个元素只是看起来相似但实际不同(比如 "abc" 和 " abc"),它们不会被当成重复项。

6.4 列表操作的知识体系

下面这张图总结了 list() 命令的核心操作分类,方便你快速查阅:

list() 命令 查询操作 LENGTH GET FIND 增删改操作 APPEND / PREPEND INSERT REMOVE_ITEM / REMOVE_AT POP_FRONT / POP_BACK 排序与反转 SORT REVERSE 去重操作 REMOVE_DUPLICATES 使用建议 先查后改,避免索引越界 去重和排序会改变列表顺序,注意副作用

6.5 实战:一个完整的列表操作示例

咱们来看一个实际场景。假设你有一个源文件列表,需要做以下处理:

  1. 添加几个新文件
  2. 删除一个废弃的文件
  3. 去重(防止重复添加)
  4. 按字母顺序排序
  5. 输出最终列表
# 初始源文件列表
set(SOURCES main.cpp utils.cpp math.cpp)

# 添加新文件
list(APPEND SOURCES network.cpp database.cpp)

# 删除废弃文件
list(REMOVE_ITEM SOURCES old_utils.cpp)

# 去重
list(REMOVE_DUPLICATES SOURCES)

# 排序
list(SORT SOURCES)

# 输出结果
message("最终源文件列表: ${SOURCES}")

这段代码在实际项目中很常见。我一般会在 CMakeLists.txt 的开头定义好源文件列表,然后用 list() 命令动态调整,最后传给 add_executable()add_library()

核心要点:
  • list() 是 CMake 中操作列表的唯一官方工具
  • 增删改查、排序、反转、去重,一应俱全
  • 注意索引从 0 开始,REMOVE_ITEMREMOVE_AT 的区别
  • 排序和去重会改变列表顺序,使用时要注意副作用

好了,关于 list() 命令就讲到这里。你想想看,如果没有这个命令,咱们得用一堆 set() 和字符串拼接来模拟列表操作,那得多痛苦?

下一节咱们会聊另一个很实用的命令——foreach() 循环。到时候你会看到,list()foreach() 配合起来,威力更大。


公众号:蓝海资料掘金营,微信deep3321