CMake'de İşlev ve Makro


90

CMake 2.8.12'nin resmi belgesi şunları söylüyor:macro

Çağrıldığında, makroya kaydedilen komutlar önce biçimsel parametrelerin ($ {arg1}) geçirilen argümanlarla değiştirilmesiyle değiştirilir ve ardından normal komutlar olarak çağrılır.

ve hakkında function

Çağrıldığında, işleve kaydedilen komutlar önce biçimsel parametrelerin ($ {arg1}) geçirilen argümanlarla değiştirilmesiyle değiştirilir ve ardından normal komutlar olarak çağrılır.

Açıkçası, iki alıntı neredeyse aynı ama kafamı karıştırıyor. Tıpkı makro gibi bir işlevi çağırırken ilk başta parametreleri değiştirir mi?


8
Arasında en az bir diğer önemli de olsa oldukça belirgin bir fark vardır functionve macrosemantik: return()Bir kullanıldığında: macro, makro gelen ancak arama işlevinden döndürmez.
Joachim W

1
Bir diğer önemli not, bir makronun, bir işlev yalnızca bir olduğunda, bağımsız değişkenler üzerinde iki geçişli genişleme aşamasına sahip olmasıdır. Bu makro ve işlevi oluşturmaya çalışın ve ${ARGV}içeriden yazdırın : macro(my_macro), function(my_func). Ve bunları kullanmak: set(a 123), my_macro("\\\${a}\\\\;\\\;;"), my_func(\${a}\\;\;;). Sen bütün çift kaçış zorunda olduğunu göreceksiniz $, \ , ;düzgün iç içe komutlara değişmeden bütün dizeyi geçmek. Bu cmake 3.14+,.
Andry

Yanıtlar:


95

Aşağıda örnek bir kod yazdım:

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

ve çıktı:

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

Öyle görünüyor argdeğerini atanır varçağrılırken Foove ${arg}değiştirilir sadece dizedir ${var}çağrılırken Moo.

Bu yüzden yukarıdaki iki alıntıyı karıştırmanın çok kolay olduğunu düşünüyorum, ancak resmi belgeler de şunu söylüyor :

O Not parametreleri makro ve değerler böyle argn her zamanki CKağıt anlamda değişkenleri değillerdir. Bunlar, C ön işlemcisinin bir makroyla yapacağı gibi dize değiştirmeleridir. Gerçek CMake değişkenleri ve / veya daha iyi CMake kapsam kontrolü istiyorsanız, fonksiyon komutuna bakmalısınız.


Bunu unuttum ama sanırım olabilir.
Yantao Xie

2
@robert Yardım Merkezine göre kendi sorunuzu hemen yanıtlamanıza izin verilir (özellikle başkalarını ilgilendiren iyi, yinelenmeyen bir soruysa). Bu, SO'nun daha iyi bir bilgi tabanı haline gelmesine yardımcı olmak içindir. Bu Yardım Merkezi konusuna bağlantısı verilen blog gönderisini okudunuz mu? stackoverflow.blog/2011/07/01/…
Emile Cormier

1
@robert SO'nun kurucusunun bu uygulama hakkında ne düşündüğünü aktarıyorum. Onunla konuş. ;-)
Emile Cormier

2
Bunun gibi örnekleri çalıştırmak cmake --trace-expandaydınlatıcı
MarcH

1
Lütfen her aramadan sonra aşağıdaki komutu eklemeyi düşünün: message("# arg in main scope = '${arg}'")+ makrodan önce işlevi çağırma.
MarcH

34

Diğer bir deyişle, fonksiyon yeni değişken kapsamını iter ve açar (yaratılan ve değiştirilen değişkenler sadece fonksiyonda bulunur), makro içermez. Ancak, işlevin varsayılan davranışını komutun PARENT_SCOPEparametresiyle geçersiz kılabilirsiniz set.


8

Alıntı yaptığınız cmake belgeleri o kadar yanıltıcı ki temelde yanlış. Şu şekilde netleştirilmeli / düzeltilmelidir:

  • makro: çağrıldığında, makroya kaydedilen komutların tümü, herhangi biri çalıştırılmadan önce biçimsel parametrelerin ($ {arg1}) geçirilen argümanlarla değiştirilmesiyle değiştirilir.

cmake --trace-expand tam olarak ne olduğunu gösterir.

Cmake 3.13.3 belgesi buna göre 2.8.12'ye kıyasla değişmedi.


3

Yantao Xie tarafından cevap makro genişleme, gerçekten gözlerimi açar!

Ayrıca, aşağıdaki öğreticinin değişken kapsam kavramını anlamaya yardımcı olacak bazı somut örneklerle birlikte geldiğini gördüm.

Learn cmake'den 15 dakikada alıntı :

CMake'de, bir işlevi tanımlamak için bir çift function/ endfunctionkomut kullanabilirsiniz. İşte argümanının sayısal değerini iki katına çıkaran ve ardından sonucu yazdıran:

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

doubleIt("4")                           # Prints: 8

Fonksiyonlar kendi kapsamlarında çalışır. Bir işlevde tanımlanan değişkenlerin hiçbiri, arayanın kapsamını kirletmez. Bir değer döndürmek istiyorsanız, bir değişkenin adını işlevinize iletebilir, ardından setözel bağımsız değişkenle komutu çağırabilirsiniz 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

Benzer şekilde, bir çift macro/ endmacrokomut bir makroyu tanımlar. Fonksiyonların aksine, makrolar çağıranla aynı kapsamda çalışır. Bu nedenle, bir makro içinde tanımlanan tüm değişkenler, arayanın kapsamında ayarlanır. Önceki işlevi aşağıdakilerle değiştirebiliriz:

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

Hem işlevler hem de makrolar rastgele sayıda bağımsız değişken kabul eder. Adsız argümanlar, adı verilen özel bir değişken aracılığıyla işleve bir liste olarak sunulur.ARGN .

İşte aldığı her argümanı ikiye katlayan, her birini ayrı bir satıra yazdıran bir işlev:

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

Arasındaki başka dikkate değer bir fark function()ve macro()bir davranıştır return().

Gönderen dönüş cmake belgelerinde () :

Bir makronun, bir işlevden farklı olarak yerinde genişletildiğini ve bu nedenle return () işlevini işleyemeyeceğini unutmayın.

Yani yerinde genişletildiği macro()için, arayan kişiden geri döner. Bir işlev içindeyken, yalnızcafunction()

Misal:

macro(my_macro)
    return()
endmacro()

function(my_function)
    return()
endfunction()

my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.