Проверка ошибок в C — это очень многословная задача, которая делает код нечитаемым. В C++ есть исключения, но когда я использую C++ для вызова функций C, головная боль возвращается. Хотелось бы, чтобы была "обертка системных вызовов для C++", где все функции имели бы одинаковые имена и параметры, но где ошибки превращались бы в исключения и все. Это изменило бы правила игры.
Пытаясь решить эту проблему многословия при взаимодействии с функциями/системными вызовами C из C++, я нашел следующее решение (упрощенное здесь):
void ko(char const* doingwhat) // or ko_check or whatever
{
if (errno != 0)
throw std::runtime_error(doingwhat);
}
void some_method()
{
errno = 0;
int fd = open(blablabla, blablabla); ko("opening the file");
void* ptr = mmap(blablabla, blablabla); ko("mmaping it");
struct stat st; fstat(fd, &st); ko("getting stats");
// etc etc etc
}
Метод состоит в том, чтобы напрямую игнорировать возвращаемые значения и вместо этого проверять errno. Поскольку errno
является локальным потоком и может измениться только в случае ошибки в вызове libc, установки errno = 0 в начале вашего метода будет достаточно.
В C это было бы бессмысленно (или, по крайней мере, не так выгодно), потому что вы все равно не сможете избавиться от возврата в случае ошибки, и именно поэтому я думаю, что никогда не видел такого подхода, упомянутого в каких-либо дискуссиях об обработке ошибок.
Есть ли какая-то серьезная ошибка в моем подходе? Может ли errno
оказаться != 0
в неожиданных ситуациях? Есть что-нибудь, о чем следует знать? Тот факт, что я никогда не видел, чтобы этот «подход» где-либо обсуждался, заставляет меня задуматься, есть ли для этого какие-либо веские причины, потому что я их не вижу.
ПРИМЕЧАНИЕ. Конечно, вы должны быть осторожны с функциями, которые устанавливают errno
в приемлемых для вас условиях. Хорошо знайте функции, которые вы используете, и не позволяйте привычке просто вызывать ko
из-за того, что вы забываете постоянно проверять man
страницы функций, которые вы используете.
Это не системные вызовы («системные вызовы»), а простые вызовы функций C. Ваше решение - втиснуть другой вызов функции в ту же строку - ужасно, лучше создайте шаблон функции, который обертывает вызываемую вами функцию. Или, что еще лучше, найдите существующую библиотеку, которая это делает! В конце концов, ваши требования не являются чем-то особенным.
Как правило, и спецификация C, и POSIX говорят, что значение errno
не определено, если вызванная функция не вернется с индикатором сбоя. Есть несколько исключений, но используемые вами функции не входят в этот (очень) короткий список.
@Someprogrammerdude Я определил его в начале метода с 0. Насколько значение сохраняется в случае «не ошибки»...
Функция не может вызвать другую функцию, которая возвращает ошибку, и не вернуть саму ошибку, но из-за этого оставить для errno ненулевое значение.
Значение становится неопределенным после каждого сделанного вами вызова. Прежде чем проверять errno
, вы всегда должны проверять, действительно ли функция дала сбой.
@Someprogrammerdude Если вы превратите свой комментарий в ответ, я приму его. Комментарий Шона также является важным моментом, который вы можете добавить в свой ответ.
Вы не можете заменить проверку ошибок исключениями при обертывании C C++. Согласно (проекту) стандарта C11 7.5 Ошибки <errno.h>, параграф 3:
Значение errno в начальном потоке равно нулю при запуске программы (начальное значение errno в других потоках является неопределенным), но никогда не устанавливается в ноль какой-либо библиотечной функцией. Значение errno может быть установлено в ненулевое значение вызовом библиотечной функции независимо от того, произошла ли ошибка, при условии, что использование errno не документировано в описании функции в настоящем международном стандарте.
Обратите внимание на эти два утверждения:
[
errno
] ... никогда не устанавливается в ноль ни одной библиотечной функцией.
и.
Значение
errno
может быть установлено в ненулевое значение вызовом библиотечной функции независимо от того, произошла ли ошибка, при условии, что использованиеerrno
не документировано в описании функции в настоящем международном стандарте.
Таким образом, вы никогда не можете полагаться на значение errno
, если вы еще не установили, что получили ошибку от функции, которая задокументирована для установки errno
.
Таким образом, вам придется обернуть все вызовы C вызовами C++ таким образом, чтобы правильно обрабатывать ошибки, чтобы превратить их в исключения.
Не говоря уже о том, что полагаться на исключения для обработки ожидаемых условий широко считается ОЧЕНЬ плохой практикой. Все файлы заканчиваются: выдача исключения в EOF является неправильным использованием исключений и ни в коем случае не является чем-то исключительным.
Я не полагаюсь на исключения для обработки ожидаемых условий, потому что в моем контексте и функциях, которые я использую, это не ожидаемые условия; это ошибки.
Ошибка — это не то же самое, что ответ, которого вы не ожидаете. Это может быть правильный ответ, но не ошибка. Плохо игнорировать возвращаемое функцией значение, которое может быть тем, для получения которого вы вызвали функцию.