In che modo la macro lambda crea una lambda?


20

Ho trovato questo pezzo di codice su GitHub ma non l'ho capito bene:

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

Poi:

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

Cosa stanno facendo i trattini bassi all'interno di #definee come restituisce un puntatore a funzione?


7
Hai provato ad espandere la macro (ad es. Con gcc -E) per vedere cosa fa?
Inutile il

5
Si prega di guardare l'espansione godbolt.org/z/C5TLWj Il risultato non è facile da capire però
Eugene Sh.

2
Presumo che tu lo sappia in base ai commenti sul punto in cui hai ottenuto questo codice, ma questo si basa sulle estensioni GCC per le funzioni nidificate.
Thomas Jager,

4
@EugeneSh. Sta inizializzando un puntatore a funzione usando le funzioni nidificate di GCC. Il codice originale è da qui . Questo progetto è stato condiviso oggi su Hacker News.
Thomas Jager,

4
@EugeneSh. È una combinazione di due estensioni GCC: funzioni nidificate e istruzioni composte nelle espressioni . La funzione nidificata viene visualizzata all'interno dell'istruzione composta.
interjay

Risposte:


10

Usando questa macro,

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

si espande in:

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

Nelle parentesi graffe, questo utilizza le funzioni annidate di GCC per creare una funzione che esegue l'operazione desiderata. Nell'ambito interno, ha il nome _.

Quindi, come notato da interjay, vengono utilizzate le espressioni Statement di GCC . In effetti, la funzione _è assegnata al puntatore max.

Se tale macro non viene utilizzata, questa potrebbe essere scritta in modo diverso e utilizzata come:

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

I tre metodi possono essere confrontati in questo esempio di codice .


La macro non fa nulla in quanto non verrà compilata in gcc
P__J__

@P__J__ Compila ideone.com/T5FLXb
Eugene Sh.

@P__J__ Ho aggiunto un esempio alla fine della mia risposta che mostra anche questa macro in uso.
Thomas Jager,

Perché non puoi farlo max(4, -30);invece di apply_binary_op(max, 4, -30);?
SS Anne,

1
"Dichiarazioni composte in espressioni" sono chiamate "espressioni di istruzioni". Dichiarazioni che hanno un valore che può (es.) Essere assegnato a qualcosa.
Peter Cordes,

7

Questa si chiama espressione di istruzione e crea una "lambda" (o funzione nidificata ) e restituisce un puntatore ad essa. È specifico per GNU C.

La macro si espande in:

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

Alla _fine è come a return.

Il carattere di sottolineatura è in realtà il nome della funzione creata e "restituita". Viene utilizzato perché è un identificatore non utilizzato (per una buona ragione; _è probabilmente l'identificatore meno descrittivo possibile).

Il motivo per cui viene utilizzata _l'espressione dell'istruzione non sarà quindi definito dopo la chiusura dell'ambito dell'espressione dell'istruzione.

Quindi, passando attraverso la macro:

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

ret_typeè il tipo di ritorno del "lambda". _è il nome della funzione utilizzata al suo interno perché è un nome identificativo non comune. _bodyconsiste negli argomenti e nel corpo della funzione. Il finale _"restituisce" il "lambda".

Questo codice si trova in Let's Destroy C (che è un nome appropriato). Non dovresti usarlo. Farà funzionare il tuo codice solo su compilatori che supportano le estensioni GNU C. Invece, basta scrivere una funzione o una macro.

Se usi molto costrutti come questo o vuoi più funzionalità, ti suggerisco di usare C ++. Con C ++ puoi fare qualcosa di simile a questo e avere un codice portatile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.