Рекомендуете ли вы использовать errno в новых C API?

#c #api #errno

#c #API #errno

Вопрос:

Рекомендуете ли вы использовать errno в новых C API?

Должны ли другие методы сообщать о проблемах, таких какhttp://developer.gnome.org/glib/stable/glib-Error-Reporting.html использовать вместо этого?

Что вы рекомендуете?

Ответ №1:

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

При написании нового кода я настоятельно не рекомендую использовать глобальную переменную в стиле errno для проверки кода ошибки. Допустим, у вас есть функции, a через b которые ab_error сообщают об их состоянии, тогда вы должны написать что-то вроде этого:

 a();
if( ab_error == AB_NOERROR ) {
    b();
    if( ab_error == NO_ERROR ) {
        /* ... */
    }
}
  

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

 int error_a;
int error_b;

if( (error_a = a()) == A_NOERROR
 amp;amp; (error_b = b()) == B_NOERROR
){
    /* ... */
} else {
    /* test the error variables */
}
  

вы даже можете связать несколько состояний ошибки, подобных этому:

 int error_a;
int error_b;
int error_c;

if( (error_a = a()) == A_NOERROR
 amp;amp;  ( (error_b = b()) == B_NOERROR
    || (error_c = c()) == C_NOERROR )
){
    /* ... */
} else {
    /* test the error variables */
}
  

Который намного более удобочитаем, чем если бы вы разбили все это на несколько вложенных операторов if.

Комментарии:

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

2. @William: Конечно, но что я имел в виду: не используйте его для передачи ваших собственных кодов ошибок.

Ответ №2:

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

Комментарии:

1. Это не «глобальный errno «, это » глобальный errno «, и он определяется не как extern int , а как макрос, который расширяется до значения типа int , именно потому, что он должен быть специфичным для потока.

2. @R.: Спасибо за комментарий, вы правы. Мое утверждение было основано на опыте работы на явно несоответствующих платформах, где не всегда соблюдалось требуемое поведение errno .

3. 1, поскольку, однако, это подчеркивает вредоносность «глобальных переменных», хотя errno не совсем глобальная переменная; однако, my man errno говорит «errno может быть макросом. errno является потоковым локальным «, не то чтобы это наверняка был макрос.