Bagaimana makro lambda membuat lambda?

20

Saya menemukan kode ini di GitHub tetapi tidak begitu memahaminya:

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

Kemudian:

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

Apa yang garis bawah lakukan di dalam #definedan bagaimana cara mengembalikan pointer fungsi?

Kish
sumber
7
Sudahkah Anda mencoba memperluas makro (mis. Dengan gcc -E) untuk melihat fungsinya?
berguna
5
Silakan lihat ekspansi godbolt.org/z/C5TLWj Namun hasilnya tidak mudah dipahami
Eugene Sh.
2
Saya berasumsi Anda tahu berdasarkan komentar di sekitar tempat Anda mendapatkan kode ini, tetapi ini bergantung pada ekstensi GCC untuk fungsi bersarang.
Thomas Jager
4
@EugeneSh. Ini menginisialisasi penunjuk fungsi dengan menggunakan fungsi bersarang GCC. Kode asli berasal dari sini . Proyek ini dibagikan di Hacker News hari ini.
Thomas Jager
4
@EugeneSh. Ini kombinasi dari dua ekstensi GCC: fungsi bersarang, dan pernyataan majemuk dalam ekspresi . Fungsi bersarang muncul di dalam pernyataan gabungan.
interjay

Jawaban:

10

Menggunakan makro ini,

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

memperluas ke:

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

Dalam kurung kurawal, ini menggunakan Nested Functions GCC untuk membuat fungsi yang melakukan operasi yang diinginkan. Di dalam lingkup batin, ia memiliki nama _.

Kemudian, seperti dicatat oleh interjay, Pernyataan Ekspresi GCC digunakan. Secara efektif, fungsi _ditugaskan ke pointer max.

Jika makro seperti itu tidak digunakan, ini bisa ditulis secara berbeda dan digunakan sebagai:

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

Tiga metode dapat dibandingkan dalam contoh kode ini .

Thomas Jager
sumber
Makro tidak melakukan apa-apa karena tidak akan dikompilasi dalam gcc
P__J__
@ P__J__ Ini mengkompilasi ideone.com/T5FLXb
Eugene Sh.
@ P__J__ Saya telah menambahkan contoh di akhir jawaban saya yang juga menunjukkan makro ini sedang digunakan.
Thomas Jager
Mengapa Anda tidak bisa melakukan max(4, -30);bukan apply_binary_op(max, 4, -30);?
SS Anne
1
"Pernyataan Majemuk dalam Ekspresi" disebut "ekspresi pernyataan". Pernyataan yang memiliki nilai yang dapat (misalnya) ditetapkan untuk sesuatu.
Peter Cordes
7

Ini disebut pernyataan pernyataan dan membuat "lambda" (atau fungsi bersarang ) dan mengembalikan pointer ke sana. Ini adalah GNU C-specific.

Makro diperluas ke:

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

Pada _akhirnya seperti return.

Garis bawah sebenarnya adalah nama fungsi yang dibuat dan "dikembalikan". Ini digunakan karena ini adalah pengidentifikasi yang tidak biasa digunakan (untuk alasan yang baik; _sangat mungkin pengidentifikasi deskriptif paling tidak mungkin).

Alasan ekspresi pernyataan digunakan adalah sehingga _tidak akan ditentukan setelah ruang lingkup pernyataan pernyataan keluar.

Jadi, melalui makro:

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

ret_typeadalah jenis kembalinya "lambda". _adalah nama fungsi yang digunakan di dalamnya karena itu adalah nama pengidentifikasi yang tidak umum. _bodyterdiri dari argumen dan tubuh fungsi. Trailing _"mengembalikan" "lambda".

Kode ini ditemukan di Let's Destroy C (yang merupakan nama yang sesuai). Anda seharusnya tidak menggunakannya. Ini akan membuat kode Anda hanya berfungsi pada kompiler yang mendukung ekstensi GNU C. Sebaliknya, cukup tulis fungsi atau makro.

Jika Anda menggunakan konstruksi seperti ini banyak atau menginginkan lebih banyak fitur, saya sarankan menggunakan C ++. Dengan C ++ Anda dapat melakukan sesuatu yang mirip dengan ini dan memiliki kode portabel.

SS Anne
sumber