Перенос предложения: голоса +4, -1
Автор идеи:pavel.darashkevich
Возвращать исходную ошибку в исключении.
Не бросать исключение в коде, который корректен (код ниже), если не включать исключения.
Добавить возвращение кода ошибки последним аргументом в функциях std::ifstream/std::ofstream и им подобным.
Пишем простую программу, которая считывает все числа из файла (файл состоит из чисел типа int, разделённых пробелами), после этого выводим сумму.
Код короткий, достаточно понятный. А теперь подумаем: что будет, если файла нет или во время чтения достанут флешку, на которой этот файл записан? Хотелось бы как-то обрабатывать эти ситуации.
Мы-то знаем, что std::ifstream может бросать исключение при ошибках. Перепишем:
Исключение std::ios_base::failure содержит мало информации об ошибке:
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_ios::clear: iostream error
Что можно понять из этого сообщения? Только то, что что-то пошло не так при работе с потоками, а что именно (проблема с файлом или с его содержимым) -- не понятно. Более того, в большой программе это вообще не информативный вывод, хуже не придумаешь.
Даже если файл существует, содержит только числа, всё равно кидается исключение после того, как достигается конец потока (кроме eofbit выставляется failbit, что логично). Как побороть это, не написав кучу некрасивого кода? Отказываться от исключений и писать так?
#include <iostream>
#include <vector>
#include <fstream>
#include <numeric>
#include <iterator>
#include <string>
int main(const int argc, const char* argv[]) {
if (argc == 1) {
std::cerr << "Usage: program path_to_file" << std::endl;
return EXIT_FAILURE;
}
const std::string path { argv[1] };
std::ifstream in;
in.open(path);
if (in.fail()) {
perror("Can't open file");
return EXIT_FAILURE;
}
std::vector<int> numbers;
while (!in.eof()) {
int number;
in >> number;
if (in.eof()) {
break;
}
if (in.fail()) {
if (errno) {
perror("Error while reading from file");
} else {
std::cerr << "Invalid file format! Value out of `int` domain or not number." << std::endl;
}
return EXIT_FAILURE;
}
numbers.push_back(number);
}
std::cout << std::accumulate(numbers.begin(), numbers.end(), 0LL);
return EXIT_SUCCESS;
}
Что-то длинно получилось... При этом я не учёл то, что на Windows ошибки правильнее смотреть через GetLastError() и выводить по-другому. Что хотелось бы написать?
Коротко и очень информативно, если возвращается соответствующая ошибка.
Итого, что предлагается?
Улучшить качество отдаваемых исключений:
1.1. Возвращать ошибку, код которой соответствует исходной (файл не найден и т.п.). При ошибке чтения из-за неверного формата -- тоже соответствующая ошибка (новую категорию parse_error {domain, format}?). И только в том случае, если код ошибки получить невозможно -- текущий вариант.
1.2. Также вместо бессмысленного basic_ios::clear тогда уже писать имя функции, которую пользователь вызвал. А ещё лучше -- завести enum (или что-то более хитрое) и его тоже возвращать:
Зачем? Чтобы можно было не только вывести хорошее сообщение об ошибке после отлавливания в любом месте программы, но и реализовать какую-то логику на основе этого.
1.3. Может подумать над тем, чтобы в исключении ещё было имя файла? Не думал, технически реализуемо ли это без накладных расходов, но всё же.
не должно выбрасываться исключение из-за того, что дошли до конца потока (если я не прав, жду полного и объёмного объяснения, почему этот код должен работать в режиме без исключений и не работать в противоположном).
Как это исправить? Сделать функцию (название и вид можно придумать лучше):
template <typename T>
bool try_read(T& var);
template <typename T>
bool try_read(T& var, std::error_code& code); // чтобы узнать, eof или что-то другое, хотя может быть лишнее, тут можно подумать.
И использовать её внутри данного итератора. Да и кроме него она, думаю, будет полезной.
В std::ifstream сделать функции с последним аргументом типа std::error_code для его получения, как сделано в std::filesystem.
Что-то очень объёмно получилось... Даже не знаю, как это лучше было разделить, поэтому написал как есть. Надеюсь, среди этого наболевшего есть хоть что-то полезное ;).
Перенос предложения: голоса +4, -1 Автор идеи: pavel.darashkevich
Возвращать исходную ошибку в исключении. Не бросать исключение в коде, который корректен (код ниже), если не включать исключения. Добавить возвращение кода ошибки последним аргументом в функциях std::ifstream/std::ofstream и им подобным.
Пишем простую программу, которая считывает все числа из файла (файл состоит из чисел типа int, разделённых пробелами), после этого выводим сумму.
Код короткий, достаточно понятный. А теперь подумаем: что будет, если файла нет или во время чтения достанут флешку, на которой этот файл записан? Хотелось бы как-то обрабатывать эти ситуации.
Мы-то знаем, что std::ifstream может бросать исключение при ошибках. Перепишем:
И сразу же замечаем 2 плохие вещи:
Что можно понять из этого сообщения? Только то, что что-то пошло не так при работе с потоками, а что именно (проблема с файлом или с его содержимым) -- не понятно. Более того, в большой программе это вообще не информативный вывод, хуже не придумаешь.
Что-то длинно получилось... При этом я не учёл то, что на Windows ошибки правильнее смотреть через GetLastError() и выводить по-другому. Что хотелось бы написать?
Коротко и очень информативно, если возвращается соответствующая ошибка.
Итого, что предлагается?
1.1. Возвращать ошибку, код которой соответствует исходной (файл не найден и т.п.). При ошибке чтения из-за неверного формата -- тоже соответствующая ошибка (новую категорию parse_error {domain, format}?). И только в том случае, если код ошибки получить невозможно -- текущий вариант.
1.2. Также вместо бессмысленного basic_ios::clear тогда уже писать имя функции, которую пользователь вызвал. А ещё лучше -- завести enum (или что-то более хитрое) и его тоже возвращать:
Зачем? Чтобы можно было не только вывести хорошее сообщение об ошибке после отлавливания в любом месте программы, но и реализовать какую-то логику на основе этого.
1.3. Может подумать над тем, чтобы в исключении ещё было имя файла? Не думал, технически реализуемо ли это без накладных расходов, но всё же.
не должно выбрасываться исключение из-за того, что дошли до конца потока (если я не прав, жду полного и объёмного объяснения, почему этот код должен работать в режиме без исключений и не работать в противоположном).
Как это исправить? Сделать функцию (название и вид можно придумать лучше):
И использовать её внутри данного итератора. Да и кроме него она, думаю, будет полезной.
Что-то очень объёмно получилось... Даже не знаю, как это лучше было разделить, поэтому написал как есть. Надеюсь, среди этого наболевшего есть хоть что-то полезное ;).
Спасибо.