Fungsi vs. Makro di CMake

90

Dokumen resmi CMake 2.8.12 mengatakan tentangmacro

Saat dipanggil, perintah yang direkam di makro pertama-tama diubah dengan mengganti parameter formal ($ {arg1}) dengan argumen yang diteruskan, dan kemudian dipanggil sebagai perintah normal.

dan tentang function

Ketika dipanggil, perintah yang direkam dalam fungsi tersebut pertama-tama dimodifikasi dengan mengganti parameter formal ($ {arg1}) dengan argumen yang diteruskan, dan kemudian dipanggil sebagai perintah normal.

Jelas, dua kutipan hampir sama tetapi membingungkan saya. Apakah mengganti parameter pada awalnya saat memanggil fungsi seperti makro?

Yantao Xie
sumber
8
Setidaknya ada satu perbedaan penting lainnya, meskipun perbedaan yang cukup jelas antara functiondan macro: semantik dari return(): Saat digunakan dalam a macro, Anda tidak akan kembali dari makro tetapi dari fungsi pemanggil.
Joachim W
1
Catatan penting lainnya, adalah bahwa makro memiliki tahap perluasan dua lintasan pada argumen ketika sebuah fungsi hanya satu. Cobalah untuk membuat makro dan fungsi ini, dan cetak ${ARGV}dari dalam: macro(my_macro), function(my_func). Dan menggunakannya: set(a 123), my_macro("\\\${a}\\\\;\\\;;"), my_func(\${a}\\;\;;). Anda akan menemukan bahwa Anda harus melarikan diri ganda semua $, \ , ;untuk benar lulus seluruh string tidak berubah dengan perintah bersarang. Ini sebenarnya di cmake 3.14+.
Andry

Jawaban:

95

Saya menulis kode contoh di bawah ini:

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

dan hasilnya adalah:

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

Jadi tampaknya argdiberi nilai varsaat memanggil Foodan ${arg}hanya string yang diganti dengan ${var}saat memanggil Moo.

Jadi menurut saya kedua kutipan di atas sangat mudah membuat orang bingung, walaupun dalam dokumen resminya juga disebutkan bahwa :

Perhatikan bahwa parameter makro dan nilai seperti ARGN bukanlah variabel dalam pengertian CMake biasa. Mereka adalah penggantian string seperti yang dilakukan preprosesor C dengan makro. Jika Anda menginginkan variabel CMake yang benar dan / atau kontrol cakupan CMake yang lebih baik, Anda harus melihat perintah fungsi.

Yantao Xie
sumber
Saya sudah lupa itu, tapi saya pikir itu mungkin.
Yantao Xie
2
@robert Menjawab pertanyaan Anda sendiri dengan segera diizinkan sesuai dengan Pusat Bantuan (terutama jika pertanyaan itu bagus, pertanyaan non-duplikat yang menarik bagi orang lain). Ini untuk membantu SO menjadi basis pengetahuan yang lebih baik. Apakah Anda membaca postingan blog yang tertaut dengan topik Pusat Bantuan itu? stackoverflow.blog/2011/07/01/…
Emile Cormier
1
@robert Saya hanya menyampaikan pendapat pendiri SO sendiri tentang praktik ini. Ambillah dengan dia. ;-)
Emile Cormier
2
Menjalankan contoh seperti ini dengan cmake --trace-expandmencerahkan
MarcH
1
Harap pertimbangkan untuk menambahkan perintah berikut setelah setiap panggilan: message("# arg in main scope = '${arg}'")+ memanggil fungsi sebelum makro.
MarcH
34

Dengan kata lain, fungsi mendorong dan memunculkan cakupan variabel baru (variabel yang dibuat dan diubah hanya ada di fungsi), makro tidak. Namun, Anda dapat mengganti perilaku default fungsi dengan PARENT_SCOPEparameter setperintah.

robert
sumber
8

Dokumentasi cmake yang Anda kutip sangat menyesatkan sehingga pada dasarnya salah. Ini harus diklarifikasi / diperbaiki seperti ini:

  • makro: ketika dipanggil, perintah yang direkam di makro pertama-tama dimodifikasi sebelum apa pun dijalankan dengan mengganti parameter formal ($ {arg1}) dengan argumen yang diteruskan.

cmake --trace-expand menunjukkan dengan tepat apa yang terjadi.

Dokumen cmake 3.13.3 tidak berubah dibandingkan dengan 2.8.12 sehubungan dengan ini.

Maret
sumber
3

The ekspansi makro, dijawab oleh Yantao Xie benar-benar membuka mata saya!

Saya juga menemukan tutorial di bawah ini dilengkapi dengan beberapa contoh konkret, yang membantu untuk memahami konsep ruang lingkup variabel.

Dikutip dari Learn cmake dalam 15 menit :

Di CMake, Anda dapat menggunakan sepasang function/ endfunctionperintah untuk menentukan fungsi. Ini salah satu yang menggandakan nilai numerik dari argumennya, lalu mencetak hasilnya:

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

doubleIt("4")                           # Prints: 8

Fungsi berjalan dalam cakupannya sendiri. Tak satu pun dari variabel yang ditentukan dalam suatu fungsi mencemari cakupan pemanggil. Jika Anda ingin mengembalikan nilai, Anda dapat mengirimkan nama variabel ke fungsi Anda, lalu panggil setperintah dengan argumen khusus 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

Demikian pula, sepasang macro/ endmacroperintah mendefinisikan makro. Tidak seperti fungsi, makro berjalan dalam cakupan yang sama dengan pemanggilnya. Oleh karena itu, semua variabel yang ditentukan di dalam makro ditetapkan dalam cakupan pemanggil. Kita dapat mengganti fungsi sebelumnya dengan yang berikut ini:

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

Baik fungsi maupun makro menerima sejumlah argumen yang berubah-ubah. Argumen tanpa nama diekspos ke fungsi sebagai daftar, melalui variabel khusus bernama ARGN.

Berikut adalah fungsi yang menggandakan setiap argumen yang diterimanya, mencetak masing-masing pada baris terpisah:

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
sumber
3

Perbedaan penting lainnya antara function()dan macro()adalah perilaku return().

Dari dokumentasi cmake return () :

Perhatikan bahwa makro, tidak seperti fungsi, diperluas pada tempatnya dan oleh karena itu tidak dapat menangani return ().

Jadi karena itu diperluas pada tempatnya, macro()itu kembali dari pemanggil. Sementara dalam suatu fungsi, ia hanya keluar darifunction()

Contoh:

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
sumber
0

Mereka berbeda dalam hal perilakunya terkait ruang lingkup variabel. Anda dapat berpikir bahwa fungsi membuat cakupan baru dan menyalin untuk variabel, tetapi makro tidak tetapi berbagi ruang lingkup dengan induk (tempat mereka dipanggil).

Untuk detail lebih lanjut: https://levelup.gitconnected.com/cmake-variable-scope-f062833581b7

rjhcnf.dll
sumber
Dalam hal apa perilaku mereka berbeda? Tanpa detail lebih lanjut, ini tidak benar-benar menjawab pertanyaan, kecuali dengan mengikuti tautan. Juga, Anda telah memposting teks dan link yang sama ini tiga kali malam ini; jika jawaban yang sama menjawab beberapa pertanyaan, pertimbangkan untuk menandainya sebagai duplikat.
Jeremy Caney
Saya baru saja menambahkan lebih banyak deskripsi dan menambahkan tautan sebagai referensi. Saya menghapus jawaban lain.
rjhcnf