|
CPP enum с минусовыми значениями, насколько это правильно |
|
|
|
May 25 2017, 06:25
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Вопрос касается кода возврата функции, а именно удачно или нет она отработала. Если плюс - удачно, информация в коде возврата - справочная. Если минус - критичная ошибка, ф-ия не отработала. (?) Насколько такой подход (с отрицательным enum) правильный-феншуйный-политкорректный ? ps - minus_4 итд приведено для наглядности, в реале - ест-но - там симв. имя ошибки вроде eErr_ADC_Ready А реализовал так: Код enum TEnum { minus_4 = -4, minus_3, minus_2, minus_1, null_0 = 0, plus_1, plus_2, plus_3, plus_4 } my_enum;
. . .
int MyFun(void) { ... ... ... return(minus_2); ..... .... return(plus_3); }
. . .
RetCode = MyFun();
if( RetCode > 0 ) { . . . . ошибок нет, в RetCode в инф. о статусе выполнения. } else // минусы и 0 { . . . . разбор ошибок }
Сообщение отредактировал k155la3 - May 25 2017, 07:20
|
|
|
|
|
May 25 2017, 06:35
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(novikovfb @ May 25 2017, 09:32)  Почему бы и нет. Одно не понятно: чем запись minus_4 лучше простой константы -4? Логичнее было бы вместо minus_4 указать наименование типа ошибки. Константы надо "изобретать" вручную, а меня это утомляет  Кроме того с символическими именами которые предоставляет enum работать в миллион раз удобнее чем с цифровыми кодами. ps - пардон, я может неверно сформулировал в примере кода. там minus_4 приведено для наглядности. в реале - ест-но - там симв. имя ошибки вроде eErr_ADC_Ready
Сообщение отредактировал k155la3 - May 25 2017, 06:37
|
|
|
|
|
May 25 2017, 08:00
|
Частый гость
 
Группа: Участник
Сообщений: 176
Регистрация: 29-03-10
Пользователь №: 56 269

