CMake类三元运算符宏编写异常,求原因解析与修复方案
错误根源
你的问题主要来自对CMake宏两个核心特性的误解:
宏的参数拆分机制:CMake宏会强制按空格拆分调用参数,即便你用引号包裹含空格的条件,变量展开后引号会被移除,导致条件被拆成多个独立参数,宏无法完整接收整个表达式。比如你调用
ternary(my_var "${x} STREQUAL par1" 1 2),当x为par1时,引号展开消失,参数被拆分为my_var、par1、STREQUAL、par1、1、2,宏仅能获取前四个参数,导致boolean参数值为par1,而非完整条件。if(${boolean})的表达式解析问题:即便宏能完整接收条件,直接用if(${boolean})会把整个表达式当作单一字符串处理,而非CMake的布尔表达式逻辑。例如if("par1 STREQUAL par1")只会判断字符串是否非空(结果为真),并非你需要的相等判断逻辑。
修复方案
方案1:改用CMake函数替代宏
函数的参数处理更符合预期,会将带空格的引号参数作为单一整体接收,同时注意函数有独立作用域,设置变量时需添加PARENT_SCOPE让父作用域可见:
cmake_minimum_required(VERSION 3.0) project(macro_test) function(ternary var boolean value1 value2) if(${boolean}) set(${var} ${value1} PARENT_SCOPE) else() set(${var} ${value2} PARENT_SCOPE) endif() endfunction() foreach (x par1 par2) ternary(my_var "${x} STREQUAL par1" 1 2) message(STATUS "x = ${x} my_var = ${my_var}") endforeach()
方案2:改进宏的参数传递(利用ARGN)
若坚持用宏,可通过ARGN接收所有额外参数,调整参数顺序避免空格拆分问题:
cmake_minimum_required(VERSION 3.0) project(macro_test) macro(ternary var value1 value2) if(${ARGN}) set(${var} ${value1}) else() set(${var} ${value2}) endif() endmacro() foreach (x par1 par2) ternary(my_var 1 2 "${x} STREQUAL par1") message(STATUS "x = ${x} my_var = ${my_var}") endforeach()
这里宏的参数顺序改为var、value1、value2,用${ARGN}接收后续完整条件,确保含空格的表达式被完整传递。
方案3:使用CMake原生生成器表达式(3.19+)
若使用CMake 3.19及以上版本,可直接用原生生成器表达式实现三元逻辑,无需自定义宏/函数:
cmake_minimum_required(VERSION 3.19) project(macro_test) foreach (x par1 par2) set(my_var $<IF:$<STREQUAL:${x},par1>,1,2>) message(STATUS "x = ${x} my_var = ${my_var}") endforeach()
生成器表达式是CMake原生支持的语法,无需担心参数传递问题,代码更简洁。
验证结果
以上三种方案运行后都会输出预期结果:
-- x = par1 my_var = 1 -- x = par2 my_var = 2
内容的提问来源于stack exchange,提问作者swineone

