【问题标题】:CMake compare to empty string with STREQUAL failedCMake与STREQUAL比较空字符串失败
【发布时间】:2013-11-27 17:45:42
【问题描述】:

我一直认为,如果你想比较两个字符串(而不是变量),你需要做的就是这样引用它:

if("${A}" STREQUAL "some string")

但现在我发现这段代码有时会打印出oops:

cmake_minimum_required(VERSION 2.8)

if("d" STREQUAL "")
  message("oops...")
endif()

可能是错误(因为它使用Xcode 打印,但不是make)? 还是有一些特殊的变量?

  • cmake: 2.8.12, 2.8.11.2
  • xcode:4.6.2、5.0.1

更新

有命令string 没有描述问题:

string(COMPARE EQUAL "${A}" "" result)
if(result)
  message("...")
endif()

更新 2

CMake 3.1.0(参见CMP0054)以来我所期望的行为。

3.0.2 的输出test

CMake version: 3.0.2
Quoted test
Surprise!
Unquoted test
Surprise!

3.1.0 的输出test

CMake version: 3.1.0
Quoted test
OK
Unquoted test
Surprise!

【问题讨论】:

  • 当“变量”未定义时,它有时会进入 (VAR STREQUAL "") 条件。在这种情况下 (NOT DEFINED VAR) 是设置默认值的暂缓时间(例如)。此处参考:cmake.org/cmake/help/v3.3/command/if.html
  • 我认为中间操作符在哪里被弃用了?在 CMAKE 中很难确定什么是新的,什么是旧的。

标签: cmake


【解决方案1】:

您遇到了 CMake 的一个相当烦人的“这不是错误,而是一个特性”行为。正如if command 的文档中所述:

 The if command was written very early in CMake's history, predating the ${} 
 variable evaluation syntax, and for convenience evaluates variables named
 by its arguments as shown in the above signatures.

嗯,便利变成了不便。在您的示例中,if 命令将字符串 "d" 视为名为 d 的变量。如果变量d恰好被定义为空字符串,则消息语句将打印“oops...”,例如:

set (d "")
if("d" STREQUAL "")
  # this branch will be taken
  message("oops...")
else()
  message("fine")
endif()

这会给像

这样的语句带来令人惊讶的结果
if("${A}" STREQUAL "some string")

因为如果变量 A 恰好被定义为一个字符串,该字符串也是 CMake 变量的名称,例如:

set (A "d")
set (d "some string")   
if("${A}" STREQUAL "some string")
  # this branch will be taken
  message("oops...")
else()
  message("fine")
endif()

可能的解决方法:

您可以在 ${} 扩展后向字符串添加后缀字符,以防止 if 语句进行自动评估:

set (A "d")
set (d "some string")
if("${A} " STREQUAL "some string ")
  message("oops...")
else()
  # this branch will be taken
  message("fine")
endif()

不要使用${}扩展:

set (A "d")
set (d "some string")
if(A STREQUAL "some string")
  message("oops...")
else()
  # this branch will be taken
  message("fine")
endif()

为防止在 STREQUAL 右侧出现意外评估,请改用 MATCHESCMake regular expression

if(A MATCHES "^value$")
  ...
endif()

附录:CMake 3.1 不再对引用的参数进行双重扩展。请参阅new policy

【讨论】:

  • 是的,我当然读过这部分。我在文档中没有看到任何双引号注释,只是希望有某种扩展保护,并且if(d ...)if("d" ...) 不同。结果是一样的:if(DEFINED "d")TRUE):由于某种原因,d 是为一个生成器(Xcode)定义的,但不是为其他生成器(make)定义的。顺便说一句,有人可以为变量名使用后缀:set("d " "other string"),可能最好使用像_NEVER_NAME_VARIABLE_LIKE_THAT 这样的重词(:
  • 使用空格作为后缀字符可能就足够了,因为您无法访问名称中包含空格的变量(在您的示例中,变量 d 无法访问)。我同意您应该避免使用其他字符,因此如果不使用空格,请按照您的示例使用长字符串。
  • @Fraser since you can't access a variable whose name contains a space 为什么不呢?示例:set("A " "some string") set(VAR_NAME "A ") message(${${VAR_NAME}}) 输出:some string。我知道这个例子看起来是合成的,但是如果你经常使用包装器,你可以在实际代码中遇到这种情况。像这样:set(VAR_NAME "${FIRST} ${SECOND}"),如果SECOND 为空,您将得到VAR_NAME 以空格结尾。
  • 您可能会好奇(因为 SO 不会通知兄弟答案)从 CMake 3.1 开始,这不再适用。
  • @ArthurTacca 不幸的是,使用旧版本的 CMake(例如 2.8)仍然很常见。
【解决方案2】:

从 CMake 3.1 开始,有新规则 variable expansions in if()。如果您满足以下任一条件,它们就会启用:

即使在这种情况下,if 的第一个参数仍然使用与该名称匹配的变量的值进行扩展(如果存在):

set (d "")
if(d STREQUAL "")
  # this branch will be taken
  message("oops...")
else()
  message("fine")
endif()

但是,如果第一个参数被引用,这将被禁用:

set (d "")
if("d" STREQUAL "")
  message("oops...")
else()
  # due to quotes around "d" in if statement,
  # this branch will be taken
  message("fine")
endif()

如果您确实想根据值测试变量的内容,您可以使用经典的不带引号的语法,或使用您建议的"${d}" 语法。感谢新规则,这永远不会遇到sakra回答中提到的双膨胀问题:

set (A "d")
set (d "some string")   
if("${A}" STREQUAL "d")
  # this branch will be taken
  message("fine")
elseif("${A}" STREQUAL "some string")
  message("oops...")
else()
  message("??")
endif()

【讨论】:

    猜你喜欢
    • 2016-07-05
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多