CMake语法—宏和函数(macro vs function)
CMake语法—宏和函数(macro vs function)
1 宏macro定义与应用
macro(<name> [<arg1> ...]) <commands> endmacro()
- macro:宏关键字
- name:宏名称
- arg1:宏参数
宏的定义与使用方式与函数相同,可参考随笔进行简单理解。本文侧重对比宏与函数区别。
2 宏与函数区别
2.1 示例代码结构
-
learn_cmake:为根目录
-
build:为CMake配置输出目录(在此例中即生成sln解决方案的地方)
-
CMakeLists.txt:CMake脚本
-
cmake_config.bat:执行CMake配置过程的脚本(双击直接运行),公用代码如下:
@echo off set currentDir=%~dp0 set buildDir=%currentDir% set cmakeOutputDir=%currentDir%\build cmake -S %buildDir% -B %cmakeOutputDir% -G"Visual Studio 16 2019" -T v140 -A x64 pause
2.2 区别1:函数会产生新作用域;宏是把执行代码替换到调用位置
2.2.1 示例代码(CMakeLists.txt)
cmake_minimum_required(VERSION 3.18) # 设置工程名称 set(PROJECT_NAME KAIZEN) # 设置工程版本号 set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义 project(${PROJECT_NAME} LANGUAGES CXX C VERSION ${PROJECT_VERSION} ) # 打印开始日志 message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION") # 定义函数 function(test_func_argument age) # 打印ARGN参数值 message(STATUS "ARGN: ${ARGN}") # 打印ARGC参数值 message(STATUS "ARGC: ${ARGC}") # 打印ARGV参数值 message(STATUS "ARGV: ${ARGV}") # 打印ARGV0参数值 message(STATUS "ARGV0: ${ARGV0}") # 打印参数个数 list(LENGTH ARGV argv_len) message(STATUS "length of ARGV: ${argv_len}") # 遍历打印各参数值 set(i 0) while(i LESS ${argv_len}) list(GET ARGV ${i} argv_value) message(STATUS "argv${i}:${argv_value}") math(EXPR i "${i} + 1") endwhile() if (ARGV1) # ARGV1 is a true variable message(STATUS "ARGV1: ${ARGV1}") endif() if (DEFINED ARGV2) # ARGV2 is a true variable message(STATUS "ARGV2: ${ARGV2}") endif() if (ARGC GREATER 2) # ARGC is a true variable message(STATUS "ARGC: ${ARGC}") endif() foreach (loop_var IN LISTS ARGN) # ARGN is a true variable message(STATUS "var: ${loop_var}") endforeach() endfunction() # 定义宏 macro(test_macro_argument age) # 打印ARGN参数值 message(STATUS "ARGN: ${ARGN}") # 打印ARGC参数值 message(STATUS "ARGC: ${ARGC}") # 打印ARGV参数值 message(STATUS "ARGV: ${ARGV}") # 打印ARGV0参数值 message(STATUS "ARGV0: ${ARGV0}") # 打印参数个数 list(LENGTH ARGV argv_len) message(STATUS "length of ARGV: ${argv_len}") # 遍历打印各参数值 set(i 0) while(i LESS ${argv_len}) list(GET ARGV ${i} argv_value) message(STATUS "argv${i}:${argv_value}") math(EXPR i "${i} + 1") endwhile() if (ARGV1) # ARGV1 is not a variable message(STATUS "ARGV1: ${ARGV1}") endif() if (DEFINED ARGV2) # ARGV2 is not a variable message(STATUS "ARGV2: ${ARGV2}") else() message(STATUS "not defined ARGV2") endif() if (ARGC GREATER 2) # ARGC is not a variable message(STATUS "ARGC: ${ARGC}") endif() foreach (loop_var IN LISTS ARGN) # ARGN is not a variable message(STATUS "var: ${loop_var}") endforeach() endmacro() test_func_argument(22 33 44) message(STATUS "\n") test_macro_argument(22 33 44) # 打印结束日志 message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
2.2.2 运行结果
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763. -- The CXX compiler identification is MSVC 19.0.24245.0 -- The C compiler identification is MSVC 19.0.24245.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting C compile features -- Detecting C compile features - done -- ########## BEGIN_TEST_MACRO_VS_FUNCTION -- ARGN: 33;44 -- ARGC: 3 -- ARGV: 22;33;44 -- ARGV0: 22 -- length of ARGV: 3 -- argv0:22 -- argv1:33 -- argv2:44 -- ARGV1: 33 -- ARGV2: 44 -- ARGC: 3 -- var: 33 -- var: 44 -- -- ARGN: 33;44 -- ARGC: 3 -- ARGV: 22;33;44 -- ARGV0: 22 -- length of ARGV: 0 -- not defined ARGV2 -- ########## END_TEST_MACRO_VS_FUNCTION -- Configuring done -- Generating done -- Build files have been written to: F:/learn_cmake/build 请按任意键继续. . .
2.2.3 说明
从示例及运行结果可知:与函数相比,在宏中ARGV、ARGV1、ARGV2、ARGC、ARGN都不是真实的变量。
那么,示例程序中第61至67行,输出第31至34行怎么来的呢?宏替换的作用。如果不理解,需要回去恶补一下C语言的宏。
2.3 区别2:函数内可以使用return;宏中不建议使用return
2.3.1 示例代码(CMakeLists.txt)
cmake_minimum_required(VERSION 3.18) # 设置工程名称 set(PROJECT_NAME KAIZEN) # 设置工程版本号 set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义 project(${PROJECT_NAME} LANGUAGES CXX C VERSION ${PROJECT_VERSION} ) # 打印开始日志 message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION") # 定义函数 function(test_func_argument age) # 打印ARGN参数值 message(STATUS "ARGN: ${ARGN}") # 打印ARGC参数值 message(STATUS "ARGC: ${ARGC}") # 打印ARGV参数值 message(STATUS "ARGV: ${ARGV}") # 打印ARGV0参数值 message(STATUS "ARGV0: ${ARGV0}") # 打印参数个数 list(LENGTH ARGV argv_len) message(STATUS "length of ARGV: ${argv_len}") if (argv_len GREATER 3) foreach (loop_var IN LISTS ARGV) # ARGV is a true variable message(STATUS "loop_var: ${loop_var}") endforeach() else() message(STATUS "in func exec return") return() ## 从此退出 endif() message(STATUS "after return") endfunction() # 定义宏 macro(test_macro_argument age) # 打印ARGN参数值 message(STATUS "ARGN: ${ARGN}") # 打印ARGC参数值 message(STATUS "ARGC: ${ARGC}") # 打印ARGV参数值 message(STATUS "ARGV: ${ARGV}") # 打印ARGV0参数值 message(STATUS "ARGV0: ${ARGV0}") # 定义一个变量 set(list_var "${ARGV}") # 打印参数个数 list(LENGTH list_var argv_len) message(STATUS "length of ARGV: ${argv_len}") if (argv_len GREATER 3) foreach (loop_var IN LISTS list_var) # list_var is a true variable message(STATUS "loop_var: ${loop_var}") endforeach() else() message(STATUS "in macro exec return") return() ## 从此退出 endif() message(STATUS "after return") endmacro() test_func_argument(22 44 66 88 100) message(STATUS "\n") test_func_argument(10 11 12) message(STATUS "\n") test_macro_argument(11 33 55 77 99) message(STATUS "\n") message(STATUS "after exec macro with 5 value to continue\n") test_macro_argument(20 21 22) message(STATUS "\n") message(STATUS "after exec macro with 3 value to continue") # 打印结束日志 message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
2.3.2 运行结果
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763. -- The CXX compiler identification is MSVC 19.0.24245.0 -- The C compiler identification is MSVC 19.0.24245.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting C compile features -- Detecting C compile features - done -- ########## BEGIN_TEST_MACRO_VS_FUNCTION -- ARGN: 44;66;88;100 -- ARGC: 5 -- ARGV: 22;44;66;88;100 -- ARGV0: 22 -- length of ARGV: 5 -- loop_var: 22 -- loop_var: 44 -- loop_var: 66 -- loop_var: 88 -- loop_var: 100 -- after return -- -- ARGN: 11;12 -- ARGC: 3 -- ARGV: 10;11;12 -- ARGV0: 10 -- length of ARGV: 3 -- in func exec return -- -- ARGN: 33;55;77;99 -- ARGC: 5 -- ARGV: 11;33;55;77;99 -- ARGV0: 11 -- length of ARGV: 5 -- loop_var: 11 -- loop_var: 33 -- loop_var: 55 -- loop_var: 77 -- loop_var: 99 -- after return -- -- after exec macro with 5 value to continue -- ARGN: 21;22 -- ARGC: 3 -- ARGV: 20;21;22 -- ARGV0: 20 -- length of ARGV: 3 -- in macro exec return -- Configuring done -- Generating done -- Build files have been written to: F:/learn_cmake/build 请按任意键继续. . .
2.3.3 小结
从示例及运行结果可知:与函数相比,如果在宏中执行return命令之后,整个CMake进程会退出执行,不再继续执行其他语句。
因为函数会产生新的作用域,在函数中return只意味着退出函数作用域,父作用域的代码语句会正常执行。
而宏只是等价替换,当调用宏时,相当于把宏中命令语句替换到相应的位置,所以宏中return,也意味着会退出整个进程。
另外,在此示例程序中第57行,我们定义了一个普通变量list_var,主要作用:为了解决宏中没有ARGV的”尴尬“,可自行体会这种用法。
2.4 区别3:在函数中调用宏的精妙
2.4.1 示例代码(CMakeLists.txt)
cmake_minimum_required(VERSION 3.18) # 设置工程名称 set(PROJECT_NAME KAIZEN) # 设置工程版本号 set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义 project(${PROJECT_NAME} LANGUAGES CXX C VERSION ${PROJECT_VERSION} ) # 打印开始日志 message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION") macro(bar) if (DEFINED ARGN) message("defined ARGN") endif() foreach(loop_var IN LISTS ARGN) message(STATUS "loop_var: ${loop_var}") endforeach() endmacro() function(foo) # 打印ARGN参数值 message(STATUS "ARGN: ${ARGN}") # 打印ARGC参数值 message(STATUS "ARGC: ${ARGC}") # 打印ARGV参数值 message(STATUS "ARGV: ${ARGV}") # 打印ARGV0参数值 message(STATUS "ARGV0: ${ARGV0}") ## 调用宏 bar(x y z) endfunction() foo(a b c) # 打印结束日志 message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
2.4.2 运行结果
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763. -- The CXX compiler identification is MSVC 19.0.24245.0 -- The C compiler identification is MSVC 19.0.24245.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting C compile features -- Detecting C compile features - done -- ########## BEGIN_TEST_MACRO_VS_FUNCTION -- ARGN: a;b;c -- ARGC: 3 -- ARGV: a;b;c -- ARGV0: a defined ARGN -- loop_var: a -- loop_var: b -- loop_var: c -- ########## END_TEST_MACRO_VS_FUNCTION -- Configuring done -- Generating done -- Build files have been written to: F:/learn_cmake/build 请按任意键继续. . .
2.4.3 小结
我们强调过很多次,在CMake语法中,函数会产生新的作用域,同时会默认生成新的变量即ARGN、ARGC、ARGV等等。
那么,在函数中调用宏时,仅仅只是把宏的实现语句往函数中拷贝了一份(即宏替换),所以,宏中语句的执行都基础函数变量的基础上。
因此,也就有示例代码23行成立的原因,同时也有了对输出结果第20行的解释或说明。
2.5 区别4:函数中有一些特有的默认变量
2.5.1 示例代码(CMakeLists.txt)
cmake_minimum_required(VERSION 3.18) # 设置工程名称 set(PROJECT_NAME KAIZEN) # 设置工程版本号 set(PROJECT_VERSION "1.0.0.10" CACHE STRING "默认版本号") # 工程定义 project(${PROJECT_NAME} LANGUAGES CXX C VERSION ${PROJECT_VERSION} ) # 打印开始日志 message(STATUS "\n########## BEGIN_TEST_MACRO_VS_FUNCTION") function(test_func_default_var) # 打印CMAKE_CURRENT_FUNCTION参数值 message(STATUS "CMAKE_CURRENT_FUNCTION: ${CMAKE_CURRENT_FUNCTION}") # 打印CMAKE_CURRENT_FUNCTION_LIST_DIR参数值 message(STATUS "CMAKE_CURRENT_FUNCTION_LIST_DIR: ${CMAKE_CURRENT_FUNCTION_LIST_DIR}") # 打印CMAKE_CURRENT_FUNCTION_LIST_FILE参数值 message(STATUS "CMAKE_CURRENT_FUNCTION_LIST_FILE: ${CMAKE_CURRENT_FUNCTION_LIST_FILE}") # 打印CMAKE_CURRENT_FUNCTION_LIST_FINE参数值 message(STATUS "CMAKE_CURRENT_FUNCTION_LIST_LINE: ${CMAKE_CURRENT_FUNCTION_LIST_LINE}") endfunction() test_func_default_var() # 打印结束日志 message(STATUS "########## END_TEST_MACRO_VS_FUNCTION\n")
2.5.2 运行结果
-- Selecting Windows SDK version 10.0.18362.0 to target Windows 10.0.17763. -- The CXX compiler identification is MSVC 19.0.24245.0 -- The C compiler identification is MSVC 19.0.24245.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done -- Check for working CXX compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done -- Detecting C compiler ABI info -- Detecting C compiler ABI info - done -- Check for working C compiler: C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin/amd64/cl.exe - skipped -- Detecting C compile features -- Detecting C compile features - done -- ########## BEGIN_TEST_MACRO_VS_FUNCTION -- CMAKE_CURRENT_FUNCTION: test_func_default_var -- CMAKE_CURRENT_FUNCTION_LIST_DIR: F:/learn_cmake -- CMAKE_CURRENT_FUNCTION_LIST_FILE: F:/learn_cmake/CMakeLists.txt -- CMAKE_CURRENT_FUNCTION_LIST_LINE: 18 -- ########## END_TEST_MACRO_VS_FUNCTION -- Configuring done -- Generating done -- Build files have been written to: F:/learn_cmake/build 请按任意键继续. . .
2.5.3 小结
- CMAKE_CURRENT_FUNCTION:当前函数名称
- CMAKE_CURRENT_FUNCTION_LIST_DIR: 当前函数路径
- CMAKE_CURRENT_FUNCTION_LIST_FILE:当前函数所属文件
- CMAKE_CURRENT_FUNCTION_LIST_LINE:当前函数定义的起始行数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异