我正在研究一些产生大量
的代码ignoring return value of ‘size_t fwrite(const void*, size_t, size_t, FILE*)’, declared with attribute warn_unused_result
使用g ++编译时出现警告,我想知道实际记录和处理大量单独序列fwrite
的返回值的最佳编程模式(即不同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的解决方案来保持代码库的其余部分。
答案 0 :(得分: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返回码,任何有理由这样做(例如可读性)都不够好。
答案 1 :(得分: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)
使用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()
返回写入的对象数量!
答案 2 :(得分:2)
您可以编写包装函数
void new_fwrite(a, b, c, d) {
if (fwrite (a, b, c, b) != b)
throw exception;
}
然后用new_fwrite替换所有对fwrite的调用
答案 3 :(得分:2)
忽略错误是一个坏主意。做一些令人讨厌的事情会更好,比如让程序崩溃,这样至少你知道出了什么问题,而不是默默地继续进行。更好的是错误检查和恢复。
如果你正在使用C ++,你可以为FILE *创建一个RAII包装器,这样它就会一直关闭。看看std :: auto_ptr的想法。然后,您可以随时返回有用的错误代码或函数,或者抛出异常而不必担心忘记清理项目。
答案 4 :(得分:0)
您可以删除以下警告:
(void) fwrite ( ,,,, );
解决你的主要问题,如果任何fwrite()调用失败,我猜它继续没有意义,因为输出可能是损坏的。在这种情况下,当你标记这个C ++时,我会抛出异常。
答案 5 :(得分: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;
我知道它看起来很丑,但它有单一的返回点,没有过多的嵌套,很容易维护。
答案 6 :(得分: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");
答案 7 :(得分:0)
您的第一个解决方案看起来不错。通常goto err;
更方便,因为您可能需要一些常见的清理部分(例如倒带到已知位置)。
让GCC保持安静只做:
(void)fwrite (&blah, sizeof (blah), 1, fp);
(void)fwrite (&foo, sizeof (foo), 1, fp);
答案 8 :(得分:0)
这样的事情会起作用
if (fwrite (&blah, sizeof (blah), 1, fp) != 1) throw SomeException;
如果你担心指针被清理,你可以在执行你的fwrite之前将指针包裹在某种形式的智能指针中。
如果您不想使用智能指针,那么这将有效,但它很麻烦,所以我先尝试智能指针路径
if (fwrite (&blah, sizeof (blah), 1, fp) != 1) {
//cleanup here
throw SomeException;
}
答案 9 :(得分:0)
为什么不将fwrite
包装到某种Writer对象中并在fwrite()返回错误代码时抛出异常?易于编码,易于使用,易于管理。恕我直言,当然。 :)
答案 10 :(得分: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;
}
答案 11 :(得分: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 ...
答案 12 :(得分: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;
以上完成了两个目标:
不幸的是,如果您不想在任何地方使用短路评估,那么'丑'if (ok)
块是必要的。我已经看到这种模式在相对较小的函数中使用到处使用短路评估,我认为它可能最适合于特定用途。