CMakeの関数とマクロ


89

CMake2.8.12の公式文書は約macro

呼び出されると、マクロに記録されたコマンドは、最初に仮パラメーター($ {arg1})を渡された引数に置き換えることによって変更され、次に通常のコマンドとして呼び出されます。

とについて function

関数に記録されたコマンドは、呼び出されると、最初に仮パラメーター($ {arg1})を渡された引数に置き換えることによって変更され、次に通常のコマンドとして呼び出されます。

明らかに、2つの引用符はほとんど同じですが、私を混乱させます。マクロのように関数を呼び出すときに、最初にパラメーターを置き換えますか?


8
functionmacro:のセマンティクスにはかなり明らかな違いがありますが、少なくとも1つの重要な違いがあります。でreturn()使用macroすると、マクロからではなく、呼び出し元の関数から戻ります。
Joachim W

1
もう1つの重要な注意点は、関数が1つしかない場合、マクロには引数の2パス展開ステージがあるということです。これらのマクロと関数を作成し${ARGV}、内部から印刷してみてください:macro(my_macro)function(my_func)。そして、それらを使用します:set(a 123)my_macro("\\\${a}\\\\;\\\;;")my_func(\${a}\\;\;;)。あなたは、すべての二重のエスケープに持っていることがわかります$\ ;適切にネストされたコマンドにそのまま文字列全体を渡します。これは実際のcmake 3.14+です。
アンドリー

回答:


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

したがって、公式文書にも次のように書かれていますが、上記の2つの引用は1つを混乱させるのは非常に簡単だと思います。

マクロのパラメーターやARGNなどのは、通常のCMakeの意味での変数ではないことに注意してください。これらは、Cプリプロセッサがマクロで行うのとよく似た文字列置換です。真のCMake変数やより優れたCMakeスコープ制御が必要な場合は、関数コマンドを確認する必要があります。


忘れてしまいましたが、そうかもしれません。
Yantao Xie 2016

2
@robertヘルプセンターに従って、自分の質問にすぐに回答することが許可されています(特に、他の人が一般的に関心を持っている、重複しない良い質問である場合)。これは、SOがより良い知識ベースになるのを助けるためです。そのヘルプセンターのトピックにリンクされているブログ投稿を読みましたか?stackoverflow.blog/2011/07/01/…–
Emile Cormier

1
@robert私はSOの創設者自身がこの慣行について考えていることを伝えているだけです。彼と一緒にそれを取り上げなさい。;-)
Emile Cormier

2
このように例を実行すると、cmake --trace-expand啓発的である
三月

1
各呼び出しの後に次のコマンドを追加することを検討してください。message("# arg in main scope = '${arg}'")+マクロの前に関数を呼び出す。
行進

34

言い換えると、関数は新しい変数スコープをプッシュおよびポップします(作成および変更された変数は関数内にのみ存在します)が、マクロはそうではありません。ただし、コマンドのPARENT_SCOPEパラメーターを使用して、関数のデフォルトの動作をオーバーライドできますset


8

あなたが引用したcmakeのドキュメントは非常に誤解を招くため、基本的に間違っています。次のように明確化/修正する必要があります。

  • マクロ:呼び出されると、マクロに記録されたコマンドは、実行される前に、仮パラメーター($ {arg1})を渡された引数に置き換えることによって、最初にすべて変更されます。

cmake --trace-expand 何が起こるかを正確に示しています。

これに関して、cmake3.13.3ドキュメントは2.8.12と比較して変更されていません。


3

間のもう一つの顕著な違いfunction()macro()の動作ですreturn()

return()のcmakeドキュメントから:

マクロは、関数とは異なり、その場で展開されるため、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謝によって答えマクロ展開は、本当に私の目をオープン!

また、以下のチュートリアルには、可変スコープの概念を理解するのに役立つ具体的な例がいくつか含まれていることがわかりました。

15分でLearncmakeから引用:

CMakeでは、function/endfunctionコマンドのペアを使用して関数を定義できます。これは、引数の数値を2倍にして、結果を出力するものです。

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ます。

受け取るすべての引数を2倍にして、それぞれを別々の行に出力する関数を次に示します。

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.