web-dev-qa-db-ja.com

GCCステートメント式を使用した匿名関数

この質問はそれほど具体的ではありません。 それは本当に私自身のCの強化のためであり、他の人も同様に役立つと思います。

免責事項:多くの人が「FPを実行しようとしている場合、関数型言語を使用するだけです。」と応答する衝動があることを知っています。他のCライブラリ、およびより多くの大きな共有ライブラリ用のスペースがあまりなく、多くの言語ランタイムをサポートしていません。さらに、動的メモリ割り当ては問題外です。

私たちの多くは、ラムダ式のためのこの気の利いたCマクロを見てきました。

_#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})
_

また、使用例は次のとおりです。

_int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example
_

_gcc -std=c89 -E test.c_を使用すると、ラムダは次のように展開されます。

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

だから、これらは私の質問です:

  1. int(* X);は正確に何を宣言しますか?もちろん、int * X;は整数へのポインタですが、これら2つの違いは何ですか?

  2. 使い古されたマクロを見てみると、最終的な___fn___は一体何をするのでしょうか?テスト関数void test() { printf("hello"); } test;を書くと、すぐにエラーがスローされます。私はその構文を理解していません。

  3. これはデバッグにとって何を意味しますか? (これとgdbを試してみるつもりですが、他の人の経験や意見は素晴らしいでしょう)。これは静的アナライザーを台無しにしますか?

49
Bill VB

この宣言(ブロックスコープで):

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

はCではなく、有効なGNU C.

2つのgcc拡張を使用します。

  1. ネストされた関数
  2. ステートメント式

ネストされた関数(複合ステートメント内の関数の定義)とステートメント式({})(基本的に値を生成するブロック)はCでは許可されず、GNU C.

ステートメント式では、最後の式ステートメントは構造の値です。これが、ネストされた関数__fn__は、ステートメント式の最後に式ステートメントとして表示されます。関数指定子(__fn__は、通常の変換によって関数へのポインターに変換されます。これは、関数ポインターmaxを初期化するために使用される値です。

39
ouah

ラムダマクロは2つのファンキーな機能を活用します。まず、ネストされた関数を使用して、関数の本体を実際にdefineします(したがって、ラムダは実際には匿名ではなく、暗黙的な___fn___を使用します変数(ダブルアンダースコアの名前はコンパイラーのために予約されているため、他の名前に変更する必要があります。したがって、おそらく_yourapp__fn___のようなものが良いでしょう)。

このすべては、GCC複合ステートメント内で実行されます( http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs を参照)、その基本形式次のようになります:

_({ ...; retval; })
_

複合ステートメントの最後のステートメントは、宣言されたばかりの関数のアドレスです。これで、int (*max)(int,int)には単に複合ステートメントの値が割り当てられ、宣言された「匿名」関数へのポインターになりました。

マクロのデバッグはもちろん王室の痛みです。

_test;_ ..少なくともここでは、「異なるタイプのシンボルとして再宣言されたテスト」を取得する理由については、GCCが(役に立たない)式ではなく宣言として扱っていることを意味すると思います。型なし変数のデフォルトはintであり、関数としてtestをすでに宣言しているため(本質的にはvoid (*)(void))、それを取得します。

しかし、これは想像力の広がりによって移植性がありません。

6
Mark Nunberg

部分的な答え:興味のあるint(* X)ではありません。int(* X)(y、z)です。これは、(y、z)を取り、intを返すXという関数への関数ポインターです。

デバッグの場合、これは非常に困難です。ほとんどのデバッガーはマクロをトレースできません。ほとんどの場合、アセンブリをデバッグする必要があります。

1
Steve Rowe
  1. int (*max)(int, int)は、宣言する変数のタイプです。 intを返すmaxという名前の関数ポインターとして定義され、パラメーターとして2つのintを取ります。

  2. __fn__は関数名を指し、この場合は最大です。

  3. 答えがありません。プリプロセッサで実行した場合、ステップスルーできると思います。

0
gcochard