【CMake】CMake入门(二)流程控制 if while foreach 数学表达式 函数定义

本篇文章不是新手入门教学文章,主要是记录笔者个人的学习笔记
参考文章 :

  • CMake中的分支与判断(if语句)
  • CMake中的条件判断if/elseif/else

CMake入门(二)

  • 一、CMake中的逻辑判断
  • 二、 while循环
  • 三、CMake中的foreach循环
  • 四、数学表达式
  • 五、系统信息查询
  • 六、函数定义
    • 1. 函数定义
    • 2. 函数定义会创建一个作用域
    • 3. 参数含关键字的函数
  • 七、宏定义
  • 八、路径操作
  • 九、文件操作

一、CMake中的逻辑判断

与大多数的编程语言一样,CMake中也是通过if语句用于判断条件是否成立。

完整的格式如下:

if(<condition>)<commands>
elseif(<condition>) # 可选,且可重复<commands>
else() # 可选<commands>
endif()

注意要点:

  • 注意:else()是可选的,else后面必须有一对空括号。
  • 条件判断最后要以endif()结尾,endif()的括号内也可以写condition,但是必须与if对应的condition完全一致;
  • CMake中在 if()while()foreach() 等语句内部定义的变量并不会创建一个新的作用域, 所以如果在if()while()foreach() 语句块内定义变量,在这些语句块外部,也同样可以访问语句块内的变量。
  • if/elseif 的特点:条件判断中变量不需要加 ${},cmake会自动尝试根据变量名求值。具体示例可以参考这篇文章 CMake中的分支与判断(if语句)

基础用法

  • 常量判断

    1, ON, YES, TRUE, Y为真, 0, OFF, NO, FALSE, N, IGNORE, NOTFOUND假,不区分大小写。如果不是这些常量,则会被当作变量或字符串对待。

    if(YES) # 把以上值填入括号测试message(STATUS 真)
    else()message(STATUS 假)
    endif()
    
  • 变量判断

    普通变量和环境变量都用这种方式。如果变量值不是为假的常量则为真。值为上述为假的或未定义则为假

    set(VAR_1 hello world)if(VAR_1) # 变量用做判断时不用加${}message(STATUS 真)
    else()message(STATUS 假)
    endif()
    
  • 引号内字符串

    除了引号内为上述为真的值,其他都为假

    if("HELLO")message(STATUS 真)
    else()message(STATUS 假)
    endif()
    
  • 逻辑运算

    if(<cond1> AND <cond2>)
    

    if(<cond1> OR <cond2>)
    

    if(NOT <condition>)
    

     

  • 其他常用判断

    • if(TARGET target-name) 判断一个目标是否存在(由add_executable(), add_library(), add_custom_target()创建)

    • if(DEFINED <name>|CACHE{<name>}|ENV{<name>})判断一个变量是否已定义

    • if(<variable|string> IN_LIST <variable>)判断给定元素是否在列表中。列表中各项可用空格或分号隔开

      set(M_LIST hello;world;and;not)if("hello" IN_LIST M_LIST)message(STATUS 真)
      else()message(STATUS 假)
      endif()
      
    • if(EXISTS path-to-file-or-directory)判断文件或路径是否存在

    • if(<variable|string> MATCHES regex)判断能否匹配上正则

关于CMake中的逻辑判断,更详细的介绍可以参考这篇文章:CMake中的条件判断if/elseif/else

二、 while循环

CMake中的while命令用于在条件为true时评估(evaluate)一组命令,其格式如下:

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

while中的与if命令中的具有相同语法和相同的逻辑进行评估。

  • 命令breakcontinue提供了从正常控制流中退出的方法。

下面是一段代码演示了CMake中的while的使用:

# CMake中的while循环set(i 0)
while (i LESS 10)# 实现自增math(EXPR i "${i} + 1")# 不等于5就打印数据if(NOT i EQUAL 5)message(STATUS "i = ${i}")else()continue()endif() # 大于7 就退出if(i GREATER 7)break()endif()
endwhile()

三、CMake中的foreach循环

CMake中的foreach循环,类似于C++中的范围for, 或者是Python中的for循环,其基本格式如下:

foreach(<loop_var> <item_list>)<commands>
endforeach()
  • loop_var 用来接收列表中每一项的变量
  • item_list需要循环的列表,里面每一项用空格或者分号隔开

可以用continue()结束本次循环,用break()终止循环

  1. 普通迭代遍历
