Funkcja a makro w CMake

89

Oficjalny dokument CMake 2.8.12 mówi omacro

Po wywołaniu polecenia zapisane w makrze są najpierw modyfikowane przez zastąpienie parametrów formalnych ($ {arg1}) przekazanymi argumentami, a następnie wywoływane jak zwykłe polecenia.

i o function

Po wywołaniu polecenia zapisane w funkcji są najpierw modyfikowane przez zastąpienie parametrów formalnych ($ {arg1}) przekazanymi argumentami, a następnie wywoływane jako zwykłe polecenia.

Oczywiście dwa cytaty są prawie takie same, ale mnie mylą. Czy najpierw zastępuje parametry podczas wywoływania funkcji, tak jak makro?

Yantao Xie
źródło
8
Istnieje co najmniej jeszcze jedna ważna, aczkolwiek dość oczywista różnica między functioni macro: semantyka return(): Gdy zostanie użyta w a macro, nie powrócisz z makra, ale z funkcji wywołującej.
Joachim W
1
Kolejną ważną uwagą jest to, że makro ma dwuprzebiegowy stopień rozwinięcia argumentów, gdy funkcja jest tylko jedna. Spróbuj stworzyć te makro i funkcje i wydrukuj ${ARGV}od wewnątrz: macro(my_macro), function(my_func). I korzystać z nich: set(a 123), my_macro("\\\${a}\\\\;\\\;;"), my_func(\${a}\\;\;;). Przekonasz się, że masz do podwójnej ucieczki wszystko $, \ , ;aby prawidłowo przechodzić cały ciąg niezmienionej do zagnieżdżonych poleceń. To jest aktualne w cmake 3.14+.
Andry

Odpowiedzi:

93

Poniżej napisałem przykładowy kod:

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

a wynik to:

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

Wydaje się więc, że argprzypisano wartośćvar , gdy dzwoni Fooi ${arg}jest po prostu ciąg zastąpione ${var}podczas wywoływania Moo.

Myślę więc, że powyższe dwa cytaty są bardzo łatwe do pomylenia, chociaż oficjalne dokumenty również mówią że :

Zwróć uwagę, że parametry makra i wartości, takie jak ARGN, nie są zmiennymi w zwykłym sensie CMake. Są zamiennikami łańcuchów, podobnie jak preprocesor C zrobiłby z makrem. Jeśli chcesz prawdziwych zmiennych CMake i / lub lepszej kontroli zakresu CMake, powinieneś przyjrzeć się poleceniu funkcji.

Yantao Xie
źródło
Zapomniałem o tym, ale myślę, że może tak być.
Yantao Xie
2
@robert Natychmiastowe odpowiadanie na własne pytanie jest dozwolone zgodnie z Centrum pomocy (zwłaszcza jeśli jest to dobre, niepowtarzalne pytanie o ogólnym znaczeniu dla innych). Ma to pomóc SO stać się lepszą bazą wiedzy. Czy przeczytałeś post na blogu, do którego link znajduje się w tym temacie w Centrum pomocy? stackoverflow.blog/2011/07/01/…
Emile Cormier
1
@robert Po prostu przekazuję, co sam założyciel SO myśli o tej praktyce. Zabierz to ze sobą. ;-)
Emile Cormier
2
Prowadzenie takich przykładów z pomocą cmake --trace-expandjest pouczające
MarcH,
1
Proszę rozważyć dodanie następującego polecenia po każdym wywołaniu: message("# arg in main scope = '${arg}'")+ wywołanie funkcji przed makrem.
MarcH
34

Innymi słowy, funkcja wypycha i zdejmuje nowy zakres zmiennej (utworzone i zmienione zmienne istnieją tylko w funkcji), makro nie. Można jednak przesłonić domyślne zachowanie funkcji PARENT_SCOPEparametrem setpolecenia.

robert
źródło
8

Cytowana przez ciebie dokumentacja cmake jest tak myląca, że ​​w zasadzie jest błędna. Należy to wyjaśnić / naprawić w następujący sposób:

  • makro: po wywołaniu polecenia zapisane w makrze są najpierw modyfikowane, zanim jakiekolwiek zostaną uruchomione, poprzez zastąpienie parametrów formalnych ($ {arg1}) przekazanymi argumentami.

cmake --trace-expand pokazuje dokładnie, co się dzieje.

Dokument cmake 3.13.3 nie zmienił się w porównaniu z wersją 2.8.12 pod tym względem.

Marsz
źródło
3

Inną zauważalną różnicą między function()i macro()jest zachowanie return().

Z dokumentacji cmake zwrotu () :

Zauważ, że makro, w przeciwieństwie do funkcji, jest rozwijane w miejscu i dlatego nie może obsługiwać funkcji return ().

Tak więc, ponieważ jest rozwijany w miejscu, w a macro()wraca od wywołującego. W funkcji po prostu wychodzi zfunction()

Przykład:

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
źródło
2

Rozszerzenie makro, na które odpowiedział Yantao Xie, naprawdę otwiera mi oczy!

Zauważyłem również, że poniższy samouczek zawiera kilka konkretnych przykładów, które są pomocne w zrozumieniu pojęcia zakresu zmiennych.

Cytowane z Learn cmake w 15 minut :

W CMake możesz użyć pary plików functionendfunction poleceń / do zdefiniowania funkcji. Oto przykład, który podwaja wartość liczbową swojego argumentu, a następnie wyświetla wynik:

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

doubleIt("4")                           # Prints: 8

Funkcje działają we własnym zakresie. Żadna ze zmiennych zdefiniowanych w funkcji nie zanieczyszcza zakresu wywołującego. Jeśli chcesz zwrócić wartość, możesz przekazać nazwę zmiennej do swojej funkcji, a następnie wywołać setpolecenie ze specjalnym argumentemPARENT_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

Podobnie, para macro/endmacro poleceń definiuje makro. W przeciwieństwie do funkcji makra działają w tym samym zakresie co ich obiekt wywołujący. Dlatego wszystkie zmienne zdefiniowane wewnątrz makra są ustawiane w zakresie wywołującego. Możemy zastąpić poprzednią funkcję następującą:

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

Zarówno funkcje, jak i makra akceptują dowolną liczbę argumentów. Nienazwane argumenty są przedstawiane funkcji jako lista za pośrednictwem specjalnej zmiennej o nazwieARGN .

Oto funkcja, która podwaja każdy otrzymany argument, wypisując każdy z nich w osobnym wierszu:

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
źródło