オブジェクト指向/機能的なマインドとC言語でのプログラミングを両立させようとしています。Cで動的ディスパッチを実現したいとしましょう。たとえば、実行するタスクのコレクションが欲しいとしましょう。実行したい関数のコレクションへのポインターを簡単に作成できます。ただし、実行されるタスクは、関数が作成されたときに存在する変数に依存し、それらを実行するものによって関数に渡されないとします。 JavaまたはC++では、これらの変数をキャプチャするオブジェクト(おそらくラムダ)を使用して、関数が実行中にそれらにアクセスできるようにします。しかし、関数ポインターは変数をキャプチャできないと聞きました標準Cでは、このデザインパターンをどのように実装すればよいでしょうか。
CS StackExchangeで 関数ポインターの観点から高次関数をエンコードする方法を説明しました 。基本的に、クロージャは環境へのvoid *
と関数ポインタのペアにすぎません。何かのようなもの:
struct IntToIntClosure {
void *environment;
int (*func)(void *, int);
}
たとえば、Linux非同期I/O APIでは、 sigevent
構造体がこれらのものをペアにします(「環境」はsigev_value
フィールドです)。
ただし、foo *
を配列として表示するときにfoo *
と長さを別々に渡すのが一般的であるように、実際にはこれらをデータ構造に一緒にパッケージ化せず、次のように渡すのが一般的です個別のパラメータ。コールバックを受け入れる多くのC APIは、通常はvoid *
である追加の「コンテキスト」パラメーターを受け取ります。このパラメーターは、呼び出されるたびに関数ポインターに渡されます。たとえば、glibcの on_exit
( atexit
と対比)。
これらの手法を使用すると、動的ディスパッチで高次関数とオブジェクトを簡単にエンコードできます。ただし、登録用の関数ポインターのみを受け入れるAPIがある場合、これを行うことはできません。*。事後の関数ポインターのどの呼び出しにどの環境を関連付ける必要があるかを知る方法はありません。この場合、グローバル変数または静的変数を使用して、付随する制限のある環境情報を、許可されている基本的に1つの環境にのみ渡す必要があります。このAPIを制御している場合、私はstronglyこの環境用の追加のパラメーターを(明示的にまたはstruct
)。
* 移植性を気にしない場合、これを回避するためのトリックがあります。関数ポインターを動的に生成できる場合(つまり、実行時にマシンコードを作成することにより)、目的の関数に追加のパラメーターを渡すスタブへの関数ポインターを作成できます。 これは、GHC HaskellがHaskell関数をCコールバックとして渡す方法です
Cではキャプチャの可能性はなく、関数は「作成」されませんが、実行可能イメージがメモリにロードされるため、存在します。
基本的に、やりたいことを行うには3つの方法があります。