|
Постоянно применяю, только не enum, а int. Обычная практика, если функция возвращает отрицательное значение, это тип ошибки, если ноль или положительное значение, то все Ок или результат. С enum не пробовал, а зачем, int можно преобразовать или интерпретировать практически любым типом, как целое, символ или логическое значение. Удобно во вложенных функциях, возвращает тип ошибки на самый верх.
|
|
|
|
|
May 25 2017, 08:47
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(AnatolyT @ May 25 2017, 11:00)  . . . . Удобно во вложенных функциях, возвращает тип ошибки на самый верх. Ok. Я при "транизитном" возврате по ошибке, при каждом выходе "вверх", код ошибки умножаю на 10 - тогда видно с какого уровня "всплыло". Более правильно наверно, использовать исключения, но пока делаю так.
|
|
|
|
|
May 25 2017, 09:46
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(k155la3 @ May 25 2017, 11:32)  Интересует "практика" использования отрицательных enum. нормальная практика. более интерено енум использовать с псевдонимами и комбинациями - например объявляеш флаги: еА = 1, еБ = 2, еВ=4. еХ=0x10 и тут же объявляеш псевдонимы комбинаций еЧеготоОбычное = еА | еХ eНеобычное = еБ еОшибка = еА |еБ |еХ тожесамое = eНеобычное | еЧеготоОбычное Меня другой момент использования енумов занимает - последнее время вижу что народ константы перечисления делает большими буквами, что обычно выглядит как макро.
Сообщение отредактировал AlexRayne - May 25 2017, 09:47
|
|
|
|
|
May 25 2017, 10:35
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(AlexRayne @ May 25 2017, 12:46)  . . . (1) более интерено енум использовать с псевдонимами и комбинациями - например объявляеш флаги . . . (2) Меня другой момент использования енумов занимает - последнее время вижу что народ константы перечисления делает большими буквами, что обычно выглядит как макро. (1) да, допустим определены 10 констант - enum. При этом, если int 16-битный, задействовано только 4 разряда и то не полностью. Оставшиеся 12 разрядов можно использовать для любой полезной инф - битовые флаги, поля итд. Фактически - структура. Отрицательные значения - это один из вариантов этого. (2) есть вроде не-дефакто-стандарты на именование различных типов. Тутуж кто-во-что горазд. A - чтоб мне (любимому) было понятно B - один из стандартов. AB или BA приоритет ставят по разному.
|
|
|
|
|
May 25 2017, 12:27
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(k155la3 @ May 25 2017, 14:35)  (1) да, допустим определены 10 констант - enum. При этом, если int 16-битный, задействовано только 4 разряда и то не полностью. Оставшиеся 12 разрядов можно использовать для любой полезной инф - битовые флаги, поля итд. Фактически - структура. да, легкая альтернатива структуре. Цитата(k155la3 @ May 25 2017, 14:35)  Отрицательные значения - это один из вариантов этого. ну вариант не структуры, просто отрицательные числа используются. и на моей практике - как индикатор ошибки.
|
|
|
|
|
May 25 2017, 12:47
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (AlexRayne @ May 25 2017, 12:46)  Меня другой момент использования енумов занимает - последнее время вижу что народ константы перечисления делает большими буквами, что обычно выглядит как макро. Ну по смыслу такой enum эквивалентен набору макросов CODE #define TAG1 VALUE1 #define TAG2 (TAG1 + 1) #define TAG3 (TAG2 + 1) поэтому вполне ожидаемо и аналогичное правило записи имен.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 25 2017, 20:47
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Список ошибок часто определяю так: enum {ERR_1 = 1, ERR_2, ERR_3, ERR_n}; //ну или от 0 А уж возврат ошибочного значения из функции тогда или: return ERR_...; или: return -ERR_...; А если нужно возвращать из функции или данный код ошибки или корректное состояние, заданное тоже enum-ом, то делаю иногда так: enum {OK_0, OK_1, OK_2, OK_n}; return (int)OK_1; //корректное завершение с кодом OK_1 return (int)OK_n + (int)ERR_1 - 1; //завершение с ошибкой ERR_1 Т.е. - возвращаются всегда положительные значения. Для Thumb2 такой способ оптимальнее.  Кто знает асм - поймёт. Цитата(k155la3 @ May 25 2017, 12:35)  Оставшиеся 12 разрядов можно использовать для любой полезной инф - битовые флаги, поля итд. Фактически - структура. Если нужно вернуть 2 параметра, то лучше и возвращать их как 2, а не лепить из них одно в точке return, и не распаковывать потом в точке вызова. Если мне нужно например на 32-битной архитектуре вернуть 2 значения из функции, я делаю так: u64 FuncA() { u64 q = (u32)val1 | (u64)(u32)val2 << 32; return q; } Если пытаться всё лепить в одно 32-битное значение, то будет куча лишних команд при упаковке и потом - при распаковке. А в вышеприведённом случае - минимум команд.
|
|
|
|
|
May 26 2017, 07:25
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(Сергей Борщ @ May 25 2017, 15:47)  Ну по смыслу такой enum эквивалентен набору макросов Код #define TAG1 VALUE1 #define TAG2 (TAG1 + 1) #define TAG3 (TAG2 + 1) поэтому вполне ожидаемо и аналогичное правило записи имен. По результату - да. По обработке и возможным "засадом" отличается. enum просчитываются компилятором "за раз", а макросы - это головная боль препроцессора. Так на какомнибуть TAG756 (сделанный копипастом)  на вход компилятора попадет: Код + (TAG1) + (TAG1+1) // TAG2 + ((TAG1 + 1) + 1) // TAG3 + (((TAG1 + 1) + 1) + 1 ) // . . . . ... на шаге N у препроцессора (или у компилятора) срабатывает ограничение на длину строки (или списка). (Это факт, по крайней мере для IAR/MSP430 5.40) Причем никаких намеков ни Err, ни даже Warn компилятор не выдал. Цитата(x893 @ May 25 2017, 19:36)  . . . . А разве это не написано в книжках ? Да у меня вопрос собст-но про отрицательные enum. В литературе как-то не натыкался на это. А проводить гугл-сеанс нет времени. Тем более можно спросить у "первоисточников" на этом форуме  Цитата(jcxz @ May 25 2017, 23:47)  Список ошибок часто определяю так: . . . . Ok - спасибо за инф. Надо осмыслить.
|
|
|
|
|
May 26 2017, 09:46
|
Знающий
   
