处理 stdio 文件写入函数的返回值的良好编程模式是什么

发布于 2024-07-14 10:35:57 字数 1288 浏览 6 评论 0原文

我正在编写一些在使用 g++ 编译时生成大量

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

警告的代码,并且我想知道实际记录和处理大量单独的顺序 fwrite 的返回值的最佳编程模式>s(即循环中的 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 ...

更新:我已经删除了 c++ 标签,因为此代码实际上只是使用 g++ 编译的 c,因此需要基于 c 的解决方案来与代码库的其余部分保持一致。

I'm working on some code that generates a lot of

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

warnings when compiled with g++, and I'm wondering about the best programming pattern to actually record and handle the return value of a large number of separate sequential fwrites (i.e. not the same fwrite in a loop)

Let's say that the code looks like this at the moment:

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

I'm currently thinking about something like this, but I may have difficulty cleaning up the file pointer:

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

I think that approach is clearly better than nesting, which would get too crazy too quick:

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

Surely there is already an established best-practice pattern for this sort of thing, though?

Of course, as I am mainly looking into this to get rid of the compiler warnings, I could just assign the return value to a dummy variable and ignore it, but I'd like to try doing it the right way first.

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

Update: I've removed the c++ tag as this code is really just c being compiled using g++, so c based solutions are needed to keep with the rest of the code base.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

绿光 2024-07-21 10:35:57

我会按照以下方式做一些事情:

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)

并使用 ferror() 而不是 Jonathan Leffler 建议的我们自己的错误标志,这可以写为

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 错误之外,您仍然需要使用一个或多个错误变量来跟踪它们。

此外,您对 sizeof(blah) 的检查是错误的:fwrite() 返回写入的对象的计数!

I'd do something along these lines:

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;

With a little C99 macro magic

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

and using ferror() instead of our own error flag as suggested by Jonathan Leffler, this can be written as

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;

If there are other error conditions aside from io errors, you'll still have to track them with one or more error variables, though.

Also, your check against sizeof(blah) is wrong: fwrite() returns the count of objects written!

丶视觉 2024-07-21 10:35:57

穷人基于 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 返回码,并且这样做的任何充分理由(例如可读性)都不够好。

The poor man's C exception handling based on goto (in fact, the one and only instance of goto NOT being harmful):

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;
}

You get the idea. Restructure as you wish (single/multiple returns, single cleanup, custom error messages, etc.). From my experience this is the most common C error handling pattern out there. The crucial point is: NEVER, EVER ignore stdlib return codes, and any good reason to do so (e.g. readability) is not good enough.

凉城 2024-07-21 10:35:57

您可以编写一个包装函数

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

,然后用 new_fwrite 替换对 fwrite 的所有调用

You could write a wrapper function

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

and then replace all calls to fwrite with new_fwrite

萝莉病 2024-07-21 10:35:57

忽略错误是一个坏主意。 最好做一些令人讨厌的事情,比如让程序崩溃,这样至少你知道出了问题,而不是默默地继续。 更好的是良好的错误检查和恢复。

如果您使用 C++,您可以为 FILE* 创建一个 RAII 包装器,以便它始终被关闭。 查看 std::auto_ptr 的想法。 然后,您可以随时从函数中返回有用的错误代码,或者抛出异常,而不必担心忘记清理项目。

Ignoring errors is a bad idea. It's much better to do something nasty like crash the program so that at least you know something went wrong, rather than silently proceeding. Even better is nice error checking and recovery.

If you're using C++, you can create an RAII wrapper for the FILE* so that it'll always get closed. Look at std::auto_ptr for ideas. You can then return a useful error code or from the function whenever you wish, or throw an exception and not have to worry about forgotten cleanup items.

寻找一个思念的角度 2024-07-21 10:35:57

您可以像这样删除警告:

(void) fwrite ( ,,,, );

解决您的主要问题,如果任何 fwrite() 调用失败,我猜继续没有意义,因为输出可能已损坏。 在这种情况下,当您标记此 C++ 时,我会抛出异常。

You can remove the warnings like this:

(void) fwrite ( ,,,, );

Addressing your main question, if any of the fwrite() calls fail, I'd guess that it doesn't make sense to continue, as the output is presumably corrupt. In that case, and as you tagged this C++, I'd throw an exception.

疯了 2024-07-21 10:35:57

嵌套不好,多重回报也不好。

我曾经使用以下模式:

#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;

