Функция против макроса в 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+.
Andry

Ответы:

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.

Поэтому я думаю, что приведенные выше две цитаты очень легко сбить с толку, хотя в официальных документах также сказано, что :

Обратите внимание, что параметры макроса и значения, такие как ARGN, не являются переменными в обычном смысле CMake. Они заменяют строки так же, как препроцессор C делает с макросами. Если вам нужны настоящие переменные CMake и / или лучший контроль области видимости CMake, вам следует взглянуть на команду функции.

Янтао Се
источник
Я забыл об этом, но думаю, может быть.
Янтао Се
2
@robert Немедленный ответ на свой вопрос разрешен в Справочном центре (особенно, если это хороший, не повторяющийся вопрос, представляющий общий интерес для других). Это должно помочь SO стать лучшей базой знаний. Вы читали сообщение в блоге, указанное в этой теме Справочного центра? stackoverflow.blog/2011/07/01/...
Эмиль Кормье
1
@robert Я просто передаю то, что сам основатель SO думает об этой практике. Возьми это с собой. ;-)
Эмиль Кормье
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

Макрорасширения ответили Yantao С действительно открывает мне глаза!

Я также обнаружил, что в приведенном ниже руководстве есть несколько конкретных примеров, которые помогают понять концепцию области видимости переменных.

Цитируется по Learn cmake за 15 минут :

В 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
Изана
источник