# 打印每一个子项
set(item_list aa bbb ccc dddd)
# 下面这两种写法结果与上面的保持一致
# set(item_list aa;bbb;ccc;dddd)
# set(item_list "aa;bbb;ccc;dddd")
foreach(var ${item_list})message(STATUS ${var})
endforeach()# 结果
-- aa
-- bbb
-- ccc
-- dddd
  • 指定范围进行遍历
    格式如下:
foreach(<loop_var> RANGE <stop>)

从0循环到stop指定的数,可以为负数

# 打印0-5
foreach(NUM RANGE 5)message(STATUS ${NUM})
endforeach()# 结果
-- 0
-- 1
-- 2
-- 3
-- 4
-- 5

指定起始值和结束值以及步长

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

start指定的数循环到stop指定的数,默认步长为1,也可以指定步长。

# 打印0- -10之间的偶数
foreach(NUM RANGE 0 -10 -2)message(STATUS ${NUM})
endforeach()# 结果
-- 0
-- -2
-- -4
-- -6
-- -8
-- -10
  • 遍历多个列表

基本格式如下:

foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]])
  • LISTS后面可以跟一个或多个用分号或空格隔开的列表,会分别循环取出每个列表中的每一项
  • ITEMS后面可以放上多项内容,循环也会取出每一项
# 示例
set(A 0;1;3)
set(B 2 3)
set(C "4 5")
set(D 6;7 8)
set(E "")
foreach(X IN LISTS A B C D E ITEMS ${A})message(STATUS "X=${X}")
endforeach()# 结果
-- X=0
-- X=1
-- X=3
-- X=2
-- X=3
-- X=4 5
-- X=6
-- X=7
-- X=8
-- X=0
-- X=1
-- X=3
  • 一次获取多个结果
foreach(<loop_var>... IN ZIP_LISTS <lists>)
# 示例 使用一个变量进行接收
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)
list(APPEND Chinese 一 二 三 四 五 六)foreach(num IN ZIP_LISTS English Bahasa Chinese)message(STATUS "num_0=${num_0}, num_1=${num_1}, num_2=${num_2}")
endforeach()# 结果
-- num_0=one, num_1=satu, num_2=-- num_0=two, num_1=dua, num_2=-- num_0=three, num_1=tiga, num_2=-- num_0=four, num_1=, num_2=-- num_0=, num_1=, num_2=-- num_0=, num_1=, num_2=
# 使用多个变量进行接收
list(APPEND English one two three four)
list(APPEND Bahasa satu dua tiga)
list(APPEND Chinese 一 二 三 四 五 六)foreach(en ba ch IN ZIP_LISTS English Bahasa Chinese)message(STATUS "en=${en}, ba=${ba}, ch=${ch}")
endforeach()

四、数学表达式

在CMake中要进行数学运算我们不能向其他编程语言一样使用数学符号+ - * /,而是要使用下面的指令,然后在表达式中填写我们要计算的表达式。

math(EXPR <variable> "<expression>" [OUTPUT_FORMAT <format>])
  • EXPR: 关键字
  • variable:运算结果
  • expression:数学表达式
  • format:输出格式,值为DECIMAL表示十进制(默认),HEXADECIMAL为十六进制
# 示例
math(EXPR result "6 * (5 + 7)" OUTPUT_FORMAT DECIMAL)
message(STATUS ${result})# 结果
-- 72

五、系统信息查询

在CMake中我么可以通过下面的指令来查询一些系统信息

cmake_host_system_information(RESULT <variable> QUERY <key> ...)
  • variable存放查询结果
  • key需要查询的信息,可以为多项,空格隔开

其中key可以是下面的参数:

  • OS_NAME 操作系统
  • OS_VERSION 操作系统的版本
  • OS_PLATFORM 操作系统的平台架构(x86 , amd64 , x86_64)
  • OS_RELEASE 操作系统的发布版本(Professional , Educational)
  • PROCESSOR_NAME 处理器的名称
  • PROCESSOR_DESCRIPTION 处理器的描述信息

更多的参数信息可以官方文档

六、函数定义

cmake 中我们也可以定义函数,cmake 提供了 function()命令用于定义一个函数,使用方法如下所示:

1. 函数定义

# 用法
function(<name> [arg1 [arg2 [arg3 ...]]])<commands>...
endfunction(<name>)
  • name函数名
  • arg 参数
# 示例
function(func arg1 arg2)message(STATUS ${arg1})message(STATUS ${arg2})
endfunction()func(1 2)# 结果
-- 1
-- 2

在CMake中对于一个函数的参数,可以传递多于该函数要求的参数个数,但是不能少于。

