CMake 函数和宏

CMake 函数

CMake 函数定义语法如下, 其中 name 为函数名, <arg1> 为参数名, <commands> 为函数体. 函数定义后, 可以通过 name 调用函数. 函数名允许字母数字下划线, 不区分大小写.

function(name [<arg1> ...])<commands>
endfunction()

如下的样例定义了一个函数fun, 不带任何参数.

function(fun)message("Hello, World!")
endfunction()# 调用函数
fun()
FUN()
Fun()
cmake_language(CALL fun)# 携带了参数, 参数被函数忽略
FUN(A B C)

函数参数

CMake 将参数分为如下几种类型:

  1. 命名参数, 按照携带值的数量进一步分为:
    1. option类型, 不携带任何值, 如果存在则视为真, 不存在视为假. 比如ENABLE_TESTS指定是否编译测试.
    2. single类型, 携带一个值. 比如参数OUTPUT指定一个目标文件.
    3. multi类型, 携带多个值. 比如参数SOURCE指定多个源文件.
  2. 未命名的参数.

CMake 对于每个函数都自动定义了如下三个变量:

  1. ARGC: 函数参数个数.
  2. ARGV: 函数参数列表. 包含命名参数和未命名参数.
  3. ARGN: 只包含未命名参数.

我们先看一下ARGN的使用场景:

function(add_gtest targetName)add_executable(${targetName} ${ARGN})target_link_libraries(${targetName} PRIVATE GTest::gtest)add_test(NAME ${targetName} COMMAND ${targetName})
endfunction()# 使用方式
add_gtest(test1 test1.cpp)
add_gtest(test2 test2.cpp util.cpp)

参数解析

CMake 使用cmake_parse_arguments来解函数参数, 这个函数有两种调用方式

cmake_parse_arguments(<prefix> <options> <one_value_keywords><multi_value_keywords> <args>...)cmake_parse_arguments(PARSE_ARGV <N> <prefix> <options><one_value_keywords> <multi_value_keywords>)

第二种方式是在 3.7 版本引入的, 并且不能再宏中使用. 二者的区别在于PARSE_ARGV指定了参数列表的起始位置, 这在一些嵌套的函数参数传递中有用.

function(fun)set(options ENABLE_A ENABLE_B ENABLE_C)set(single OUTPUT_NAME)set(multi DEPENDS SOURCES)cmake_parse_arguments(arg "${options}" "${single}" "${multi}" ${ARGN})foreach(opt IN LISTS options)if(arg_${opt})message(STATUS "${opt} is set")endif()endforeach()if (arg_OUTPUT_NAME)message(STATUS "OUTPUT_NAME=${arg_OUTPUT_NAME}")endif()foreach(key IN LISTS multi)if (arg_${key})message(STATUS "${key}=${arg_${key}}")endif()endforeach()
endfunction()# 调用函数
fun(ENABLE_AOUTPUT_NAME "output.exe"DEPENDS "lib-a" "lib-b" "lib-c"SOURCES s1.cpp s2.cpp s3.cpp
)

输出:

-- ENABLE_A is set
-- OUTPUT_NAME=output.exe
-- DEPENDS=lib-a;lib-b;lib-c
-- SOURCES=s1.cpp;s2.cpp;s3.cpp

设置返回值

从 CMake 3.25 开始, CMake 支持return语句中设置返回值. 注意此时需要设置 CMake Policy CMP0140NEW.

cmake_minimum_required(VERSION 3.25)
cmake_policy(SET CMP0140 NEW)function(getVal retValName)set(${retValName} "Hello, World!")return (PROPAGATE ${retValName})
endfunction()getVal(ret1)
message(STATUS "ret1=${ret1}") # 输出 -- ret1=Hello, World!

而在以前的版本中, 一般是通过set变量存在于父级的作用域达到返回值目的.

function(getValOld retValueName)set(${retValueName} "Glad to see you" PARENT_SCOPE)
endfunction()getValOld(ret2)
message(STATUS "ret2=${ret2}")

常见错误

  1. 函数重复定义. 当使用 function()macro() 定义一个新命令时, 如果已经存在同名的命令, CMake 有一个未记录的行为: 旧命令会以原名称加下划线的形式继续可用. 无论旧名称是内置命令, 还是自定义函数或宏, 都是如此. 了解这一行为的开发者有时会试图利用它来创建现有命令的包装器,
function(fun)message("call 1")
endfunction()function(fun)message("call 2")_fun()
endfunction()function(fun)message("call 3")_fun()
endfunction()fun()

