Як макрос лямбда створює лямбда?


20

Я знайшов цей фрагмент коду на GitHub, але не зовсім його зрозумів:

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

Тоді:

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

Що роблять підкреслення всередині #defineта як він повертає функцію вказівника?


7
Ви спробували просто розширити макрос (наприклад, з gcc -E), щоб побачити, що він робить?
Марно

5
Будь ласка, подивіться на розширення godbolt.org/z/C5TLWj Хоча результат зрозуміти непросто
Євген Ш.

2
Я припускаю, що ви знаєте, базуючись на коментарях місця, де ви отримали цей код, але це покладається на розширення GCC для вкладених функцій.
Томас Джагер

4
@EugeneSh. Це ініціалізація покажчика функції за допомогою вкладених функцій GCC. Оригінальний код звідси . Цей проект поділився сьогодні в новинах Hacker News.
Томас Джагер

4
@EugeneSh. Це поєднання двох розширень GCC: вкладених функцій та складених операторів у виразах . Вкладена функція з'являється всередині складеного оператора.
interjay

Відповіді:


10

Використовуючи цей макрос,

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

розширюється до:

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

У фігурних фігурних дужках для вбудованої функції GCC створена функція, яка виконує потрібну операцію. У межах внутрішньої сфери вона має назву _.

Потім, як зазначає interjay, використовуються вирази заяви GCC . Ефективно функція _призначається вказівнику max.

Якщо такий макрос не використовується, він може бути записаний інакше і використовуватись як:

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

У цьому прикладі коду можна порівняти три методи .


Макрос нічого не робить, оскільки не
збиратиметься

@P__J__ Він компілює ideone.com/T5FLXb
Євген Ш.

@P__J__ Я додав приклад до кінця своєї відповіді, який також показує, що цей макрос використовується.
Томас Джагер

Чому ви не можете зробити це max(4, -30);замість цього apply_binary_op(max, 4, -30);?
СС Енн

1
"Складені заяви у виразах" називаються "виразами висловлювань". Заяви, які мають значення, які можуть (наприклад) бути присвоєні чомусь.
Пітер Кордес

7

Це називається виразом заяви і створює "лямбда" (або вкладену функцію ) і повертає на нього вказівник. Це специфічно для GNU C

Макрос розгортається до:

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

В _кінці, як а return.

Підкреслення - це фактично назва функції, яка створюється та "повертається". Він використовується, тому що це нечасто використовуваний ідентифікатор (з поважних причин; _цілком можливо, найменш можливий описовий ідентифікатор).

Причина, за якою використовується вираз заяви _, не буде визначена після того, як область виразу виразу буде закрита.

Отже, переглядаючи макрос:

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

ret_type- тип повернення "лямбда". _- це назва функції, яка використовується всередині неї, оскільки це нечасте ідентифікатор. _bodyскладається з аргументів і тіла функції. Трейлінг _"повертає" лямбда ".

Цей код можна знайти в Let’s Destroy C (що є відповідною назвою). Ви не повинні його використовувати. Це змусить ваш код працювати лише на компіляторах, які підтримують розширення GNU C. Замість цього просто напишіть функцію або макрос.

Якщо ви багато використовуєте такі конструкції або хочете отримати більше функцій, пропоную використовувати C ++. За допомогою C ++ ви можете зробити щось подібне до цього і мати портативний код.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.