然后在函数中可以使用下面的内置变量进行访问:

  • ARGC 传入该函数的参数个数。
  • ARGV 传入该函数的参数形成的列表
  • ARGN 未命名的参数形成的列表
  • ARGV<number> 访问ARGV列表中对应下标中的元素
# 示例
function(func arg1 arg2)message(STATUS "arg1=${arg1}")message(STATUS "arg2=${arg2}")message(STATUS "ARGC=${ARGC}")message(STATUS "ARGV=${ARGV}")message(STATUS "ARGN=${ARGN}")message(STATUS "ARGV0=${ARGV0}")message(STATUS "ARGV1=${ARGV1}")
endfunction()func(1 2 3 4 5)# 结果-- arg1=1
-- arg2=2
-- ARGC=5
-- ARGV=1;2;3;4;5
-- ARGN=3;4;5
-- ARGV0=1
-- ARGV1=2

2. 函数定义会创建一个作用域

没有指定了变量的PARENT_SCOPE时,在函数内部修改变量值在外部无法访问。
下面是一个示例,关于PARENT_SCOPE 的使用

set(num 100)
function(func arg1)set(num 11 PARENT_SCOPE)message(STATUS "arg1=${arg1}")
endfunction()func(100)message(STATUS "num=${num}")# 结果
-- arg1=100
-- num=11

3. 参数含关键字的函数

cmake中的一些函数target_link_library中的PUBLIC | PRIVATE | INTERFACE即为关键字,如果需要在自定义函数中使用类似的关键字,需要用到cmake_parse_arguments,其语法格式如下:

cmake_parse_arguments(<prefix> <options> <one_value_keywords> <multi_value_keywords> <args>...)
  • prefix:前缀,可以用prefix_<KEYWORD>的形式访问对应的参数值
  • options:不接收数据的关键字列表
  • one_value_keywords:接收一项数据的关键字列表
  • multi_value_keywords:接收多项数据关键字列表
  • args:函数的参数,可以直接放${ARGV}${ARGN}
# 1. 关键字传参 不接收数据的关键字列表
function(func)cmake_parse_arguments("CPA" "NO_VAL" "" "" ${ARGV})message(STATUS "NO_VAL=${CPA_NO_VAL}")
endfunction()func()
func(NO_VAL)# 结果
-- NO_VAL=FALSE
-- NO_VAL=TRUE
# 2.关键字传参 接收一个数据的关键字列表
function(func)cmake_parse_arguments("CPA" "" "ONE_VAL" "" ${ARGV})message(STATUS "ONE_VAL=${CPA_ONE_VAL}")
endfunction()func(ONE_VAL hello)# 结果
-- ONE_VAL=hello
# 3.关键字传参 接收多个数据的关键字列表
function(func)cmake_parse_arguments("CPA" "" "" "MULTI_VAL" ${ARGV})message(STATUS "MULTI_VAL=${CPA_MULTI_VAL}")
endfunction()func(MULTI_VAL hello world 1 2 3)# 结果
-- MULTI_VAL=hello;world;1;2;3

七、宏定义

CMake中宏指令定义与用法类似函数的定义与用法,区别类似于C语言中宏函数定义与普通函数定义。

基本格式:

macro(<name> [<arg1> ...])<commands>
endmacro()

宏定义中可变参数与关键字参数用法与函数相同。

宏定义与函数不同点:

  • 宏定义不会创建新的使作用域
  • 函数中所有参数、包括ARGCARGV等都是变量,但宏定义中这些参数不是变量,因此不能用类似if(DEFINED ...)的形式来检测宏定义中对应的参数是否已定义
  • 宏定义的调用类似于把宏的内容直接复制到调用的地方
# macro
set(num 100)
macro(macroFun arg)math(EXPR num "${num} - ${arg}")
endmacro()macroFun(20)
message(STATUS "num=${num}")# 结果
-- num=80

由于宏只是进行文本替换,所以我们可以在宏体内直接修改变量的值

八、路径操作

详细介绍及例子见官方文档

CMake中要对路径进行操作可以使用下面的指令,基本格式如下:

  cmake_path(GET <path-var> FILENAME <out-var>)cmake_path(GET <path-var> EXTENSION [LAST_ONLY] <out-var>)cmake_path(GET <path-var> STEM [LAST_ONLY] <out-var>)cmake_path(GET <path-var> PARENT_PATH <out-var>)
  • FILENAME 获取文件名
  • EXTENSION 获取扩展名
  • STEM 获取文件名,不带后缀
  • PARENT_PATH 获取父级路径
