Funzione e macro in CMake


90

Il documento ufficiale di CMake 2.8.12 parla dimacro

Quando viene richiamato, i comandi registrati nella macro vengono prima modificati sostituendo i parametri formali ($ {arg1}) con gli argomenti passati, e quindi richiamati come normali comandi.

e a proposito di function

Quando viene richiamato, i comandi registrati nella funzione vengono prima modificati sostituendo i parametri formali ($ {arg1}) con gli argomenti passati, e quindi richiamati come normali comandi.

Ovviamente, due citazioni sono quasi uguali ma mi confondono. Sostituisce i parametri all'inizio quando si chiama una funzione proprio come la macro?


8
C'è almeno un'altra differenza importante, anche se abbastanza ovvia, tra functione macro: la semantica di return(): Quando viene utilizzata in a macro, non si ritorna dalla macro ma dalla funzione chiamante.
Joachim W

1
Un'altra nota importante è che una macro ha una fase di espansione a due passaggi sugli argomenti quando una funzione è solo una. Prova a creare queste macro e funzioni e stampa ${ARGV}dall'interno: macro(my_macro), function(my_func). E li usa: set(a 123), my_macro("\\\${a}\\\\;\\\;;"), my_func(\${a}\\;\;;). Troverete che è necessario fare doppio fuga tutto il $, \ , ;di passare correttamente intera stringa invariato ai comandi annidati. Questo è effettivo in cmake 3.14+.
Andry

Risposte:


95

Ho scritto un codice di esempio di seguito:

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

e l'output è:

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

Quindi sembra che argsia assegnato il valore di varquando si chiama Fooe che ${arg}sia solo una stringa sostituita con ${var}quando si chiama Moo.

Quindi penso che le due citazioni precedenti siano molto facili da confondere, sebbene i documenti ufficiali dicessero anche che :

Si noti che i parametri di una macro e valori come ARGN non sono variabili nel solito senso di CMake. Sono sostituzioni di stringhe proprio come farebbe il preprocessore C con una macro. Se desideri vere variabili CMake e / o un migliore controllo dell'ambito CMake, dovresti guardare il comando della funzione.


L'ho dimenticato, ma penso che possa essere.
Yantao Xie,

2
@robert Rispondere immediatamente alla tua domanda è consentito secondo il Centro assistenza (soprattutto se si tratta di una buona domanda non duplicata di interesse generale per gli altri). Questo per aiutare SO a diventare una base di conoscenza migliore. Hai letto il post del blog collegato a quell'argomento del Centro assistenza? stackoverflow.blog/2011/07/01/…
Emile Cormier

1
@robert Sto solo trasmettendo ciò che lo stesso fondatore di SO pensa di questa pratica. Prendilo con lui. ;-)
Emile Cormier

2
L'esecuzione di esempi come questo con cmake --trace-expandè illuminante
MarcH

1
Considerare di aggiungere il seguente comando dopo ogni chiamata: message("# arg in main scope = '${arg}'")+ chiamare la funzione prima della macro.
MarcH

34

In altre parole, la funzione inserisce e apre il nuovo ambito della variabile (le variabili create e modificate esistono solo nella funzione), la macro no. Tuttavia, è possibile sovrascrivere il comportamento predefinito della funzione con il PARENT_SCOPEparametro del setcomando.


8

La documentazione di cmake che hai citato è così fuorviante che fondamentalmente è sbagliata. Dovrebbe essere chiarito / corretto in questo modo:

  • macro: quando viene invocato, i comandi registrati nella macro vengono prima tutti modificati prima di eseguirne uno sostituendo i parametri formali ($ {arg1}) con gli argomenti passati.

cmake --trace-expand mostra esattamente cosa succede.

Il documento cmake 3.13.3 non è cambiato rispetto a 2.8.12 rispetto a questo.


3

L' espansione macro, a cui ha risposto Yantao Xie, mi apre davvero gli occhi!

Ho anche scoperto che il tutorial di seguito include alcuni esempi concreti, utili per comprendere il concetto di ambito variabile.

Citato da Learn cmake in 15 mins :

In CMake, puoi usare una coppia di comandi function/ endfunctionper definire una funzione. Eccone uno che raddoppia il valore numerico del suo argomento, quindi stampa il risultato:

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

doubleIt("4")                           # Prints: 8

Le funzioni vengono eseguite nel proprio ambito. Nessuna delle variabili definite in una funzione inquina l'ambito del chiamante. Se vuoi restituire un valore, puoi passare il nome di una variabile alla tua funzione, quindi chiamare il setcomando con l'argomento speciale 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

Allo stesso modo, una coppia di comandi macro/ endmacrodefinisce una macro. A differenza delle funzioni, le macro vengono eseguite nello stesso ambito del loro chiamante. Pertanto, tutte le variabili definite all'interno di una macro sono impostate nell'ambito del chiamante. Possiamo sostituire la funzione precedente con la seguente:

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

Sia le funzioni che le macro accettano un numero arbitrario di argomenti. Gli argomenti senza nome vengono esposti alla funzione come un elenco, tramite una variabile speciale denominata ARGN.

Ecco una funzione che raddoppia ogni argomento che riceve, stampandoli su una riga separata:

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

3

Un'altra notevole differenza tra function()e macro()è il comportamento di return().

Dalla documentazione cmake di return () :

Notare che una macro, a differenza di una funzione, è espansa sul posto e quindi non può gestire return ().

Quindi, poiché è espanso sul posto, in a macro()ritorna dal chiamante. Mentre è in una funzione, esce semplicemente dal filefunction()

Esempio:

macro(my_macro)
    return()
endmacro()

function(my_function)
    return()
endfunction()

my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.