Группа: Участник
Сообщений: 518
Регистрация: 29-09-11
Пользователь №: 67 450

|
Цитата(AnatolyT @ May 26 2017, 13:11)  Непонятно какие преимущества дает использование enum как типа возвращаемого значения функции. 1. компилятор проверит тип, если есть 2 различных функции, возвращающих коды завершения, имеющих разные наборы значений (и разные enum) - при перепутывании будет предупреждение. 2. символические константы содержат намек на смысловое значение кода, меньше гадать, что означает возврат кода 4.
|
|
|
|
|
May 26 2017, 09:54
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (novikovfb @ May 26 2017, 12:46)  при перепутывании будет предупреждение. В плюсах. В обычных Сях enum неявно приводится к int и обратно, поэтому предупреждения, насколько помню, не будет. Добавлено: перечитал название темы...
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
May 26 2017, 10:01
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(AnatolyT @ May 26 2017, 12:11)  Непонятно какие преимущества дает использование enum как типа возвращаемого значения функции. Кроме очевидных преимуществ-удобств разгружающих моск. Если Вы определили возвр. значение с типом enum, то при работе на отладке Вы в Watch-окне будете видеть не абстрактные 12345 (и декодировать их вручную), а символическое имя - в удобо-читаемом-понимаемом виде.
|
|
|
|
|
May 26 2017, 10:10
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
По-моему идеально использовать enum по возможности так, чтобы было вообще всё равно какое там у него значение. Оно так автоматически и получается, если везде работать с enum как с типом, а не пользоваться тем, что он может быть неявно преобразован в int. Если все ошибки в вашей системе перечислены в enum и вам не нужно обеспечивать совместимость со сторонним кодом, где у ошибок есть конкретные int значения, то определите для ошибок тип Цитата typedef enum{ ERR_OK, ERR_SOMETHING, ERR_SOMETHING2 } Error_t; Пусть все функции будут возвращать Error_t, а обработчики ошибок везде будут реагировать на ошибки путем обращения к ним по именам ERR_OK, ERR_SOMETHING, ERR_SOMETHING2 то всё будет отлично. Код switch( error) { case ERR_OK: делаем что-то break; case ERR_SOMETHING: break; case ERR_SOMETHING2: break; } Это будет работать вне зависимости от значений энумов. Eсли функция возвращает int а мы делаем return ERR_SOMETHING; который вообще-то Error_t - это дурной тон. Потом чтобы обработать эту ошибку нам надо этот инт опять кастить в enum и начинается каша. Потом кто-то подставит вместо ERR_SOMETHING магическое число какое-нибудь и оно работает. Через год в энум Error_t доавбяют сверху пару новых кодов ошибок, значения все съезжают и то самое магическое число начинает указывать на другую ошибку. Начинается очень большое веселье. Систему типов компилятора надо использовать, а не бороться с ней!
--------------------
The truth is out there...
|
|
|
|
|
May 26 2017, 10:58
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата Может быть чтобы не было соблазна добавить в начало enum новый код ошибки лучше однозначно закрепить код ошибки в define, Может, если в коде не будет ненужных приведений типов и магических цифр - можно доавбялть в энумы что угодно и куда угодно? Enum - это не define. Дискутировать в плоскости enum vs define не корректно. Так можно и до define vs template дойти.... это всё глупости.
--------------------
The truth is out there...
|
|
|
|
|
May 26 2017, 13:33
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(sigmaN @ May 26 2017, 13:10)  Код typedef enum{ ERR_OK, ERR_SOMETHING, ERR_SOMETHING2 } Error_t; sigmaN: а почему Вы большими буквами значения енума определяете?
Сообщение отредактировал AlexRayne - May 26 2017, 13:33
|
|
|
|
|
May 26 2017, 14:38
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(AlexRayne @ May 26 2017, 16:33)  sigmaN: а почему Вы большими буквами значения енума определяете? Ну например, #def в проекте мало. А кодов на базе enum - очень много, и надо чтоб они хорошо выделялись из текста. Если нет какой-либо корп. политики-правил, то это дело целесообразности-удобства для аффтора  Я использую префикс eSET_MODE_RUN - с макросами уже не путаются. IMHO.
|
|
|
|
|
May 27 2017, 05:21
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (sigmaN @ May 26 2017, 22:45)  да это всё дело вкуса на самом деле... Это как раз не принципиально. Скорее не вкуса, а стиля. Заглавными символами принято описывать неизменяемые сущности типа констант, литералов. CODE #define SLON 1 const uint32_t MAMONT = 10; enum TSlon { KOT = 20, BEGEMOT };
...
TSlon Slon = KOT;
uint32_t x = Slon == BEGEMOT ? MAMONT : SLON; Сразу видно, где переменные, а где константы/литералы. Устоявшийся стиль. Стараюсь придерживаться его.
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
May 28 2017, 11:17
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата The enum type The compiler will use the smallest type required to hold enum constants, preferring signed rather than unsigned. When IAR Systems language extensions are enabled, and in C++, the enum constants and types can also be of the type long, unsigned long, long long, or unsigned long long. To make the compiler use a larger type than it would automatically use, define an enum constant with a large enough value. For example: /* Disables usage of the char type for enum */ enum Cards{Spade1, Spade2, DontUseChar=257}; http://supp.iar.com/FilesPublic/UPDINFO/00...opmentGuide.pdf page 227
--------------------
The truth is out there...
|
|
|
|
|
May 28 2017, 13:02
|

