Я могу сделать это только тупым путем, может быть, у кого-то есть лучшее решение:
# df
mIndex valueA valueB
0 1 a a
1 2 b b
2 2 b b
3 3 c c
Вот и мы: ]
Я сделал бы что-то вдоль этих строк:
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()
возвращает количество записанных объектов!
Обработка исключений бедного человека C на основе goto (на самом деле, тот и только экземпляр 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 коды возврата, и любое серьезное основание сделать так (например, удобочитаемость) не достаточно хорошо.
Вы могли записать функцию обертки
void new_fwrite(a, b, c, d) {
if (fwrite (a, b, c, b) != b)
throw exception;
}
и затем замените все вызовы к fwrite с new_fwrite
Игнорирование ошибок является плохой идеей. Намного лучше сделать что-то противное как катастрофический отказ программа так, чтобы, по крайней мере, Вы знали, что что-то пошло не так, как надо вместо тихого продолжения. Еще лучше хорошая проверка ошибок и восстановление.
При использовании C++ можно создать обертку RAII для ФАЙЛА* так, чтобы это всегда закрывалось. Взгляд на станд.:: auto_ptr для идей. Можно затем возвратить полезный код ошибки или из функции каждый раз, когда Вы желаете, или выдаете исключение и не имеете для волнения об объектах очистки, о которых забывают.
Можно удалить предупреждения как это:
(void) fwrite ( ,,,, );
Рассматривая Ваш основной вопрос, если бы какой-либо из fwrite () называет сбой, я предположил бы, что не имеет смысла продолжаться, поскольку вывод, по-видимому, поврежден. В этом случае, и поскольку Вы отметили этот C++, я выдам исключение.
Хорошо... Вы могли создать функцию обертки, которая повторяет запись, если она перестала работать, возможно, некоторое максимальное количество повторений, и возвращает успех/отказ:
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");
Почему Вы не переноситесь fwrite
в объект Устройства записи некоторого вида и выдают исключение, если fwrite () возвращает код ошибки? Легкий кодировать, простой в использовании, легкий справиться. По моему скромному мнению, конечно.:)
Ваше первое решение смотрит хорошо. Обычно a goto err;
прибывает в более удобный, поскольку Вам, возможно, понадобится некоторая общая часть очистки (такая как перемотка к известной позиции).
Для создания GCC тихим, просто сделайте:
(void)fwrite (&blah, sizeof (blah), 1, fp);
(void)fwrite (&foo, sizeof (foo), 1, fp);
Вложение плохо, и несколько прибыли не хороши также.
Я раньше использовал следующий шаблон:
#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;
Я знаю, что это выглядит ужасным, но это имеет единственную точку возврата, никакое чрезмерное вложение и очень легкий поддержать.
Возможно, что-то вроде этого? Вы фиксируете ошибки, не делая код слишком нечитабельным, и можно сделать очистку после конца поддельного цикла.
#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;
}
Что-то вроде этого работало бы
if (fwrite (&blah, sizeof (blah), 1, fp) != 1) throw SomeException;
Если Вы волнуетесь по поводу указателей, очищаемых, можно перенести указатель в некоторую форму интеллектуального указателя перед выполнением fwrite's.
Если Вы не захотите использовать интеллектуальные указатели затем, то это будет работать, но это грязно, таким образом, я попробовал бы маршрут интеллектуального указателя сначала
if (fwrite (&blah, sizeof (blah), 1, fp) != 1) {
//cleanup here
throw SomeException;
}
Потенциально изящное решение 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)
блоки необходимы, если Вы не хотите использовать оценку короткого замыкания везде. Я видел этот шаблон, используемый в сравнительно небольших функциях с помощью оценки короткого замыкания везде, и я думал бы, что это, вероятно, подходит лучше всего для того конкретного использования.
Хорошо, учитывая, что я ищу a 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 ...