CMake中的功能与宏


89

CMake 2.8.12的官方文件中提到macro

调用宏时,首先通过用传递的参数替换形式参数($ {arg1})来修改宏中记录的命令,然后将其作为普通命令来调用。

和关于 function

调用它时,函数中记录的命令首先通过用传递的参数替换形式参数($ {arg1})进行修改,然后作为普通命令调用。

显然,两个引号几乎相同,但使我感到困惑。像宏一样调用函数时,首先要替换参数吗?


8
function和之间至少还有一个重要的区别,尽管macro:的语义相当明显return():在a中使用时macro,您不会从宏返回,而是从调用函数返回。
Joachim W

1
另一个重要说明是,当函数仅为一个时,宏对参数进行两次遍历扩展。尝试创建这些宏和函数,然后${ARGV}从内部打印macro(my_macro),,function(my_func)。并使用它们:set(a 123)my_macro("\\\${a}\\\\;\\\;;")my_func(\${a}\\;\;;)。您将发现必须对所有$,进行两次转义\ ;才能正确地将未更改的整个字符串传递给嵌套命令。这是实际的cmake 3.14+
安德里

Answers:


93

我在下面编写了示例代码:

set(var "ABC")

macro(Moo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})

function(Foo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})

输出为:

=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc

因此,似乎arg被赋予的价值var时调用Foo,并${arg}与刚刚替换字符串${var}时调用Moo

所以我认为上述两个引号很容易使人迷惑,虽然官方文件还表示

请注意,在通常的CMake意义上,宏的参数和诸如ARGN之类的不是变量。它们是字符串替换,非常类似于C预处理器对宏的处理。如果您需要真正的CMake变量和/或更好的CMake范围控制,则应查看function命令。


我已经忘记了,但是我认为可能是这样。
Yantao Xie

2
@robert根据帮助中心的要求,可以立即回答您自己的问题(特别是如果它是他人普遍关心的一个很好的,不可重复的问题)。这是为了帮助SO成为更好的知识库。您是否阅读了该帮助中心主题中链接的博客文章?stackoverflow.blog/2011/07/01/...
埃米尔·科米尔

1
@robert我只是在传达SO的创始人本人对此做法的看法。随他去吧。;-)
Emile Cormier

2
运行这样的示例cmake --trace-expand很有启发性
-MarcH

1
请考虑在每次调用后添加以下命令:message("# arg in main scope = '${arg}'")+在宏之前调用函数。
MarcH

34

换句话说,函数推送并弹出新的变量范围(创建和更改的变量仅存在于函数中),宏则不存在。但是,您可以使用命令的PARENT_SCOPE参数覆盖功能默认行为set


8

您引用的cmake文档是如此令人误解,以至于基本上是错误的。应该这样澄清/修复:

  • 宏:调用宏时,首先将所有记录在宏中的命令修改,然后再通过将形式参数($ {arg1})替换为传递的参数来运行任何命令。

cmake --trace-expand 显示确切发生了什么。

与此相比,cmake 3.13.3文档与2.8.12相比没有变化。


3

function()和之间的另一个显着区别macro()是的行为return()

cmake的return()文档中

请注意,与函数不同,宏在适当位置被扩展,因此无法处理return()。

因此,由于它已在适当位置扩展,因此它macro()从调用方返回。在功能中,它只是退出function()

例:

macro(my_macro)
    return()
endmacro()

function(my_function)
    return()
endfunction()

my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed

2

谢延涛回答宏扩展真的让我大开眼界

我还发现下面的教程带有一些具体示例,这有助于理解可变范围的概念。

在15分钟内Learn cmake引用:

在CMake中,您可以使用一对function/endfunction命令来定义一个函数。这是将其参数的数值加倍的一个,然后输出结果:

function(doubleIt VALUE)
    math(EXPR RESULT "${VALUE} * 2")
    message("${RESULT}")
endfunction()

doubleIt("4")                           # Prints: 8

函数在自己的范围内运行。函数中定义的任何变量都不会污染调用方的作用域。如果要返回值,可以将变量的名称传递给函数,然后set使用特殊参数调用命令PARENT_SCOPE

function(doubleIt VARNAME VALUE)
    math(EXPR RESULT "${VALUE} * 2")
    set(${VARNAME} "${RESULT}" PARENT_SCOPE)    # Set the named variable in caller's scope
endfunction()

doubleIt(RESULT "4")                    # Tell the function to set the variable named RESULT
message("${RESULT}")                    # Prints: 8

同样,一对macro/endmacro命令定义一个宏。与函数不同,宏在与其调用程序相同的范围内运行。因此,在宏内定义的所有变量都在调用者的作用域中设置。我们可以用以下内容替换以前的功能:

macro(doubleIt VARNAME VALUE)
    math(EXPR ${VARNAME} "${VALUE} * 2")        # Set the named variable in caller's scope
endmacro()

doubleIt(RESULT "4")                    # Tell the macro to set the variable named RESULT
message("${RESULT}")                    # Prints: 8

函数和宏都接受任意数量的参数。未命名的参数通过名为的特殊变量以列表形式显示在函数中ARGN

这是一个函数,它将接收到的每个参数加倍,并在单独的行上打印每个参数:

function(doubleEach)
    foreach(ARG ${ARGN})                # Iterate over each argument
        math(EXPR N "${ARG} * 2")       # Double ARG's numeric value; store result in N
        message("${N}")                 # Print N
    endforeach()
endfunction()

doubleEach(5 6 7 8)                     # Prints 10, 12, 14, 16 on separate lines
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.