Объявление enum в сигнатуре функции как возвращаемый статус

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

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

Например. так:

// 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) не будет генерировать предупреждение или ошибку.

Почему не typedef?

MFisherKDX 09.04.2019 08:46

Каким компилятором вы компилируете этот код? gcc недоволен из-за переопределений...

Mathieu 09.04.2019 08:55

Даже если значения перечислителя идентичны, я не уверен, что это законно C. Если значения перечислителя каким-либо образом отличаются, то это определенно не так, что делает метод бесполезным. Тем не менее, это интересный вопрос, если значения идентичны, как вы представляете.

Bathsheba 09.04.2019 09:06

@Closers: как, черт возьми, основано это мнение?

Bathsheba 09.04.2019 09:08

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

Jonathan Leffler 09.04.2019 09:12

@Bathsheba Я думаю, это потому, что он спрашивал плюсы и минусы определенного стиля программирования. Это не по теме, потому что то, что люди считают плюсами и минусами, часто основано на личном мнении. Я отредактировал вопрос, чтобы удалить этот язык и отразить то, что на самом деле обсуждается здесь.

TylerH 09.04.2019 23:01
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
200
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

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

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

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

Цитата из 6.2.5 Типы с. 16

Each distinct enumeration constitutes a different enumerated type

Это тот. Спасибо.

Bathsheba 09.04.2019 09:07

Это относится к одному TU. На кону в разных TU стоят разные вопросы. См. §6.2.7 Совместимый тип и составной тип для некоторых правил, касающихся определений, совместно используемых TU.

Jonathan Leffler 09.04.2019 09:10

Как это может сделать их несовместимыми? 6.2.7 В §1 говорится, что типы совместимы.

Lundin 09.04.2019 09:50

«Более того, две структуры, объединения или перечисляемые типы, объявленные в отдельных единицах перевода, совместимы, если их теги и члены удовлетворяют следующим требованиям: если один объявлен с тегом, другой должен быть объявлен с тем же тегом. Если оба завершены где-либо в пределах своих соответствующих единиц перевода, то применяются следующие дополнительные требования: между их элементами должно быть однозначное соответствие, так что каждая пара соответствующих членов объявляется с совместимыми типами; /--/ "Для двух перечислений соответствующие элементы должны иметь одинаковые значения."

Lundin 09.04.2019 09:51

Пытающийся:

#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?

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