Chức năng so với Macro trong CMake


89

Tài liệu chính thức của CMake 2.8.12 nói vềmacro

Khi nó được gọi, các lệnh được ghi trong macro lần đầu tiên được sửa đổi bằng cách thay thế các tham số chính thức ($ {arg1}) bằng các đối số được truyền vào, sau đó được gọi như các lệnh bình thường.

và về function

Khi nó được gọi, các lệnh được ghi trong hàm lần đầu tiên được sửa đổi bằng cách thay thế các tham số chính thức ($ {arg1}) bằng các đối số được truyền vào, sau đó được gọi như các lệnh bình thường.

Rõ ràng, hai trích dẫn gần như giống nhau nhưng làm tôi khó hiểu. Có thay thế các tham số lúc đầu khi gọi một hàm giống như macro không?


8
Có ít nhất một sự khác biệt quan trọng khác, mặc dù khá rõ ràng giữa functionmacro: ngữ nghĩa của return(): Khi được sử dụng trong a macro, bạn sẽ không trả về từ macro mà từ hàm gọi.
Joachim W

1
Một lưu ý quan trọng khác, là macro có giai đoạn mở rộng hai lần trên các đối số khi một hàm chỉ là một. Cố gắng tạo macro và hàm này và in ${ARGV}từ bên trong: macro(my_macro), function(my_func). Và sử dụng chúng: set(a 123), my_macro("\\\${a}\\\\;\\\;;"), my_func(\${a}\\;\;;). Bạn sẽ thấy rằng bạn phải kích đúp thoát tất cả các $, \ , ;để vượt qua đúng toàn bộ chuỗi không thay đổi để các lệnh lồng nhau. Điều này là thực tế trong cmake 3.14+.
Andry

Câu trả lời:


93

Tôi đã viết một mã mẫu bên dưới:

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

và đầu ra là:

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

Vì vậy, nó có vẻ như argđược gán giá trị varkhi gọi Foo${arg}chỉ là chuỗi được thay thế bằng ${var}khi gọi Moo.

Vì vậy, tôi nghĩ hai trích dẫn trên rất dễ làm cho người ta nhầm lẫn, mặc dù các tài liệu chính thức cũng nói rằng :

Lưu ý rằng các tham số của macro và các giá trị như ARGN không phải là các biến theo nghĩa CMake thông thường. Chúng là sự thay thế chuỗi giống như bộ tiền xử lý C sẽ làm với macro. Nếu bạn muốn các biến CMake thực sự và / hoặc kiểm soát phạm vi CMake tốt hơn, bạn nên xem lệnh hàm.


Tôi đã quên điều đó, nhưng tôi nghĩ có thể là như vậy.
Yantao Xie

2
@robert Việc trả lời câu hỏi của riêng bạn ngay lập tức được cho phép theo Trung tâm trợ giúp (đặc biệt nếu đó là một câu hỏi hay, không trùng lặp về lợi ích chung đối với người khác). Điều này là để giúp SO trở thành một nền tảng kiến ​​thức tốt hơn. Bạn đã đọc bài đăng trên blog được liên kết trong chủ đề Trung tâm trợ giúp đó chưa? stackoverflow.blog/2011/07/01/…
Emile Cormier

1
@robert Tôi chỉ chuyển tiếp những gì mà bản thân người sáng lập SO nghĩ về cách làm này. Mang nó với anh ta. ;-)
Emile Cormier

2
Chạy ví dụ như thế này với cmake --trace-expandlà làm sáng tỏ
March

1
Vui lòng xem xét thêm lệnh sau sau mỗi lần gọi: message("# arg in main scope = '${arg}'")+ gọi hàm trước macro.
MarcH

34

Nói cách khác, hàm đẩy và bật phạm vi biến mới (các biến được tạo và thay đổi chỉ tồn tại trong hàm), macro thì không. Tuy nhiên, bạn có thể ghi đè hành vi mặc định của hàm bằng PARENT_SCOPEtham số của setlệnh.


8

Tài liệu cmake bạn đã trích dẫn rất sai lệch về cơ bản là sai. Nó phải được làm rõ / sửa như thế này:

  • macro: khi nó được gọi, các lệnh được ghi trong macro trước hết đều được sửa đổi trước khi bất kỳ lệnh nào được chạy bằng cách thay thế các tham số chính thức ($ {arg1}) bằng các đối số được truyền vào.

cmake --trace-expand hiển thị chính xác những gì xảy ra.

Bản cmake 3.13.3 không thay đổi so với 2.8.12 về mặt này.


3

Một khác biệt đáng chú ý giữa function()macro()là hành vi của return().

Từ tài liệu cmake của return () :

Lưu ý rằng macro, không giống như một hàm, được mở rộng tại chỗ và do đó không thể xử lý hàm return ().

Vì vậy, bởi vì nó được mở rộng tại chỗ, macro()nó sẽ trả về từ người gọi. Khi ở trong một chức năng, nó chỉ thoát khỏifunction()

Thí dụ:

macro(my_macro)
    return()
endmacro()

function(my_function)
    return()
endfunction()

my_function()
message(hello) # is printed
my_macro()
message(hi) # is not printed

2

Việc mở rộng vĩ mô, được trả lời bởi Yantao Xie thực sự mở rộng tầm mắt của tôi!

Tôi cũng thấy hướng dẫn bên dưới đi kèm với một số ví dụ cụ thể, rất hữu ích để hiểu khái niệm phạm vi biến.

Trích dẫn từ Learn cmake trong 15 phút :

Trong CMake, bạn có thể sử dụng một cặp lệnh function/ endfunctionđể xác định một hàm. Đây là một trong những nhân đôi giá trị số của đối số của nó, sau đó in kết quả:

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

doubleIt("4")                           # Prints: 8

Các hàm chạy trong phạm vi riêng của chúng. Không có biến nào được xác định trong một hàm gây ô nhiễm phạm vi của người gọi. Nếu bạn muốn trả về một giá trị, bạn có thể chuyển tên của một biến vào hàm của mình, sau đó gọi setlệnh với đối số đặc biệt 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

Tương tự, một cặp lệnh macro/ endmacroxác định một macro. Không giống như các hàm, macro chạy trong cùng phạm vi với trình gọi của chúng. Do đó, tất cả các biến được xác định bên trong macro đều được đặt trong phạm vi của người gọi. Chúng ta có thể thay thế chức năng trước bằng chức năng sau:

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

Cả hàm và macro đều chấp nhận một số lượng đối số tùy ý. Các đối số không tên được hiển thị cho hàm dưới dạng danh sách, thông qua một biến đặc biệt có tên ARGN.

Đây là một hàm tăng gấp đôi mọi đối số mà nó nhận được, in từng đối số trên một dòng riêng biệt:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.