8

私は多くを生成するいくつかのコードに取り組んでいます

ignoring return value of ‘size_t fwrite(const void*, size_t, size_t, FILE*)’, declared with attribute warn_unused_result

g ++でコンパイルすると警告が表示され、多数の個別の順次fwritesの戻り値を実際に記録して処理するための最良のプログラミングパターンについて疑問に思っています(つまりfwrite、ループでは同じではありません)

現時点でコードが次のようになっているとします。

fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...

現在、次のようなことを考えていますが、ファイル ポインターをクリーンアップするのが難しい場合があります。

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) return someerrorcode;
// ... more code ...
if (fwrite (&foo, sizeof (foo), 1, fp) != 1) return someerrorcode;
// ... more code ...

このアプローチは、入れ子にするよりも明らかに優れていると思います。

if (fwrite (&blah, sizeof (blah), 1, fp) == 1) {
   // ... more code ...
   if (fwrite (&foo, sizeof (foo), 1, fp) == 1) {;
      // ... more code ...
   }
}

とはいえ、この種のベスト プラクティス パターンは既に確立されているのではないでしょうか?

もちろん、コンパイラの警告を取り除くためにこれを主に調べているので、戻り値をダミー変数に割り当てて無視することもできますが、最初は正しい方法で試してみたいと思います.

dummy = fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
dummy = fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...

更新:このコードは実際には g++ を使用してコンパイルされた c であるため、c++ タグを削除しました。そのため、コード ベースの残りの部分を維持するには、c ベースのソリューションが必要です。

4

13 に答える 13

10

goto に基づく貧しい人の C 例外処理 (実際、goto の唯一無二のインスタンスは有害ではありません):

int foo() {
    FILE * fp = fopen(...);
    ....

    /* Note: fwrite returns the number of elements written, not bytes! */
    if (fwrite (&blah, sizeof (blah), 1, fp) != 1) goto error1;

    ...

    if (fwrite (&foo, sizeof (foo), 1, fp) != 1) goto error2;

    ...

ok:
    /* Everything went fine */
    fclose(fp);
    return 0;

error1:
    /* Error case 1 */
    fclose(fp);
    return -1;

error2:
    /* Error case 2 */
    fclose(fp);
    return -2;
}

あなたはアイデアを得る。必要に応じて再構成します (単一/複数のリターン、単一のクリーンアップ、カスタム エラー メッセージなど)。私の経験からすると、これは最も一般的な C エラー処理パターンです。重要な点は、stdlib の戻りコードを決して無視しないことです。そうする正当な理由 (読みやすさなど) は十分ではありません。

于 2009-02-20T13:49:23.853 に答える
10

私はこれらの行に沿って何かをします:

FILE * file = fopen("foo", "wb");
if(!file) return FAILURE;

// assume failure by default
_Bool success = 0;

do
{
    if(!fwrite(&bar, sizeof(bar), 1, file))
        break;

    // [...]

    if(!fwrite(&baz, sizeof(baz), 1, file))
        break;

    // [...]

    success = 1;
} while(0);

fclose(file);

return success ? SUCCESS : FAILURE;

ちょっとした C99 マクロ マジックで

#define with(SUBJECT, FINALIZE, ...) do { \
    if(SUBJECT) do { __VA_ARGS__ } while(0); if(SUBJECT) FINALIZE; \
} while(0)

Jonathan Leffler によって提案された独自のエラー フラグの代わりに使用ferror()すると、次のように記述できます。

FILE * file = fopen("foo", "wb");
with(file, fclose(file),
{
    if(!fwrite(&bar, sizeof(bar), 1, file))
        break;

    // [...]

    if(!fwrite(&baz, sizeof(baz), 1, file))
        break;

    // [...]
});

return file && !ferror(file) ? SUCCESS : FAILURE;

io エラー以外のエラー条件がある場合でも、1 つ以上のエラー変数を使用してそれらを追跡する必要があります。

また、あなたのチェックsizeof(blah)は間違っています:fwrite()書き込まれたオブジェクトの数を返します!

于 2009-02-20T14:02:20.343 に答える
2

ラッパー関数を書くことができます

void new_fwrite(a, b, c, d) {
    if (fwrite (a, b, c, b) != b) 
       throw exception;
}

次に、fwrite へのすべての呼び出しを new_fwrite に置き換えます

于 2009-02-20T13:49:50.747 に答える
2

エラーを無視するのは悪い考えです。何も言わずに先に進むよりも、プログラムをクラッシュさせるなどのやっかいなことをして、少なくとも何かが間違っていたことがわかるようにする方がはるかに優れています。さらに優れているのは、優れたエラー チェックと回復です。

C++ を使用している場合は、FILE* の RAII ラッパーを作成して、常に閉じられるようにすることができます。アイデアについては std::auto_ptr を見てください。その後、いつでも便利なエラー コードを返したり、関数から返すことができます。また、例外をスローすることで、忘れたクリーンアップ項目について心配する必要がなくなります。

于 2009-02-20T13:50:08.593 に答える
0

次のように警告を削除できます。

(void) fwrite ( ,,,, );

fwrite() 呼び出しのいずれかが失敗した場合、主な質問に対処すると、出力がおそらく破損しているため、続行しても意味がないと思います。その場合、この C++ にタグを付けると、例外がスローされます。

