【问题标题】:Function vs. Macro in CMakeCMake 中的函数与宏
【发布时间】:2014-08-09 11:36:43
【问题描述】:

The official document of CMake 2.8.12macro

调用时,宏中记录的命令首先 通过用实参替换形式参数 (${arg1}) 进行修改 通过,然后作为正常命令调用。

关于function

在调用时,函数中记录的命令是第一个 通过用实参替换形式参数 (${arg1}) 进行修改 通过,然后作为正常命令调用。

显然,这两个引号几乎相同,但令人困惑。参数替换在函数和宏中的行为是否相同?

【问题讨论】:

  • functionmacro 之间至少还有一个重要的,尽管相当明显的区别:return() 的语义:当用于 macro 时,你不会从宏,但来自调用函数。
  • 另一个重要的注意事项是,当函数只有一个时,宏在参数上具有两遍扩展阶段。尝试创建这些宏和函数,并从内部打印${ARGV}macro(my_macro)function(my_func)。并使用它们:set(a 123)my_macro("\\\${a}\\\\;\\\;;")my_func(\${a}\\;\;;)。您会发现您必须对所有 $\ ; 进行双重转义,才能正确地将整个字符串原封不动地传递给嵌套命令。这是cmake 3.14+ 中的实际情况。

标签: cmake


【解决方案1】:

我在下面写了一个示例代码:

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 在调用Foo 时被分配了var 的值,而${arg} 在调用Moo 时只是用${var} 替换的字符串。

所以我觉得上面的两个引用很容易让人混淆,虽然官方文档也说that:

请注意,宏的 参数 和 ARGN 等 不是通常 CMake 意义上的变量。它们是字符串替换,就像 C 预处理器会做的那样 用宏。如果你想要真正的 CMake 变量和/或更好的 CMake 范围控制你应该看看函数命令。

更新(2021 年 1 月 29 日)

在语句Moo(${var})之后添加以下语句,使宏和函数的区别更加清晰。

message(${arg})

此语句将打印出abc

【讨论】:

  • 我忘记了,但我想可能是。
  • @robert 根据帮助中心的规定,允许立即回答您自己的问题(特别是如果这是一个很好的、非重复的、其他人普遍感兴趣的问题)。这是为了帮助 SO 成为更好的知识库。您是否阅读了该帮助中心主题中链接的博客文章? stackoverflow.blog/2011/07/01/…
  • @robert 我只是在转述 SO 创始人本人对这种做法的看法。和他一起去吧。 ;-)
  • cmake --trace-expand 运行这样的例子很有启发性
  • 请考虑在每次调用后添加以下命令:message("# arg in main scope = '${arg}'") + 在宏之前调用函数。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    您引用的 cmake 文档具有误导性,基本上是错误的。应该像这样澄清/修复它:

    • 宏:当它被调用时,宏中记录的命令在任何运行之前首先被修改,通过将形式参数(${arg1})替换为传递的参数。

    cmake --trace-expand 准确显示了发生的情况。

    cmake 3.13.3 文档在这方面与 2.8.12 相比没有变化。

    【讨论】:

      【解决方案4】:

      macro expansion, answered by Yantao Xie 真的让我大开眼界!

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

      引用自Learn cmake in 15 mins

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

      function(doubleIt VALUE)
          math(EXPR RESULT "${VALUE} * 2")
          message("${RESULT}")
      endfunction()
      
      doubleIt("4")                           # Prints: 8
      

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

      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
      

      【讨论】:

        【解决方案5】:

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

        来自cmake documentation of 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-07
          相关资源
          最近更新 更多