W jaki sposób makro lambda tworzy lambda?

20

Znalazłem ten fragment kodu na GitHub, ale nie do końca go zrozumiałem:

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

Następnie:

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

Co robią podkreślenia wewnątrz #definei jak zwraca wskaźnik funkcji?

Kisz
źródło
7
Czy próbowałeś właśnie rozszerzyć makro (np. Za pomocą gcc -E), aby zobaczyć, co on robi?
Bezużyteczne
5
Proszę spojrzeć na rozszerzenie godbolt.org/z/C5TLWj Wynik nie jest jednak łatwy do zrozumienia
Eugene Sh.
2
Zakładam, że wiesz na podstawie komentarzy wokół miejsca, w którym masz ten kod, ale to zależy od rozszerzeń GCC dla zagnieżdżonych funkcji.
Thomas Jager
4
@EugeneSh. Inicjuje wskaźnik funkcji za pomocą zagnieżdżonych funkcji GCC. Oryginalny kod jest stąd . Ten projekt został udostępniony dzisiaj w Hacker News.
Thomas Jager
4
@EugeneSh. Jest to kombinacja dwóch rozszerzeń GCC: funkcji zagnieżdżonych i instrukcji złożonych w wyrażeniach . Zagnieżdżona funkcja pojawia się w instrukcji złożonej.
interjay

Odpowiedzi:

10

Za pomocą tego makra

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

rozwija się do:

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

W nawiasach klamrowych korzysta z funkcji zagnieżdżonych GCC, aby utworzyć funkcję, która wykonuje żądaną operację. W zakresie wewnętrznym ma nazwę _.

Następnie, jak zauważył interjay, używane są wyrażenia instrukcji GCC . W efekcie funkcja _jest przypisana do wskaźnika max.

Jeśli takie makro nie jest używane, można je zapisać inaczej i użyć jako:

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

Trzy metody można porównać w tym przykładzie kodu .

Thomas Jager
źródło
Makro nic nie robi, ponieważ nie skompiluje się w gcc
P__J__
@P__J__ Kompiluje ideone.com/T5FLXb
Eugene Sh.
@P__J__ Na końcu mojej odpowiedzi dodałem przykład, który pokazuje również, że to makro jest używane.
Thomas Jager
Dlaczego nie możesz max(4, -30);tego zrobić zamiast apply_binary_op(max, 4, -30);?
SS Anne
1
„Instrukcje złożone w wyrażeniach” nazywane są „wyrażeniami instrukcji”. Instrukcje, które mają wartość, którą można (np.) Przypisać do czegoś.
Peter Cordes
7

Nazywa się to wyrażeniem instrukcji i tworzy „lambda” (lub funkcję zagnieżdżoną ) i zwraca do niej wskaźnik. Jest to specyficzne dla GNU C.

Makro rozwija się do:

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

Na _końcu jest jak return.

Podkreślenie to tak naprawdę nazwa funkcji, która została utworzona i „zwrócona”. Jest używany, ponieważ jest to rzadko używany identyfikator (nie bez powodu; _jest to możliwie najmniej opisowy identyfikator).

Powodem użycia wyrażenia jest to, _że nie zostanie zdefiniowane po zakończeniu zakresu wyrażenia wyrażenia.

Przeglądając makro:

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

ret_typejest typem zwracanym „lambda”. _to nazwa funkcji użytej w nim, ponieważ jest to rzadka nazwa identyfikatora. _bodyskłada się z argumentów i treści funkcji. Końcowy _„zwraca” „lambda”.

Ten kod znajduje się w Let's Destroy C (która jest odpowiednią nazwą). Nie powinieneś go używać. Sprawi, że Twój kod będzie działał tylko na kompilatorach obsługujących rozszerzenia GNU C. Zamiast tego po prostu napisz funkcję lub makro.

Jeśli często używasz takich konstrukcji lub chcesz więcej funkcji, sugeruję użycie C ++. Z C ++ możesz zrobić coś podobnego i mieć przenośny kod.

SS Anne
źródło