于 2009-02-20T13:42:59.470 に答える
0

ネスティングも悪いし、複数回のリターンも良くありません。

私は次のパターンを使用していました:

#define SUCCESS (0)
#define FAIL    (-1)
int ret = SUCCESS;

if (!fwrite(...))
    ret = FAIL;
if (SUCCESS == ret) {
    do_something;
    do_something_more;
    if (!fwrite(...))
        ret = FAIL;
}
if (SUCCESS == ret)
    do_something;

return ret;

見栄えが悪いことはわかっていますが、単一のリターンポイントがあり、過度のネストがなく、保守が非常に簡単です。

于 2009-02-20T13:44:15.457 に答える
0

うーん...失敗した場合に書き込みを再試行し、おそらく最大再試行回数まで書き込みを再試行し、成功/失敗を返すラッパー関数を作成できます。

int safe_fwrite(FILE *file, const void *data, size_t nbytes, unsigned int retries);
void print_and_exit(const char *message);

次に、メインコードは次のように記述できます

#define RETRIES 5
if(!safe_fwrite(fp, &blah, sizeof blah, RETRIES))
  print_and_exit("Blah writing failed, aborting");
if(!safe_fwrite(fp, &foo, sizeof foo, RETRIES))
  print_and_exit("Foo writing failed, aborting");
于 2009-02-20T13:46:15.077 に答える
0

最初のソリューションは問題ないようです。通常、goto err;いくつかの一般的なクリーンアップ部分 (既知の位置への巻き戻しなど) が必要な場合があるため、 a の方が便利です。

GCC を静かにするには、次のようにします。

(void)fwrite (&blah, sizeof (blah), 1, fp);
(void)fwrite (&foo, sizeof (foo), 1, fp);
于 2009-02-20T13:46:42.720 に答える
0

このようなものはうまくいくでしょう

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) throw SomeException;

ポインターがクリーンアップされるのが心配な場合は、fwrite を実行する前に、ポインターを何らかの形式のスマート ポインターでラップできます。

スマート ポインターを使用したくない場合は、これで機能しますが、面倒なので、最初にスマート ポインター ルートを試してみます。

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) {
    //cleanup here
    throw SomeException;
}
于 2009-02-20T13:47:01.630 に答える
0

fwritefwrite() がエラー コードを返す場合は、何らかの Writer オブジェクトにラップして例外をスローしないのはなぜですか? コーディングしやすく、使いやすく、管理しやすい。もちろん、私見です。:)

于 2009-02-20T13:47:49.210 に答える
0

これに対する潜在的にエレガントな C ソリューションは、次のようなものになる可能性があります (警告 - テストされていない、コンパイルされていないコードが先にある):


  size_t written;
  int    ok = 1;
  size_t num_elements = x;
  ok = (fwrite(stuff, sizeof(data), num_elements, outfile) == num_elements);

  if (ok) {
    ... do other stuff ...
  }

  ok = ok && (fwrite(stuff, sizeof(data), num_elements, outfile) == num_elements);

  if (ok) {
    ... etc etc ad nauseam ...
  }

  fclose(outfile);

  return ok;

上記は、2 つの目標を同時に達成します。

  • 戻り値がチェックされるため、警告がなくなり、ステータス コードを返すことができます。
  • 短絡評価のおかげで、fwrite() 呼び出しの 1 つが失敗した場合、後続の呼び出しは実行されないため、関数の途中でエラー状態が解消された場合、破損した可能性のあるファイルを提供する代わりに、少なくともファイル書き込みが停止し、あなたは再びデータを書き込むことができます

if (ok)残念ながら、どこでも短絡評価を使用したくない場合は、「醜い」ブロックが必要です。私はこのパターンがどこでも短絡評価を使用する比較的小さな関数で使用されているのを見たことがありますが、おそらくその特定の用途に最も適していると思います。

于 2009-02-20T14:10:44.647 に答える
0

たぶん、このようなものですか?コードを読みにくくすることなくエラーをキャッチし、偽のループの終了後にクリーンアップを実行できます。

#define WRITE_ERROR 100
#define WRITE_OK 0

int do_fwrite(void* ptr, size_t bytes, int fp) {
  if ( fwrite(ptr, bytes, 1, fp) != bytes ) return WRITE_ERROR;
  return WRITE_OK;
}

int my_func() {
   int errcode = 0;

   ...
   do {
     if ( errcode = do_fwrite(&blah, sizeof(blah), fp) ) break;
     ....
     if ( errcode = do_fwrite(&foo, sizeof(foo), fp) ) break;
     ....
     etc
   } while( false );

   fclose(fp);
   return errcode;
}
于 2009-02-20T13:53:29.227 に答える
0

わかりました、私がc解決策を探していることを考えると(例外はありません)、どうですか:

void safe_fwrite(data,size,count,fp) {
   if (fwrite(data,size,count,fp) != count) {
      printf("[ERROR] fwrite failed!\n");
      fclose(fp);
      exit(4);
   }
}

そして、私のコードには次のものがあります。

safe_fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
safe_fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...
于 2009-02-20T14:02:46.193 に答える