cmake处理参数时的一些问题说明
- 函数传参空格和分号的坑
- 函数转发的坑
- demo
- 函数传参遇到不平衡方括号的坑
函数传参空格和分号的坑
我们在处理函数和宏的时候不过不小心会遇到很多坑例如:
someCommand(a b c)
someCommand(a b c)
因为cmake中使用空格或者分号分隔符所以上面代码等价于:
someCommand(a b;c)
someCommand(a;;;;b;c)
如果我们想要参数的值包含空格和引号,就需要使用引号将其引起来,例如
someCommand(a "b b" c)
someCommand(a "b;b" c)
someCommand(a;"b;b";c)
使用空格和分号作为分隔符还有一点区别,当涉及到变量求值并且参数未使用引号的时候,例如:
set(containsSpace "b b")
set(containsSemiColon "b;b")
someCommand(a ${containsSpace} c)
someCommand(a ${containsSemiColon} c)
实际上someCommand(a ${containsSpace} c)
会传递三个参数,而someCommand(a ${containsSemiColon} c)
会传递四个参数.
测试:
function(someCommand)
message("=======[${ARGC}]========")
message("=======[${ARGV}]========")
endfunction()
set(containsSpace "b b")
set(containsSemiColon "b;b")
someCommand(a ${containsSpace} c)
someCommand(a ${containsSemiColon} c)
function(someCommand)
message("=======[${ARGC}]========")
message("=======[${ARGV}]========")
message("xxxxxxxxxxxxxxxxxxxxxxxxx")
endfunction()
mesage("=====================")
set(empty "")
set(space " ")
set(semicolon ";")
set(semiSpace "; ")
set(spaceSemi " ;")
set(spaceSemiSpace " ; ")
set(spaceSemiSemi " ;;")
set(semiSemiSpace ";; ")
set(spcaeSemiSemiSpcae " ;; ")
someCommand(${empty})
someCommand(${space})
someCommand(${semicolon})
someCommand(${semiSpace})
someCommand(${spaceSemi})
someCommand(${spaceSemiSpace})
someCommand(${spaceSemiSemi})
someCommand(${semiSemiSpace})
someCommand(${spcaeSemiSemiSpcae})
通过上面的例子可以得出:
- 当参数时变量的求值的结果的时候,空格不能被抛弃,也不会充当分隔符.
- 当参数没有引号的时候,其值开头和结尾的一个或者多个分号会被丢弃.
- 当参数没有加引号的时候,其值中间的连续分号会被合并成一个分号.
通常如果我们传递的参数如果是一个变量求值的结果,那建议使用引号,这样可以避免大多数的困扰,但是并不总是这样的,也有明确需要参数不使用引号的情况.
例如:
关键字参数
function(func)
set(noValues ENABLE_A ENABLE_B)
set(singleVaues FORMAT ARCH)
set(multiValues SOURCE IMAGES)
cmake_parse_arguments(
ARG
"${noValues}" "${singleVaues}" "${multiValues}"
${ARGV}
)
endfunction()
"${noValues}" "${singleVaues}" "${multiValues}"
这三个参数明确使用了引号,${ARGV}
这个参数明确不使用引号.
func(a "" c)
func("a;b;c" "1;2;3")
对于第一条命令${ARGV}
的求值是:a;;c
,根据上述结论3,因为中间的多个分号会被合并成一个,所以cmake_parse_arguments()
命令实际上只会把a
和c
作为要解析的参数.
对于第二条命令,${ARGV}
的求值是:a;b;c;1;2;3
,本来就是想把a;b;c
当做第一个参数,把1;2;3
当作第二个参数,但是cmake_parse_arguments()
命令实际上会认为传入了6个参数.
上述问题都可以通过避免使用${ARGV}
来解决,也就是使用下面的命令形式:
cmake_parse_arguments(
PARSE_ARGV 0 ARG
"${noValues}" "${singleVaues}" "${multiValues}"
)
这种方式可以是参数完全按照传入的方式保留.
我i们在自己定义函数或者宏的时候,一个相对常见的需求就是围绕现有的命令创建某种包装器.项目可能希望支持一些额外的选项或者删除现有的选项,或者它可能希望在调用之前或之后执行的某些处理.保留参数并不在改变其结构或丢失信息的情况下转发他们可能会非常困难.
函数转发的坑
demo
function(printArgs)
message("ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
endfunction()
printArgs("a;b;c" "d;e;f")
因此传入的承诺书是带引号的,所以ARGC
的值是2,但是ARGV
却是一个包含6个值的列表,原始的参数形式在这里丢失了,如果要做参数转发就会产生错误,例如:
function(printArgs)
message("ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
endfunction()
printArgs("a;b;c" "d;e;f")
function(inner)
message("inner:\n"
"ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
endfunction()
function(outer)
message("outer:\n"
"ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
inner(${ARGN})
endfunction()
message("=====================")
outer("a;b;c" "d;e;f")
解决上面问题其实使用cmake_parse_arguments()
命令的PARSE_ARGV
形式:
function(inner)
message("inner:\n"
"ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
endfunction()
function(outer)
message("outer:\n"
"ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
cmake_parse_arguments(PARSE_ARGV 0 FWD "" "" "")
message("FWD_UNPARSED_ARGUMENTS = ${FWD_UNPARSED_ARGUMENTS}")
inner(${FWD_UNPARSED_ARGUMENTS})
endfunction()
message("=====================")
outer("a;b;c" "d;e;f")
如果有空的双引号就会出错:
outer("a;b;c" "d;e;f")
因为没有关键字需要解析,所以所有的参数都被保存到FWD_UNPARSED_ARGUMENTS
中,这种方式转发原始参数就不会丢失参数的原始形式.
为了避免空参数丢失,每个参数都应该单独列出来,并加上引号.如下:
function(inner)
message("inner:\n"
"ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
endfunction()
function(outer)
message("outer:\n"
"ARGC = ${ARGC}\n"
"ARGN = ${ARGN}")
cmake_parse_arguments(PARSE_ARGV 0 FWD "" "" "")
set(quoteArgs "")
foreach(arg IN LISTS FWD_UNPARSED_ARGUMENTS)
string(APPEND quoteArgs " [===[${arg}]===]")
endforeach()
message("quoteArgs = ${quoteArgs}")
cmake_language(EVAL CODE "inner(${quoteArgs})")
endfunction()
message("=====================")
outer("a;b;c" "d;e;f" "")
注意: string(APPEND quoteArgs " [===[${arg}]===]")
引号第一个空格不能省.
上面解决方案因为用到cmake_parse_arguments()
命令的PARSE_ARGV
形式,所以在宏里面不能使用.
函数传参遇到不平衡方括号的坑
还有一种特殊情况,就是参数带有不平衡的方括号需要特别注意,例如:
function(func)
message("Number of arguments: ${ARGC}")
math(EXPR lastIndex "${ARGC} - 1")
foreach(n RANGE 0 ${lastIndex})
message("ARGV${n} = ${ARGV${n}}")
endforeach()
foreach(arg IN LISTS ARGV)
message("${arg}")
endforeach()
endfunction()
message("=====================")
func("a[a" "b]b" "c[c]c" "d[d" "eee")
func()函数可以看到5个参数,但是对ARGV取值来说,只能取到3个值,这就是方括号不平衡的问题.我们写cmake的时候需要特别注意,避免使用.