Làm thế nào để macro lambda tạo ra lambda?


20

Tôi đã tìm thấy đoạn mã này trên GitHub nhưng không hiểu lắm về nó:

#define lambda(ret_type, _body) ({ ret_type _ _body _; })

Sau đó:

int (*max)(int, int) = lambda(int,
                             (int x, int y) {
                                 return x > y ? x : y;
                             });

int max_value = max(1, 2);
// max_value is 2

Các dấu gạch dưới đang làm gì bên trong #definevà làm thế nào để nó trả về một con trỏ hàm?


7
Bạn đã thử mở rộng macro (ví dụ: với gcc -E) để xem nó làm gì chưa?
Vô dụng

5
Vui lòng xem phần mở rộng godbolt.org/z/C5TLWj Kết quả không dễ hiểu mặc dù
Eugene Sh.

2
Tôi giả sử bạn biết dựa trên các nhận xét xung quanh vị trí bạn nhận được mã này, nhưng điều này phụ thuộc vào các tiện ích mở rộng GCC cho các hàm lồng nhau.
Thomas Jager

4
@EugeneSh. Đó là khởi tạo một con trỏ hàm bằng cách sử dụng các hàm lồng nhau của GCC. Mã ban đầu là từ đây . Dự án này đã được chia sẻ trên Hacker News ngày hôm nay.
Thomas Jager

4
@EugeneSh. Đó là sự kết hợp của hai phần mở rộng GCC: hàm lồng nhau và câu lệnh ghép trong biểu thức . Hàm lồng nhau xuất hiện bên trong câu lệnh ghép.
interjay

Câu trả lời:


10

Sử dụng macro này,

int (*max)(int, int) = lambda(int,
                             (int x, int y) {
                                 return x > y ? x : y;
                             });

mở rộng tới:

int (*max)(int, int) = ({
    int _ (int x, int y) { return x > y ? x : y; }
    _;
});

Trong các dấu ngoặc nhọn, hàm này sử dụng Hàm lồng nhau của GCC để tạo một hàm thực hiện thao tác mong muốn. Trong phạm vi bên trong, nó có tên _.

Sau đó, như được lưu ý bởi interjay, Biểu thức tuyên bố của GCC được sử dụng. Có hiệu quả, chức năng _được gán cho con trỏ max.

Nếu một macro như vậy không được sử dụng, điều này có thể được viết khác và được sử dụng như:

int val1 = 4;
int val2 = -30;

int perform_operation(int (*op)(int, int)) {
    int new_val = op(val1, val2);
    val1 = val2;
    val2 = new_val;
    return new_val;
}

int enclosing_function (void) {
    // Create max "lambda"
    int (*max)(int, int);
    {
        // Curly braces limit the scope of _
        int _ (int x, int y) { return x > y ? x : y; }
        max = _;
    }

    return perform_operation(max);
}

Ba phương thức có thể được so sánh trong ví dụ mã này .


Macro không làm gì vì nó sẽ không biên dịch trong gcc
P__J__

@P__J__ Nó biên dịch ideone.com/T5FLXb
Eugene Sh.

@P__J__ Tôi đã thêm một ví dụ vào cuối câu trả lời của mình cũng cho thấy macro này đang được sử dụng.
Thomas Jager

Tại sao bạn không thể làm max(4, -30);thay vì apply_binary_op(max, 4, -30);?
SS Anne

1
"Báo cáo hợp chất trong biểu thức" được gọi là "biểu thức tuyên bố". Các câu lệnh có một giá trị có thể (ví dụ) được gán cho một cái gì đó.
Peter Cordes

7

Đây được gọi là biểu thức câu lệnh và tạo ra một "lambda" (hoặc hàm lồng nhau ) và trả về một con trỏ tới nó. Nó là đặc trưng của GNU C.

Macro mở rộng thành:

int (*max)(int, int) = ({ int _ (int x, int y) { return x > y ? x : y; } _; })

Các _cuối cùng giống như một return.

Dấu gạch dưới thực sự là tên của hàm được tạo và "trả lại". Nó được sử dụng bởi vì nó là một định danh được sử dụng không phổ biến (vì lý do chính đáng; _hoàn toàn có thể là định danh mô tả ít nhất có thể).

Lý do biểu thức câu lệnh được sử dụng là vì vậy _sẽ không được xác định sau khi phạm vi của biểu thức câu lệnh thoát.

Vì vậy, đi qua vĩ mô:

#define lambda(ret_type, _body) ({ ret_type _ _body _; })

ret_typelà kiểu trả về của "lambda". _là tên của hàm được sử dụng bên trong nó bởi vì nó là tên định danh không phổ biến. _bodybao gồm các đối số và cơ thể của chức năng. Các dấu _"trả lại" "lambda".

Mã này được tìm thấy tại Let Phá hủy C (là một tên thích hợp). Bạn không nên sử dụng nó. Nó sẽ làm cho mã của bạn chỉ hoạt động trên các trình biên dịch hỗ trợ các phần mở rộng GNU C. Thay vào đó, chỉ cần viết một hàm hoặc macro.

Nếu bạn sử dụng các cấu trúc như thế này rất nhiều hoặc muốn có nhiều tính năng hơn, tôi khuyên bạn nên sử dụng C ++. Với C ++, bạn có thể làm một cái gì đó tương tự như thế này có mã di động.

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.