1

タイトルが既に述べているように、ネストされた関数を宣言し、その関数へのポインターを返そうとしています。この関数 'not' が元の関数の否定を返す新しい関数ポインタを返すようにしたい。

ここに私が持っているものがあります:

someType not( someType original ) {
    int isNot( ListEntry* entry ) {
        return !original( entry );
    }

    someType resultFunc = calloc( 1024, 1 );
    memcpy( resultFunc, &isNot, 1024 );

    return resultFunc;
}

someType は次のように定義されます。

typedef int(*someType)(ListEntry* entry)
4

4 に答える 4

19

スティーブ、あなたは C 関数とは何かについて完全に間違ったメンタル モデルを持っています。

someType resultFunc = calloc( 1024, 1 );
memcpy( resultFunc, &isNot, 1024 );

あなたのコード フラグメントから、関数のコンパイル済みコードをメモリ ブロックにコピーして再利用できると考えていると推測できます。この種のことは Lisp のにおいがしますが、Lisp でさえそのようにしないでください。

実際、「&isNot」と言うと、関数へのポインタが得られます。ポインタが指しているメモリをコピーするのは逆効果です。実行可能ファイルをメモリにロードしたときにメモリが初期化され、変更されていません。いずれにせよ、someFunc() を書き込むとコア ダンプが発生します。これは、someFunc の背後にあるヒープ メモリを実行できないためです。これにより、あらゆる種類のウイルスから保護されます。

C でのクロージャの実装を期待しているようです。その実装は単に存在しません。Lisp や Perl、Ruby とは異なり、C ではスタック フレームの要素を保持することはできません。一部のコンパイラではネストされた関数が許可されていても、それらの関数内から非グローバル変数を参照することはできないと確信しています。クロージャーを閉じるのは、実際には状態を格納し、operator() を実装する C++ オブジェクトですが、これはまったく異なるアプローチであり、手動で行う必要があります。

更新:ここに GCC ドキュメントの関連部分があります。「しかし、この手法は、含まれている関数 (この例ではハック) が終了しない場合にのみ機能します。」を探します。

于 2008-09-02T21:47:07.957 に答える
1

あなたはあなたが望む方法でこれを行うことができないでしょう。いくつかの代替オプションがあります。

マクロを使用できます。

#define FN_NOT(F) !F
#define notSomeFunc FN_NOT(someFunc)
...
x = notSomeFunc(entry);

しかし、否定された関数を関数ポインターを受け取る他の関数に渡して、それが機能しないようにしたかったのではないかと思います。

インターフェースを変更して、いくつかの追加情報を受け入れることができます。

struct closure {
  void *env;
  int (*f)(struct closure* extra, ListEntry*);
};

static int isNot(struct closure* extra, ListEntry *entry) {
  someType original = extra->env;
  return !original(entry);
}

struct closure not(someType original) {
   closure rv;
   rv.env = original;
   rv.f = &isNot;
   return rv;
}

そしてそれを次のように使用します:

struct closure inverse_fn;
inverse_fn = not( &fn );
if( inverse_fn.f(&inverse_fn, entry) ) {
    ...
}

実行時のJITing関数など、他にも試すことができるものがありますが、そのような手法はプラットフォームとアーキテクチャに依存します。このソリューションは厄介ですが、純粋なCでポータブルです。

于 2009-02-08T16:06:38.300 に答える
0

GCCを使用しています。

フラグを使用して、ネストされた関数をオンにできます。

-fnested-functions

コンパイルするとき。

于 2008-09-02T20:28:00.407 に答える
0

また、Cでネストされた関数について聞いたことはありませんが、gccがそれをサポートしている場合、これは期待どおりに機能しません。isNotのマシン命令をコピーするだけで、「not」が呼び出されたときの「original」の実際の値は含まれません。

C ++クラスを使用して、「original」の値で初期化し、「not」からこのクラスのインスタンスを返すことができるポインターを格納する関数オブジェクトを実装する必要があります。

于 2008-09-02T20:42:34.850 に答える