Можно ли вернуть разные «ложные» в C? - PullRequest
0 голосов
/ 26 января 2019

Я пишу оболочку для realloc.Для возвращаемых значений я использую это:

typedef enum { SALLOC_OK, SALLOC_OVERFLOW, SALLOC_ALLOCFAIL, ... } salloc_rc;

Это используется в такой функции:

salloc_rc salloc(<args>) {
    if(blabla) return SALLOC_OVERFLOW;
    if(blabla) return SALLOC_ALLOCFAIL;
    /* More error codes */
    return SALLOC_OK;
}

Это, конечно, не полный код, но этого достаточно, чтобы продемонстрироватьчто я хочу.Все идет нормально.Я могу проверить возвращаемое значение следующим образом:

if(salloc(a,b) != SALLOC_OK) // Handle error

или, если я хочу быть более точным:

if(salloc(a,b) == SALLOC_OVERFLOW) // Handle overflow

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

if(!salloc(a,b)) // Handle error

и

if(salloc(a,b) { /* Do stuff */ }
else { /* Handle error */ }

Проблема здесь в том, что 0 ложно, а ВСЕ остальное верно.То, что я хочу сделать, кажется невозможным, но мог бы быть какой-то путь, который я пропустил.Единственное решение, которое я нашел до сих пор, это дать функции дополнительный аргумент с указателем на место, где я могу сохранить код ошибки, но я хочу избежать этого, если это возможно.

TL; DR

Как сделать функцию <type> foo() способной возвращать различные сообщения об ошибках в зависимости от того, что пошло не так, при этом сохраняя возможность проверять наличие ошибок «традиционным» способом, таким как

if (!foo()) exit(EXIT_FAILURE);

Ответы [ 3 ]

0 голосов
/ 27 января 2019

Если вы напишите

if (! salloc(a,b)) handleError();

это просто неправильно. Серьезная ошибка Но если вы напишите

if (salloc(a, b)) handleError();

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

salloc_rc returnCode = salloc(a, b);
if (returnCode != SALLOC_OK) handleError();

Он чистый, он точно сообщает читателю, что происходит, дает возможность установить точку останова, где вы проверяете код возврата. Выиграть все кончено. Не бойтесь лишних нажатий клавиш. Сделайте ваш код читабельным. И если вам говорят о «обычном способе проверки на наличие ошибок», который затрудняет чтение кода, не используйте «обычный способ проверки на наличие ошибок».

Обратите внимание, что во многих более современных языках (Java, Swift) вы не можете использовать enum как условие в операторе if или в качестве аргумента для! (не). Черт, я только что назвал Java «более современным языком»: - (

0 голосов
/ 27 января 2019

У вас есть несколько вариантов, если вам нужен только один код успеха и несколько кодов ошибок.
Ни один из них не настолько естественен и идиоматичен и, следовательно, хорош, как если бы все было наоборот:

  1. Используйте дополнительное пространство для дополнительной информации:

    1. Используйте errno для передачи дополнительной информации для заинтересованных.
      Стандартная библиотека делает это широко.

    2. Сохранить конкретную ошибку в используемом объекте. fgetc() делает это с feof() и ferror().

    3. Определите свой собственный (локальный для потока) тайник для дополнительной информации об ошибке.

    4. Использовать обратный вызов или дополнительный выходной указатель.

    5. Верните struct со всеми необходимыми членами.

  2. Использовать инвертированную логику, что означает, что только false - это успех.
    Это напоминает использование общей функции сравнения, такой как strcmp(), для проверки равенства.

  3. Посвятить часть диапазона возвращаемых значений ошибкам:

    1. Использовать отрицательное среднее значение ошибки. Приятно то, что у вас есть все негативы для успеха.
      COM делает это экстенсивно с их HRESULT. Как и многие системные вызовы Linux.

    2. Числа с плавающей точкой обычно имеют много значений NaN. Можно было бы указать здесь конкретику, а некоторые архитектуры даже гарантированно распространяют ту, которая содержит наименьший код. К сожалению, он редко использовался, имел небольшую стоимость и поэтому не использовался для новых инструкций.

    Есть и другие, менее удобные примеры.

Мой совет - хранить его в манипулируемом объекте, если можете, затем errno, затем отрицать логику и, наконец, соглашения COM.

0 голосов
/ 27 января 2019

Обычно это делается противоположным образом.

 if(salloc(a,b)) // Handle error

 if(USB_Transmit(a,b)) // Handle error

Это очень простая логика - если функция возвращает ненулевое значение - это означает что-то не то./ * После состояния USB-устройства * /

typedef enum {
  USBD_OK   = 0,
  USBD_BUSY,
  USBD_FAIL,
}USBD_StatusTypeDef;

#define NRF_SUCCESS                           (NRF_ERROR_BASE_NUM + 0)  ///< Successful command
#define NRF_ERROR_SVC_HANDLER_MISSING         (NRF_ERROR_BASE_NUM + 1)  ///< SVC handler is missing
#define NRF_ERROR_SOFTDEVICE_NOT_ENABLED      (NRF_ERROR_BASE_NUM + 2)  ///< SoftDevice has not been enabled
#define NRF_ERROR_INTERNAL                    (NRF_ERROR_BASE_NUM + 3)  ///< Internal Error
#define NRF_ERROR_NO_MEM                      (NRF_ERROR_BASE_NUM + 4)  ///< No Memory for operation
#define NRF_ERROR_NOT_FOUND                   (NRF_ERROR_BASE_NUM + 5)  ///< Not found
#define NRF_ERROR_NOT_SUPPORTED               (NRF_ERROR_BASE_NUM + 6)  ///< Not supported
#define NRF_ERROR_INVALID_PARAM               (NRF_ERROR_BASE_NUM + 7)  ///< Invalid Parameter
#define NRF_ERROR_INVALID_STATE               (NRF_ERROR_BASE_NUM + 8)  ///< Invalid state, operation disallowed in this state
#define NRF_ERROR_INVALID_LENGTH              (NRF_ERROR_BASE_NUM + 9)  ///< Invalid Length
#define NRF_ERROR_INVALID_FLAGS               (NRF_ERROR_BASE_NUM + 10) ///< Invalid Flags
#define NRF_ERROR_INVALID_DATA                (NRF_ERROR_BASE_NUM + 11) ///< Invalid Data
#define NRF_ERROR_DATA_SIZE                   (NRF_ERROR_BASE_NUM + 12) ///< Invalid Data size
#define NRF_ERROR_TIMEOUT                     (NRF_ERROR_BASE_NUM + 13) ///< Operation timed out
#define NRF_ERROR_NULL                        (NRF_ERROR_BASE_NUM + 14) ///< Null Pointer
#define NRF_ERROR_FORBIDDEN                   (NRF_ERROR_BASE_NUM + 15) ///< Forbidden Operation
#define NRF_ERROR_INVALID_ADDR                (NRF_ERROR_BASE_NUM + 16) ///< Bad Memory Address
#define NRF_ERROR_BUSY                        (NRF_ERROR_BASE_NUM + 17) ///< Busy
#define NRF_ERROR_CONN_COUNT                  (NRF_ERROR_BASE_NUM + 18) ///< Maximum connection count exceeded.
#define NRF_ERROR_RESOURCES                   (NRF_ERROR_BASE_NUM + 19) ///< Not enough resources for operation

Где NRF_ERROR_BASE_NUM обычно 0

...