Adept
     
Группа: Свой
Сообщений: 3 469
Регистрация: 6-12-04
Из: Novosibirsk
Пользователь №: 1 343

|
QUOTE (sigmaN @ May 28 2017, 16:13)  Кстати вы заметили, как в С++11 поправили enum? Я имею ввиду enum class. Убрали возможность неявного приведения к int, ограничили областть видимости перечислений?
Конечно же для совместимости им пришлось оставить старый вариант, но тем не менее это еще раз доказывает, что ход мыслей программиста на плюсах должен всегда по возможности двигаться в сторону более строгой типизации. Да, улучшения налицо. Только enum class - это, я понял, как раз создание пространства имён для элементов перечисления, а тип там указывается, например, как enum EnumName : uint32_t (тип можно использовать любой целый, который подходит для хранения представления). Жаль, что некоторые популярные компиляторы - например, IAR, - не поддерживают C++11. Или я отстал о жизни?
--------------------
«Отыщи всему начало, и ты многое поймёшь» К. Прутков
|
|
|
|
|
May 28 2017, 13:50
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата Только enum class - это, я понял, как раз создание пространства имён для элементов перечисления Ну и это и плюс отсутствие неявного преобразования в int. Код enum class Color {RED, GREEN, BLUE}; int i = Color::RED; //compiler error! int i = static_cast<int>(Color::RED); //Ok Т.е. именно более сильная типизация во всех её проявлениях.
--------------------
The truth is out there...
|
|
|
|
|
May 29 2017, 07:42
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(dxp @ May 27 2017, 09:21)  Скорее не вкуса, а стиля. Заглавными символами принято описывать неизменяемые сущности типа констант, литералов.
Сразу видно, где переменные, а где константы/литералы. Устоявшийся стиль. Стараюсь придерживаться его. На моем опыте старались отличать макрос от немакроса. а вот как раз константы вполне подходит описывать как и обычные переменные, потому что на одном этапе разработки они переменные, на другом уже константы.
|
|
|
|
|
May 29 2017, 08:35
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(sigmaN @ May 28 2017, 13:17)  The compiler will use the smallest type required to hold enum constants, preferring Это всё известно и хорошо. Но, по уму, должно касаться только правил хранения этого enum. Т.е. - какого размера память выделить для его хранения. А не касательно аргументов функций передаваемых в регистрах. При передаче аргумента в регистре, он занимает весь регистр, и расширение байт->слово в таком случае - совершенно бессмысленная операция. Да ещё выполняемая перед каждым использованием значения этого enum-а. Конечно можно сделать: int x = x_enum; в начале функции, но опять теряется удобство отладки
|
|
|
|
|
May 29 2017, 08:55
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Ну тогда надо конкретизировать на примерах. Я так понимаю, что если для энума(для его хранения как вы говорите) будет выбран тип uint8_t то и аргумент функции тоже будет uint8_t и передача аргумента этой функции должна быть выполнена по тем-же правилам, что и передача аргумента с типом uint8_t. Как-то слишком уж глупо нечто, что хранится как uint8_t передавать в функцию по правилам uint16_t... Конкретизируем. STM8, IAR, функция с одинм аргументом. По правилам передачи аргументов 8ми битные передаются через регистр А, 16ти битные через регистр X Вы утрерждаете, что enum будет передан через регистр X не смотря на то, что в памяти будет занимать 8бит. Правильно я понял ход ваших мыслей? Код typedef enum{ TEST1, TEST2 } testEnum_t;
volatile testEnum_t te = TEST1; //to avoid inlining and optimizations
void testF(testEnum_t enm) { nop(); switch(enm) { case TEST1: GPIOA->ODR = 0; break; case TEST2: GPIOA->ODR = 0xff; break; } nop(); }
int main() { testF(te); } Код 327 testF(te); \ 000008 C6 .... LD A, L:te \ 00000B 9D NOP \ 00000C 27 09 JREQ L:??main_0 \ 00000E 4A DEC A \ 00000F 26 0A JRNE L:??main_1 \ 000011 35 FF 5000 MOV L:0x5000, #0xff \ 000015 20 04 JRA L:??main_1 \ ??main_0: \ 000017 725F 5000 CLR L:0x5000 \ ??main_1: \ 00001B 9D NOP Всё замечательно, аргумент функции в виде энума использует 8ми битный регистр A ибо весь энум помещается в 8бит. исправляем так, чтоб энум стал 16бит. Всё остальное не меняем Код typedef enum{ TEST1, TEST2 = 1024 } testEnum_t; компилируем.... Код 327 testF(te); \ 000008 CE .... LDW X, L:te \ 00000B 9D NOP \ 00000C 27 0B JREQ L:??main_0 \ 00000E 1D 0400 SUBW X, #0x400 \ 000011 26 0A JRNE L:??main_1 \ 000013 35 FF 5000 MOV L:0x5000, #0xff \ 000017 20 04 JRA L:??main_1 \ ??main_0: \ 000019 725F 5000 CLR L:0x5000 \ ??main_1: \ 00001D 9D NOP используется регистр X.
--------------------
The truth is out there...
|
|
|
|
|
May 29 2017, 09:34
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(sigmaN @ May 29 2017, 10:55)  Я так понимаю, что если для энума(для его хранения как вы говорите) будет выбран тип uint8_t то и аргумент функции тоже будет uint8_t и передача аргумента этой функции должна быть выполнена по тем-же правилам, что и передача аргумента с типом uint8_t. Как-то слишком уж глупо нечто, что хранится как uint8_t передавать в функцию по правилам uint16_t... Глупо - это если сказано про расширение размера данных не понимать, что речь естественно идёт не о 8-битной архитектуре. Я говорил про 32-битный IAR for ARM. И я написал: аргумент функции типа enum, а не u8 или u16.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|