这个函数将会无限循环, 并最终导致栈溢出.

  1. 第二次定义的时候, _fun指向第一个定义的函数, 此时还是可以正常工作的.
  2. 第三次定义的时候, _fun已经指向了第二个定义的函数, 而第二个定义的函数中又调用了_fun, 因此会无限循环.

CMake 宏

CMake 宏的定义方式与函数定义方式相同, 定义语法与函数定义语法相同.

macro(name [arg1 [arg2 [...]]])# command list...
endmacro()

宏在调用之后就是被粘贴到调用的位置, 宏不会产生一个新的作用域. 跟 C/C++中的#define类似, 其实本质上就是做的文本替换.

专栏目录

  • 快速上手
  • 最佳实践
  • CMake基础: 变量
  • CMake基础: 控制流
  • CMake基础: 函数和宏

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

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

相关文章

【QA】Qt有哪些迭代器模式的应用?

在 Qt/C 中&#xff0c;迭代器模式的设计主要分为 标准 C 风格 和 Qt 框架特有风格&#xff0c;以下结合代码详细说明两种实现方式的关键设计及其应用场景&#xff1a; 一、Qt 框架中的迭代器模式设计 Qt 提供了两种迭代器风格&#xff1a;Java 风格&#xff08;显式迭代器&am…

Mysql表的简单操作

&#x1f3dd;️专栏&#xff1a;Mysql_猫咪-9527的博客-CSDN博客 &#x1f305;主页&#xff1a;猫咪-9527-CSDN博客 “欲穷千里目&#xff0c;更上一层楼。会当凌绝顶&#xff0c;一览众山小。” 目录 3.1 创建表 3.2 查看表结构 3.3 修改表 1. 添加字段 2. 修改字段 3…

【云馨AI-大模型】自动化部署Dify 1.1.2,无需科学上网,Linux环境轻松实现,附Docker离线安装等

Dify介绍 官网&#xff1a;https://dify.ai/zh生成式 AI 应用创新引擎开源的 LLM 应用开发平台。提供从 Agent 构建到 AI workflow 编排、RAG 检索、模型管理等能力&#xff0c;轻松构建和运营生成式 AI 原生应用。 Dify安装脚本 目录创建 mkdir -p /data/yunxinai &&a…

WordPress上传图片时显示“未提供数据”错误

在WordPress中上传图片时显示“未提供数据”的错误&#xff0c;通常是由多种原因引起的&#xff0c;以下是一些常见的问题及其解决方法&#xff1a; 1. 文件权限问题 WordPress需要正确的文件和目录权限才能正常上传图片。如果权限设置不正确&#xff0c;可能会导致无法上传图…

python3面试题20个(python web篇)

更多内容请见: python3案例和总结-专栏介绍和目录 文章目录 1.python asyncio的原理?2.对Flask蓝图(Blueprint)的理解?3.Flask 和 Django 路由映射的区别?4.什么是wsgi,uwsgi,uWSGI?5.Django、Flask、Tornado的对比?6.CORS 和 CSRF的区别?7.Session,Cookie,JWT的理解8.简…

RedisTemplate和RedissonClient适用的场景有什么不同

在 Spring Boot 项目中&#xff0c;RedisTemplate 和 RedissonClient 分别针对不同的使用场景设计&#xff0c;以下是它们的核心区别和适用场景分析&#xff1a; 一、RedisTemplate&#xff08;Spring Data Redis&#xff09; 定位 Spring 官方提供的 Redis 操作工具&#xf…

人脸表情识别系统分享(基于深度学习+OpenCV+PyQt5)

最近终于把毕业大论文忙完了&#xff0c;众所周知硕士大论文需要有三个工作点&#xff0c;表情识别领域的第三个工作点一般是做一个表情识别系统出来&#xff0c;如下图所示。 这里分享一下这个表情识别系统&#xff1a; 采用 深度学习OpenCVPyQt5 构建&#xff0c;主要功能包…

GitHub供应链攻击事件:Coinbase遭袭,218个仓库暴露,CI/CD密钥泄露

此次供应链攻击涉及GitHub Action "tj-actions/changed-files"&#xff0c;最初是针对Coinbase的一个开源项目的高度定向攻击&#xff0c;随后演变为范围更广的威胁。 攻击过程与影响 Palo Alto Networks Unit 42在一份报告中指出&#xff1a;“攻击载荷主要针对其…

Redis 核心源码解析:从设计哲学到企业级应用实践

