web-dev-qa-db-ja.com

単一の構造体を介して関数の引数を渡すメリット

同僚は、すべての関数がすべての実際のパラメーターを含む単一の構造体パラメーターを取る、ユーザー向けのAPIのスタイルを提唱し始めました。つまり、次の代わりに:

void fn(int foo, bool bar);

私たちはすべきです:

typedef struct {
    int foo;
    bool bar;
} fn_args_t;

void fn(fn_args_t args);

これまでにこのスタイルに出会ったことはありません。呼び出し元を更新する必要なく、後で新しいパラメーターを簡単に追加できると彼は主張しますが、引数(省略された既存の引数、または後で追加したい引数)を省略しやすくなることも心配です。ゼロ以外を渡します)。

このスタイルの名前はありますか?長所短所?それが以前に使用されたのを見たことがないのは悪い兆候のようです。

5
Michael Mrozek

要するに

このアプローチは、特定の状況では理にかなっています。ただし、すべての機能の体系的なアプローチとして使用するべきではありません。

長い間詳細に

長所

APIのいくつかの関数で繰り返し使用されるstructいくつかのパラメーターグループにグループ化することは非常に理にかなっています。このアプローチは非常に自然です。同じパラメーターを繰り返し使用すると、これらのパラメーターが何らかの形で関連していることが強く示唆されます。そしてstructはこの関係を具体化するだけです。

長所は、関数呼び出しを短くし、APIを覚えやすくし、さらに構造体コンポーネント間の関係についてユーザーに啓蒙することです。

標準ライブラリには、 asctime()mktime() などのいくつかの例があります。しかし、よく知られているAPIには他にも多くの例があり、構造体は他のパラメーターと一緒に使用されます(たとえば、WinAPIは、x、y座標またはPOINT構造体のいずれかを選択することがよくあります)。 。

短所

日常のプログラミングでは、言語の大きな進歩を忘れることがあります。 K&Rの初期の頃は、パラメーターの受け渡しでミスを犯すのは非常に簡単でした。関数を呼び出すときに、関数が同じ順序で同じパラメーターを使用すると想定して、スタック(またはレジスター)にプッシュされたパラメーターを呼び出す場合。呼び出し元がミスした場合、たとえば、パラメーターを間違った順序で使用した場合、またはパラメーターを忘れた場合、コンパイラーは文句を言わなかった。そのようなエラーを見つけるのがいかに厄介で難しいかをよく覚えています。

標準Cが後で関数プロトタイプとパラメーターチェックを導入した のとき、それは祝福でした!多くのばかげた過ちがすぐにコンパイラーによってキャッチされました。私にとっては、コンパイラのエラーメッセージのおかげで何百時間もの深夜のデバッグが回避されました。そしてチームに持って来られて、それはそれ以上ではないにしても数千時間です。

structの主な問題は、それだけです。これにより、呼び出し引数のチェックの効果が低下します。構造体を渡すと、コンパイラーは満足します。構造体メンバーの1つを初期化するのを忘れましたか?あなたは苦しむでしょう。さらに悪いことに、あなたのコードは セキュリティの問題 を作成するかもしれません。

このため、この手法は、リスクを上回る重要なメリットをもたらす場合にのみ使用する必要があります。これを体系的なアプローチとして使用しないでください。

7
Christophe

非常に一般的なテクニック。構造体の最後に簡単に機能を追加して、以前のバージョンが引き続き機能できるようにします(追加のメンバーを期待せず、構造体の最後であると思われるものを超えてアクセスできません)。

最初のメンバーをバージョン番号にして、構造体のサイズも示すことをお勧めします。または、最初のメンバーをバイト長にして、関数が処理する構造体のバージョンを認識できるようにします。

私はこのような多くの組み込みシステムドライバーとAPIを作成しました。他の人のコードを壊すことなく機能を追加するのは非常に簡単です。

1
GaryLa