我知道它看起来很丑,但它有单个返回点,没有过多的嵌套并且非常易于维护。

Nesting is bad, and multiple returns are not good either.

I used to use following pattern:

#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;

I know it looks ugly but it has single return point, no excessive nesting and very easy to maintain.

苏璃陌 2024-07-21 10:35:57

好吧...您可以创建一个包装函数,如果失败则重新尝试写入,也许达到最大重试次数,并返回成功/失败:

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");

Well ... You could create a wrapper function, that re-tries the write if it fails, perhaps up to some maximum number of retries, and returns success/failure:

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

Then your main code could be written as

#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");
天暗了我发光 2024-07-21 10:35:57

你的第一个解决方案看起来不错。 通常,goto err; 会更方便,因为您可能需要一些常见的清理部分(例如倒带到已知位置)。

要让 GCC 安静,只需执行以下操作:

(void)fwrite (&blah, sizeof (blah), 1, fp);
(void)fwrite (&foo, sizeof (foo), 1, fp);

Your first solution looks ok. Usually a goto err; comes in more handy as you may need some common cleanup part (such as rewinding to a known position).

To make GCC quiet just do:

(void)fwrite (&blah, sizeof (blah), 1, fp);
(void)fwrite (&foo, sizeof (foo), 1, fp);
独自唱情﹋歌 2024-07-21 10:35:57

为什么不将 fwrite 包装到某种 Writer 对象中,并在 fwrite() 返回错误代码时抛出异常? 易于编码、易于使用、易于管理。 恕我直言,当然。 :)

Why don’t you wrap the fwrite into a Writer object of some kind and throw an exception if fwrite() returns an error code? Easy to code, easy to use, easy to manage. IMHO, of course. :)

逆蝶 2024-07-21 10:35:57

也许是这样的? 您可以捕获错误而不会使代码变得太不可读,并且可以在人造循环结束后进行清理。

#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;
}

Maybe something like this? You catch the errors without making the code too unreadable, and you can do cleanup after the end of the faux loop.

#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;
}
把时间冻结 2024-07-21 10:35:57

像这样的东西会起作用

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

如果您担心指针被清理,您可以在执行 fwrite 之前将指针包装在某种形式的智能指针中。

如果您不想使用智能指针,那么这可以工作,但是很混乱,所以我首先尝试智能指针路线

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) {
    //cleanup here
    throw SomeException;
}

Something like this would work

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

If you're worried about pointers being cleaned up you can wrap the pointer in some form of smart pointer before carrying out your fwrite's.

If you don't want to use smart pointers then this will work, but it's messy, so I'd try the smart pointer route first

if (fwrite (&blah, sizeof (blah), 1, fp) != 1) {
    //cleanup here
    throw SomeException;
}
一身骄傲 2024-07-21 10:35:57

一个潜在的优雅的 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;

上面同时实现了两个目标:

  • 检查返回值,从而消除警告并让您能够返回状态代码。
  • 由于短路评估,如果其中一个 fwrite() 调用失败,后续调用将不会被执行,因此如果错误条件在函数中途消失,至少文件写入会停止,而不是给您一个可能损坏的文件,并且你可以再次写入数据

不幸的是,如果你不想在任何地方使用短路评估,那么“丑陋”的if(ok)块是必要的。 我已经看到这种模式在相对较小的函数中使用,到处都使用短路评估,我认为它可能最适合该特定用途。

A potentially elegant C solution for this could be something like this (warning - untested, uncompiled code ahead):


  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;

The above accomplishes two goals at the same time:

  • Return values are checked, thus eliminating the warning and giving you the ability to return a status code.
  • Thanks to the short circuit evaluation, should one of the fwrite() calls fail, the subsequent ones will not be executed, so at least the file write stops instead of giving you a potentially corrupt file if the error condition disappears halfway through the function and you're able to write data again

Unfortunately the 'ugly' if (ok) blocks are necessary if you don't want to use the short-circuit evaluation everywhere. I've seen this pattern used in comparatively small functions using short circuit evaluation everywhere and I would think that it's probably best suited to that particular use.

心病无药医 2024-07-21 10:35:57

好的,鉴于我正在寻找一个 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 ...

Ok, given that I'm looking for a c solution (no exceptions), how about:

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

And then in my code I have:

safe_fwrite (&blah, sizeof (blah), 1, fp);
// ... more code ...
safe_fwrite (&foo, sizeof (foo), 1, fp);
// ... more code ...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文