# 示例
set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/commands.cmake")
cmake_path(GET path1 FILENAME result1)
cmake_path(GET path1 EXTENSION result2)
cmake_path(GET path1 STEM result3)
cmake_path(GET path1 PARENT_PATH  result4)message(STATUS "path1=${path1}")
message(STATUS "result=${result1}")
message(STATUS "result=${result2}")
message(STATUS "result=${result3}")
message(STATUS "result=${result4}")# 结果
-- path1=D:/Complete/commands.cmake
-- result=commands.cmake
-- result=.cmake
-- result=commands
-- result=D:/Complete

将相对路径转化为一个普通路径

  cmake_path(NORMAL_PATH <path-var> [OUTPUT_VARIABLE <out-var>])
# 示例
set(path1 "${CMAKE_CURRENT_SOURCE_DIR}/../")
cmake_path(NORMAL_PATH path1 OUTPUT_VARIABLE result1)message(STATUS "path1=${path1}")
message(STATUS "result=${result1}")# 结果
-- path1=D:/Complete/../
-- result=D:/

九、文件操作

  1. 文件读取
file(READ <filename> <out-var> [...])
  • filename 要读取的文件名
  • 读取到的内容存放的变量
# 示例
file(READ "License.txt" content)
message(STATUS ${content})# 结果
-- This is the open source License.txt file introduced in
CMake/Tutorial/Step9...
  1. 文件写入
file({WRITE | APPEND} <filename> <content>...)
# 示例
file(WRITE "a.txt" "hello world") # 文件不存在会自动创建

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/842741.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++练级之路】【Lv.22】C++11——右值引用和移动语义

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、右值引用1.1 左值和右值1.2 左值引用和右值引用的范围1.3 左值引用的意义 二、移动语义2.1 移动构造…

AI大模型探索之路-实战篇9:探究Agent智能数据分析平台的架构与功能

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5&#xff1a;探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6&#xff1a;掌握Function Calling的详细流程 AI大模型探索之路-实战篇7…

中断处理过程介绍

概念 中断 中断源 分类 中断处理过程 中断请求 实现器件 中断判优 软件判优 过程 器件实现 程序实现 硬件判优 链路判优 器件实现 控制器判优 中断响应 中断服务 中断返回

GBDT 算法【python,机器学习,算法】

GBDT 即 Gradient Boosting Decision Tree 梯度提升树&#xff0c; 是一种迭代的决策树算法&#xff0c;又叫 MART(Multiple Additive Regression Tree)&#xff0c; 它通过构造一组弱的学习器(树)&#xff0c;然后把多棵决策树的结果累加起来作为最终的预测输出。该算法将决策…

史上最全的Linux常用命令、使用技巧汇总(超全面!简单明了!)

目录 常用Linux命令 --help ls pwd cd touch mkdir rm clear vim cat 基本命令 find 快捷键 小技巧 系统命令 reboot 常用Linux命令 --help 作 用&#xff1a;显示 命令的帮助信息 ls 作 用&…

STM32H750外设之ADC通道选择

目录 概述 1 通道选择功能介绍 2 通道选择&#xff08; SQRx、 JSQRx&#xff09; 2.1 通道复用 2.1.1 通道介绍 2.1.2 通道框图 2.2 转换分组 2.3 内部专用通道 3 通道预选寄存器 (ADCx_PCSEL) 3.1 功能介绍 3.2 预选通道寄存器 概述 本位主要介绍STM32H750外设之…

AI学习指南数学工具篇-凸优化在支持逻辑回归中的应用

AI学习指南数学工具篇-凸优化在支持逻辑回归中的应用 一、引言 在人工智能领域&#xff0c;逻辑回归是一种常见的分类算法&#xff0c;它通过学习样本数据的特征和标签之间的关系&#xff0c;来进行分类预测。而在逻辑回归算法中&#xff0c;凸优化是一种重要的数学工具&…

如何开展人工智能项目呢?

1.分析问题&#xff0c;确定输入和输出 比如&#xff1a;中英翻译&#xff0c;输入&#xff1a; 苹果 输出&#xff1a; apple 确定了输入和输出后&#xff0c;要想办法将输入和输出抽象成一些数字&#xff0c;因为计算机只能为你处理数字。比如说&#xff0c;你输入一朵花&am…

栈 队列

目录 1.1栈的基本概念 1.1.1栈的定义 1.1.2栈的基本操作 1.2栈的顺序存储结构 1.2.1构造原理 1.2.2基本算法 1.3栈的链式存储结构 1.3.1构造原理 1.3.2基本算法 2.1队列的基本概念 2.1.1队列的定义 2.1.2队列的基本运算 2.2队列的顺序存储结构 2.2.1构造原理 2.2.1基…

