Función frente a macro en CMake

89

El documento oficial de CMake 2.8.12 dice sobremacro

Cuando se invoca, los comandos registrados en la macro se modifican primero reemplazando los parámetros formales ($ {arg1}) con los argumentos pasados ​​y luego se invocan como comandos normales.

y sobre function

Cuando se invoca, los comandos registrados en la función se modifican primero reemplazando los parámetros formales ($ {arg1}) con los argumentos pasados ​​y luego se invocan como comandos normales.

Obviamente, dos citas son casi iguales pero me confunden. ¿Reemplaza los parámetros al principio cuando se llama a una función como una macro?

Yantao Xie
fuente
8
Hay al menos otra diferencia importante, aunque bastante obvia, entre functiony macro: la semántica de return(): Cuando se usa en a macro, no regresará de la macro sino de la función de llamada.
Joachim W
1
Otra nota importante es que una macro tiene una etapa de expansión de dos pasos en los argumentos cuando una función es solo una. Intente crear estas macros y funciones, e imprima ${ARGV}desde adentro: macro(my_macro), function(my_func). Y utilizarlos: set(a 123), my_macro("\\\${a}\\\\;\\\;;"), my_func(\${a}\\;\;;). Usted encontrará que hay que hacer doble escape de todo el $, \ , ;para pasar adecuadamente toda la cadena sin cambios a los comandos anidados. Esto es real en el cmake 3.14+.
Andry

Respuestas:

93

Escribí un código de muestra a continuación:

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})

y la salida es:

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

Así que parece que argse le asigna el valor de varcuando se llama Fooy ${arg}solo se reemplaza la cadena con ${var}cuando se llamaMoo .

Así que creo que las dos citas anteriores son muy fáciles de confundir, aunque los documentos oficiales también dicen que :

Tenga en cuenta que los parámetros de una macro y valores como ARGN no son variables en el sentido habitual de CMake. Son reemplazos de cadenas muy parecidos a los que haría el preprocesador de C con una macro. Si desea verdaderas variables de CMake y / o un mejor control del alcance de CMake, debe mirar el comando de función.

Yantao Xie
fuente
Lo he olvidado, pero creo que puede serlo.
Yantao Xie
2
@robert Se permite responder su propia pregunta de inmediato según el Centro de ayuda (especialmente si es una buena pregunta no duplicada de interés general para otros). Esto es para ayudar a SO a convertirse en una mejor base de conocimientos. ¿Leíste la publicación de blog vinculada en ese tema del Centro de ayuda? stackoverflow.blog/2011/07/01/…
Emile Cormier
1
@robert Solo estoy transmitiendo lo que el propio fundador de SO piensa sobre esta práctica. Hazlo con él. ;-)
Emile Cormier
2
Ejecutar ejemplos como este con cmake --trace-expandes esclarecedor
MarcH
1
Considere agregar el siguiente comando después de cada llamada: message("# arg in main scope = '${arg}'")+ llamar a la función antes de la macro.
MarcH
34

En otras palabras, la función empuja y muestra el nuevo alcance de la variable (las variables creadas y cambiadas existen solo en la función), la macro no. Sin embargo, puede anular el comportamiento predeterminado de la función con el PARENT_SCOPEparámetro del setcomando.

robert
fuente
8

La documentación de cmake que citó es tan engañosa que básicamente es incorrecta. Debería aclararse / arreglarse así:

  • macro: cuando se invoca, los comandos registrados en la macro se modifican primero antes de ejecutar cualquiera, reemplazando los parámetros formales ($ {arg1}) con los argumentos pasados.

cmake --trace-expand muestra exactamente lo que sucede.

El documento cmake 3.13.3 no ha cambiado en comparación con 2.8.12 con respecto a esto.

Marzo
fuente
3

Otra diferencia notable entre function()y macro()es el comportamiento de return().

De la documentación de cmake de return () :

Tenga en cuenta que una macro, a diferencia de una función, se expande en su lugar y, por lo tanto, no puede manejar return ().

Entonces, debido a que se expande en su lugar, macro()regresa de la persona que llama. Mientras está en una función, simplemente sale delfunction()

Ejemplo:

macro(my_macro)
    return()
endmacro()

function(my_function)
    return()
endfunction()

my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed
Adam Zahran
fuente
2

La macro expansión, respondida por Yantao Xie realmente me abre los ojos!

También encontré que el tutorial a continuación viene con algunos ejemplos concretos, lo cual es útil para comprender el concepto de alcance variable.

Citado de Learn cmake en 15 minutos :

En CMake, puede usar un par de comandos function/ endfunctionpara definir una función. Aquí hay uno que duplica el valor numérico de su argumento y luego imprime el resultado:

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

doubleIt("4")                           # Prints: 8

Las funciones se ejecutan en su propio ámbito. Ninguna de las variables definidas en una función contamina el alcance de la persona que llama. Si desea devolver un valor, puede pasar el nombre de una variable a su función, luego llamar alset comando con el argumento especial 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

Del mismo modo, un par de macroendmacro comandos / define una macro. A diferencia de las funciones, las macros se ejecutan en el mismo ámbito que su llamador. Por lo tanto, todas las variables definidas dentro de una macro se establecen en el alcance del llamador. Podemos reemplazar la función anterior por la siguiente:

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

Tanto las funciones como las macros aceptan un número arbitrario de argumentos. Los argumentos sin nombre se exponen a la función como una lista, a través de una variable especial llamadaARGN .

Aquí hay una función que duplica cada argumento que recibe, imprimiendo cada uno en una línea separada:

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
Izana
fuente