Как лямбда-макрос создает лямбду?

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

В этом примере кода можно сравнить три метода .

Томас Ягер
источник
Макрос ничего не делает, так как он не скомпилируется в gcc
P__J__
@P__J__ Это компилирует ideone.com/T5FLXb
Евгений Ш.
@P__J__ Я добавил пример в конец моего ответа, который также показывает, что этот макрос используется.
Томас Ягер
Почему ты не можешь сделать max(4, -30);вместо apply_binary_op(max, 4, -30);?
SS Anne
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 ++ вы можете сделать что-то похожее на это и иметь переносимый код.

СС Энн
источник