一、Redis 的核心设计哲学 Redis 的成功源于其 「用内存换时间」 的核心理念&#xff0c;围绕以下三个核心原则构建&#xff1a; 极简主义&#xff1a;单线程模型避免锁竞争&#xff0c;代码保持高度内聚。 性能至上&#xff1a;所有数据常驻内存&#xff0c;网络层采用事件驱…

GZCTF平台搭建及题目上传

前言 我用手里的Ubuntu虚拟机搭建的&#xff0c;大家根据自己的实际情况来吧 安装及部署 首先&#xff0c;你的虚拟机需要有Docker和Docker-Compose&#xff0c;前者可以看我之前的文章&#xff0c;另外一个可以输入下面的命令安装&#xff0c;注意先获取管理员权限&#xff…

Pycharm社区版创建Flask项目详解

一、创建工程项目 二、配置工程目录 新建的空项目下创建目录。 1、新建app.py文件 2、app.py代码如下&#xff1a; from flask import Flask, render_templateapp Flask(__name__)app.route("/") def root():"""主页:return: Index.html"&qu…

CentOS 7 64位安装Docker

以下是在已有的 CentOS 7 64 位虚拟机上安装 Docker 并配置华为镜像源的详细步骤&#xff1a; 1. 备份原有 Yum 源&#xff08;可选&#xff0c;建议操作&#xff09; # 备份原有仓库文件 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backu…

运动仿真——phased.Platform

在雷达仿真过程中&#xff0c;运动仿真的必要性&#xff0c;以及运动仿真可以实现哪些功能&#xff0c;在matlab对应的user guide中已经讲的很清楚了&#xff0c;这里不再赘述。 本文主要介绍phased.Platform的一些“坑”&#xff0c;和典型的用法。 第一坑&#xff1a;系统对…

缓存删除三级补偿方案:延迟队列+消息队列+定时任务兜底

问题背景: 在 Cache-Aside 模式中&#xff0c;更新数据库后删除缓存失败会导致数据不一致。本文提供工业级三级补偿方案&#xff0c;实现最终一致性保障。 整体架构: 更新操作触发 → 一级延迟队列 → 二级消息队列 → 三级定时任务方案实现: 一、第一级补偿&#xff1a;延迟队…

从零开始实现 C++ TinyWebServer 数据库连接池 SqlConnectPool详解

文章目录 数据库连接池是什么&#xff1f;Web Server 中为什么需要数据库连接池&#xff1f;SqlConnectPool 成员变量实现 Init() 函数实现 ClosePool() 函数SqlConnectRAII 类SqlConnectPool 代码SqlConnectPool 测试 从零开始实现 C TinyWebServer 项目总览 项目源码 数据库连…

C++题目

1、内存管理 1.内存模型 栈:在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。 堆&#xff1a;就是那些由new分配的内存块&#xff0c;其释放由程序员控制&#xff08;一个new对应一个delete&#xff09…

天地图InfoWindow插入React自定义组件

截至2025年03月21日天地图的Marker不支持添加Label; 同时Label和Icon是不支持自定义HTMLElement只支持String&#xff1b;目前只有InfoWindow支持自定义HTMLElement; 效果图 React核心api import ReactDOM from react-dom/client const content document.createElement(div);…

Java并发编程面试汇总

Java并发编程 一、 基础概念1. 进程与线程的区别是什么&#xff1f;2. 创建线程的几种方式&#xff1f;3. 线程的生命周期&#xff08;状态&#xff09;有哪些&#xff1f;4. 什么是守护线程&#xff08;Daemon Thread&#xff09;&#xff1f;5. 线程优先级&#xff08;Priori…

【STM32】第一个工程的创建

目录 1、获取 KEIL5 安装包2、开始安装 KEIL52.1、 激活2.2、安装DFP库 3、工程创建4、搭建框架5、开始编写代码 1、获取 KEIL5 安装包 要想获得 KEIL5 的安装包&#xff0c;在百度里面搜索“KEIL5 下载”即可找到很多网友提供的下载文件&#xff0c;或者到 KEIL 的官网下载&a…

动态规划~01背包问题

01背包问题 经典的0 - 1背包问题的解决方案。 二维数组的版本 代码功能概述 0 - 1背包问题指的是有 n 个物品和一个容量为 m 的背包&#xff0c;每个物品有对应的体积 v[i] 和价值 w[i]&#xff0c;需要从这些物品里挑选若干个放入背包&#xff0c;让背包内物品的总价值达到最…