Объявление перечисления в сигнатуре функции как возвращаемого статуса - PullRequest
2 голосов
/ 09 апреля 2019

Я недавно придумал, как мне кажется, странный способ объявления статуса возврата функции в c-коде.Функция, выполняющая задачу, часто возвращает код состояния в зависимости от успеха или ошибки.Это часто отправляется как целое число или перечисление, объявленное где-то еще в файле (или другом файле).Я обнаружил, что, объявив enum в сигнатуре функции, мы можем:

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

Например, вот так:

// implementation.h
enum {
    OK,
    INTERNAL_ERROR,
    NO_PARAMETERS
} do_that();

// implementation.c
enum {
    OK,
    INTERNAL_ERROR,
    NO_PARAMETERS
} do_that() {
    if (this())
        return OK;
    else if (that())
        return NO_PARAMETERS;
    return INTERNAL_ERROR;
}

Я никогда не видел, чтобы кто-нибудь использовал этот стиль;это действительно?Если нет, то почему бы и нет?

Один недостаток, который я вижу, заключается в том, что вы должны указать возможные возвращаемые значения как в c-файле, так и в заголовочном файле, но это также делает его более доступным и понятным непосредственно, когда вы смотрите наподпись.

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

Ответы [ 3 ]

6 голосов
/ 09 апреля 2019

Одной из основных проблем является то, что вы не можете использовать компилятор для перекрестной проверки заголовка и реализации, потому что TU реализации (единица перевода) - то есть implementation.c в вопросе - не может включать заголовок (implementation.h) потому что константы перечисления будут определены дважды, что недопустимо. Это означает, что компилятор не может обнаружить несоответствия между поставщиком и потребителями сервиса. Потребители могут использовать заголовок, но поставщик (implementation.c) не может.

Кроме того, только одна функция может использовать OK (или INTERNAL_ERROR или NO_PARAMETERS) - другим функциям требуются разные имена для статуса «без ошибок» и разные имена для каждого из состояний ошибок. Следовательно, это не практичное решение даже для небольшого программного обеспечения, даже если его можно использовать для одной функции (но это не очень хорошая идея).

Кстати, обратите внимание, что хотя для do_that() существуют объявления, для него нет ни прототипа, ни в заголовке, ни в файле реализации. Объявлено, что функция принимает неопределенный список параметров - все, что известно, это то, что она формально не является функцией с переменными координатами (без многоточия ...), и все ее аргументы подчиняются расширению аргумента по умолчанию правила (примерно: char и short повышен до int; float повышен до double). Если функция не принимает аргументов, ее следует записать как do_that(void), чтобы после завершения объявления был доступен прототип.

1 голос
/ 09 апреля 2019

Это недопустимо, так как первое перечисление отличается от второго перечисления, поэтому объявление и определение имеют несовместимые подписи, даже если их постоянные члены идентичны.

Цитата из 6.2.5 Типы p,16

Каждое отдельное перечисление представляет собой различный перечисляемый тип

0 голосов
/ 09 апреля 2019

Попытка:

#include <stdio.h> 
// implementation.h
enum {
    OK,
    INTERNAL_ERROR,
    NO_PARAMETERS
} do_that();

// implementation.c
enum {
    OK,
    INTERNAL_ERROR,
    NO_PARAMETERS
} do_that() {
    if (mytest == 0)
        return OK;
    else if (mytest == 1)
        return NO_PARAMETERS;
    return INTERNAL_ERROR;
}

int main() 
{ 
    printf("do_that(%d) = %d\n",0,do_that(0));
    printf("do_that(%d) = %d\n",1,do_that(1));
    printf("do_that(%d) = %d\n",2,do_that(2));
    return 0; 
} 

Дает:

Main.c:11:5: error: redefinition of enumerator 'OK'
    OK,
    ^
Main.c:4:5: note: previous definition is here
    OK,

Так что вы не можете:

  • включить ваш .h в.c.

  • объявить прототип функции

Почему бы вам просто не использовать typedef?

...