SAP项目中的国际化团队

参与过国际项目或者管理过国际化的团队的同仁是不是有同样的感受&#xff0c;管理SAP项目中的国际化团队是一项复杂的任务&#xff0c;需要考虑多种因素&#xff0c;包括跨文化沟通、时区管理、项目协调等。今天我就根据我多个国际项目的经验来给大家分享一下如何很好的管理国际…

深刻解析 volatile 关键字和线程本地存储ThreadLocal

1.volatile关键字在Java多线程编程中的重要性 在多线程编程中&#xff0c;volatile关键字扮演着至关重要的角色&#xff0c;它确保了变量在多个线程间的可见性&#xff0c;并且能防止指令重排序&#xff0c;从而达到线程安全的目的。 1.1 保证多线程环境下变量的可见性 在Ja…

CRLF注入漏洞

1.CRLF注入漏洞原理 Nginx会将 $uri进行解码&#xff0c;导致传入%0a%0d即可引入换行符&#xff0c;造成CRLF注入漏洞。 执行xss语句 2.漏洞扩展 CRLF 指的是回车符(CR&#xff0c;ASCII 13&#xff0c;\r&#xff0c;%0d) 和换行符(LF&#xff0c;ASCII 10&#xff0c;\n&am…

Java中的事件驱动编程:增强应用的互动性和响应性

事件驱动编程是一种编程范式&#xff0c;其中程序的流程由外部事件决定&#xff0c;如用户操作、系统消息或其他程序的输入。在Java中&#xff0c;事件驱动编程广泛应用于图形用户界面&#xff08;GUI&#xff09;开发、网络编程和组件交互。本文将探讨Java中的事件驱动编程基础…

FTP协议——LightFTP安装(Linux)

1、简介 LightFTP是一个轻量级的FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;客户端软件。FTP是一种用于在网络上传输文件的标准协议&#xff0c;允许用户通过TCP/IP网络&#xff08;如互联网&#xff09;在计算机之间进行文件传输。 2、步骤…

在ARM开发板上,栈大小设置为2MB(常用设置)里面存放的数据

系列文章目录 在ARM开发板上&#xff0c;栈大小设置为2MB&#xff08;常用设置&#xff09;里面存放的数据 在ARM开发板上&#xff0c;栈大小设置为2MB&#xff08;常用设置&#xff09;里面存放的数据 系列文章目录 在ARM开发板上&#xff0c;栈&#xff08;Stack&#xff09;…

Flutter 中的 LimitedBox 小部件:全面指南

Flutter 中的 LimitedBox 小部件&#xff1a;全面指南 Flutter 是一个功能强大的 UI 框架&#xff0c;它提供了大量的小部件来帮助开发者构建美观且响应式的用户界面。在 Flutter 的布局小部件中&#xff0c;LimitedBox 是一个不太常见但非常有用的组件&#xff0c;它可以用来…

Keras深度学习框架第二十五讲:使用KerasNLP预训练Transformer模型

1、KerasNPL预训练Transformer模型概念 使用KerasNLP来预训练一个Transformer模型涉及多个步骤。由于Keras本身并不直接提供NLP的预训练模型或工具集&#xff0c;我们通常需要结合像TensorFlow Hub、Hugging Face的Transformers库或自定义的Keras层来实现。 以下是一个简化的…

Thingsboard规则链:Message Type Filter节点详解

一、Message Type Filter节点概述 二、具体作用 三、使用教程 四、源码浅析 五、应用场景与案例 智能家居自动化 工业设备监控 智慧城市基础设施管理 六、结语 在物联网&#xff08;IoT&#xff09;领域&#xff0c;数据处理与自动化流程的实现是构建智能系统的关键。作…

创新实训2024.05.28日志:记忆化机制、基于MTPE与CoT技术的混合LLM对话机制

1. 带有记忆的会话 1.1. 查询会话历史记录 在利用大模型自身能力进行对话与解答时&#xff0c;最好对用户当前会话的历史记录进行还原&#xff0c;大模型能够更好地联系上下文进行解答。 在langchain chat chat的chat函数中&#xff0c;通过实现langchain框架提供的ChatMemo…

【设计模式】创建型-工厂方法模式

前言 工厂方法模式是一种经典的创建型设计模式&#xff0c;它提供了一种灵活的方式来创建对象实例。通过本文&#xff0c;我们将深入探讨工厂方法模式的概念、结构和应用。 一、什么是工厂方法模式 工厂方法模式是一种创建型设计模式&#xff0c;旨在解决对象的创建过程和客…