ラムダマクロはどのようにラムダを作成しますか?


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拡張に依存しています。
Thomas Jager

4
@EugeneSh。GCCのネストされた関数を使用して、関数ポインターを初期化しています。元のコードはここからです。このプロジェクトは本日ハッカーニュースで共有されました。
Thomas Jager

4
@EugeneSh。これは、2つのGCC拡張機能の組み合わせです。ネストされた関数と、式の複合ステートメントです。ネストされた関数は、複合ステートメント内に表示されます。
インタージェイ

回答:


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

このコード例では、3つの方法を比較できます。


マクロはgccでコンパイルされないため、何もしません
P__J__


@P__J__このマクロが使用されていることも示す例を私の回答の最後に追加しました。
Thomas Jager

max(4, -30);代わりにできないのはなぜapply_binary_op(max, 4, -30);ですか?
SSアン

1
「式の複合ステートメント」は「ステートメント式」と呼ばれます。何かに割り当てることができる(たとえば)値を持つステートメント。
Peter Cordes

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 ++を使用すると、これ同様のことができ移植可能なコードを作成できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.