C++ и системные вызовы: игнорируем возвращаемое значение и вместо этого проверяем errno

Проверка ошибок в 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 страницы функций, которые вы используете.

Ошибка — это не то же самое, что ответ, которого вы не ожидаете. Это может быть правильный ответ, но не ошибка. Плохо игнорировать возвращаемое функцией значение, которое может быть тем, для получения которого вы вызвали функцию.

Weather Vane 26.05.2024 21:40

Это не системные вызовы («системные вызовы»), а простые вызовы функций C. Ваше решение - втиснуть другой вызов функции в ту же строку - ужасно, лучше создайте шаблон функции, который обертывает вызываемую вами функцию. Или, что еще лучше, найдите существующую библиотеку, которая это делает! В конце концов, ваши требования не являются чем-то особенным.

Ulrich Eckhardt 26.05.2024 21:43

Как правило, и спецификация C, и POSIX говорят, что значение errno не определено, если вызванная функция не вернется с индикатором сбоя. Есть несколько исключений, но используемые вами функции не входят в этот (очень) короткий список.

Some programmer dude 26.05.2024 21:47

@Someprogrammerdude Я определил его в начале метода с 0. Насколько значение сохраняется в случае «не ошибки»...

ABu 26.05.2024 21:53

Функция не может вызвать другую функцию, которая возвращает ошибку, и не вернуть саму ошибку, но из-за этого оставить для errno ненулевое значение.

Shawn 26.05.2024 22:04

Значение становится неопределенным после каждого сделанного вами вызова. Прежде чем проверять errno, вы всегда должны проверять, действительно ли функция дала сбой.

Some programmer dude 26.05.2024 22:06

@Someprogrammerdude Если вы превратите свой комментарий в ответ, я приму его. Комментарий Шона также является важным моментом, который вы можете добавить в свой ответ.

ABu 26.05.2024 22:29
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
110
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы не можете заменить проверку ошибок исключениями при обертывании C C++. Согласно (проекту) стандарта C11 7.5 Ошибки <errno.h>, параграф 3:

Значение errno в начальном потоке равно нулю при запуске программы (начальное значение errno в других потоках является неопределенным), но никогда не устанавливается в ноль какой-либо библиотечной функцией. Значение errno может быть установлено в ненулевое значение вызовом библиотечной функции независимо от того, произошла ли ошибка, при условии, что использование errno не документировано в описании функции в настоящем международном стандарте.

Обратите внимание на эти два утверждения:

[errno] ... никогда не устанавливается в ноль ни одной библиотечной функцией.

и.

Значение errno может быть установлено в ненулевое значение вызовом библиотечной функции независимо от того, произошла ли ошибка, при условии, что использование errno не документировано в описании функции в настоящем международном стандарте.

Таким образом, вы никогда не можете полагаться на значение errno, если вы еще не установили, что получили ошибку от функции, которая задокументирована для установки errno.

Таким образом, вам придется обернуть все вызовы C вызовами C++ таким образом, чтобы правильно обрабатывать ошибки, чтобы превратить их в исключения.

Не говоря уже о том, что полагаться на исключения для обработки ожидаемых условий широко считается ОЧЕНЬ плохой практикой. Все файлы заканчиваются: выдача исключения в EOF является неправильным использованием исключений и ни в коем случае не является чем-то исключительным.

Я не полагаюсь на исключения для обработки ожидаемых условий, потому что в моем контексте и функциях, которые я использую, это не ожидаемые условия; это ошибки.

ABu 27.05.2024 00:33

Другие вопросы по теме