|
|
  |
enum в С++, как определить неявное преобразование одного enum к другому? |
|
|
|
Jan 26 2007, 18:55
|

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

|
задача в следующем: Код enum command_t {CMD1, CMD2, CMD3 }; enum device1_cmd {CMD4 = CMD3, CMD5, CMD6}; enum device2_cmd {CMD7 = CMD3, CMD8, CMD9}; т.е. есть некий общий набор команд и его расширения специфичные для разных устройств. Теперь если я пытаюсь сделать device1_cmd = CMD9; то получаю ошибку a value of type "device2_cmd" cannot be used to initialize an entity of type "device1_cmd". Что правильно, и ради этого все enum и затевались. но такую же ошибку я получаю и на device1_cmd = CMD1; а вот этого не хочется. Т.е. хочется объявить оператор неявного преобразования из command_t в device1_cmd. Как это сделать? P.S. явное преобразование не хочется потому что можно случайно преобразовать совсем не то, что нужно.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 26 2007, 19:20
|

Местный
  
Группа: Свой
Сообщений: 304
Регистрация: 5-07-04
Из: г. Москва
Пользователь №: 259

|
Цитата(Сергей Борщ @ Jan 26 2007, 18:55)  Теперь если я пытаюсь сделать device1_cmd = CMD9; то получаю ошибку a value of type "device2_cmd" cannot be used to initialize an entity of type "device1_cmd". Что правильно, и ради этого все enum и затевались. Это же название типа, а не имя переменной. Определите переменную типа device1_cmd, а уж ей присваивайте значение
--------------------
Водку пьянствовать и безобразия нарушать!!!
|
|
|
|
|
Jan 26 2007, 20:30
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Ну это ведь действительно в плюсах определения ТИПОВ. Цитата(Сергей Борщ @ Jan 26 2007, 17:55)  device1_cmd = CMD1; Код device1_cmd aaaa; ...... device1_cmd aaaa = (device1_cmd)CMD1; По идее должно работать, хотя насколько мне помнится, никаких официальных требований к возможности такого преобразования не накладывается - посему, как "повезет". Вообще это извращение enum. Для "красоты" (device1_cmd)CMD1 можно в макросик замаскировать. А уж "НЕЯВНОЕ ПРЕОБРАЗОВАНИЕ" ни в какие ворота не лезет - не будет такого.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 26 2007, 22:14
|

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

|
Цитата(v_shamaev @ Jan 26 2007, 18:20)  Цитата(Сергей Борщ @ Jan 26 2007, 18:55)  device1_cmd = CMD9;
Это же название типа, а не имя переменной. Определите переменную типа device1_cmd, а уж ей присваивайте значение Извиняюсь, кончно же у меня что-то вроде Код device1_cmd cmd1; cmd1 = CMD5; // проходит, ибо "родное" cmd1 = CMD9; // не проходит, и правильно cmd1 = CMD1; // не проходит, а я хочу чтоб проходило. Вопрос - как сделать так, чтобы значение типа command_t неявно(!) при необходимости преобразовывалось к device1_cmd. С классами понятно - добавил конструктор и все. А как с enum? Цитата(zltigo @ Jan 26 2007, 19:30)  Код aaaa = (device1_cmd)CMD1; По идее должно работать Да, работает. Но меня это не устраивает. Для чего заводится enum? Чтобы ненароком не присвоить переменной значение которого она никогда не может иметь. Ошибка отлавливается компилятором и сразу. Если я делаю приведение типов вручную, компилятор уже ничего не проверяет. Он пропустит и Код aaaa = (device1_cmd)CMD9; а я хочу этого избежать.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 26 2007, 22:43
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Сергей Борщ @ Jan 26 2007, 21:14)  Для чего заводится enum? Чтобы ненароком не присвоить переменной значение которого она никогда не может иметь. Ага, и именно по этой причине желаемые НЕЯВНЫЕ прообразования с этим типом не могут быть в принципе. Цитата Если я делаю приведение типов вручную, компилятор уже ничего не проверяет. Он пропустит и Код aaaa = (device1_cmd)CMD9; а я хочу этого избежать. Лучше вручную, чем неявно. Fuzzy logic хочется  Для пущей "безопасности" можно пробовать городить еще макрос для присвоения с наворотами - имя переменной в и CMDx намеспайсинг спрятать - прямо уже не присвоишь.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jan 26 2007, 23:45
|

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

|
Цитата(Ivan_Kov @ Jan 26 2007, 22:02)  ихмо стоит попробовать определить операцию присвоения для типов device1_cmd и device2_cmd . 1)Пробовал. Или я что-то не так делал, но компилятор ругается "Оператор присваивания должен быть нестатическим членом". 2)Все равно оператор присваивания не решает проблемы, ибо не работает при инициализации структуры через {} Цитата(zltigo @ Jan 26 2007, 21:43)  Ага, и именно по этой причине желаемые НЕЯВНЫЕ прообразования с этим типом не могут быть в принципе. Ну почему? Я же определяя эту операцию преобразования явно указываю какие именно типы преобразовывать можно. Значит остальные неявно нельзя. Цитата(zltigo @ Jan 26 2007, 21:43)  Fuzzy logic хочется  Для пущей "безопасности" можно пробовать городить еще макрос для присвоения с наворотами - имя переменной в и CMDx намеспайсинг спрятать - прямо уже не присвоишь. Почему fuzzy? Для классов это нормальная операция: Код class A { public: A(int); }
class B { public: B(int); B(A); } А макросы в данном случае - некрасивое решение. неймспасинг тоже не проходит - пробовал. Мне надо "скрестить" command_t c device1_cmd и с device2_cm2, и чтобы при этом device1_cmd и device2_cmd не пересеклись.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 27 2007, 14:09
|
Местный
  
Группа: Свой
Сообщений: 351
Регистрация: 11-09-05
Из: Харьков
Пользователь №: 8 458

|
Цитата(Сергей Борщ @ Jan 26 2007, 23:14)  Код device1_cmd cmd1; cmd1 = CMD5; // проходит, ибо "родное" cmd1 = CMD9; // не проходит, и правильно cmd1 = CMD1; // не проходит, а я хочу чтоб проходило. Вопрос - как сделать так, чтобы значение типа command_t неявно(!) при необходимости преобразовывалось к device1_cmd. А как вам понравится такое "проходит" Код #include <iostream> using std::cout; using std::endl;
enum command_t { CMD1, CMD2, CMD3 = 3 }; enum device1_cmd { CMD4 = CMD3, CMD5 = 8, CMD6 = 12 }; enum device2_cmd { CMD7 = CMD3, CMD8, CMD9 };
int main( int argc, char* argv[] ) { device1_cmd cmd1; cmd1 = CMD5; // проходит, ибо "родное" cmd1 = device1_cmd( CMD4 | CMD5 ); cout << cmd1 << endl; exit( EXIT_SUCCESS ); }; и чтоб не быть голословным - вот вам прогон (gcc 3.3.5 - QNX 6.3.2): Код /root/enum # enum 11 Вы хотите использовать enum совсем не в том качестве, для которого они предназначены: enum фактически не вводит нового класса, а всего лишь определяет синоним, но за счёт именной (а не структурной) типизации С++, между ними запрещены преобразования (но не запрещено присвоить "хрен знает что", что и делается в примере выше). Именно из-за этого вы не можете (и не сможете) переопределить операцию "=" - эта операция должна определяться как член класса (внутри класса) - а класса то нет? P.S. специально не отвечал вчера - засомневался  , решил перепровериться у Страуструпа - у него утверждается в точности то же самое. Тем более, нереалистично желание переложить контроль допустимости значений на компилятор (как я понял, вы хотели бы чтоб это были проверки времени компиляции а не времени исолнения): значение, которое вы захотите присваивать может динамически измениться на выполнении (как в примере) - кто тогда будет проверять его допустимость? Цитата(Сергей Борщ @ Jan 26 2007, 23:14)  С классами понятно - добавил конструктор и все. А как с enum? Вот и определите то что вы хотите внутри класса (вложив внутрь класса) ... определите для него "=" ... но и тут будут проблемы - ну и что вы будуте делать когда выясниться что присваивается недопустимое значение? P.S. предполагаю (чутьё подсказывает  ) - что то что вы хотите вы могли бы определить используя template ... в стиле a'la Александреску  ... Попробуйте.
|
|
|
|
|
Jan 27 2007, 18:02
|

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

|
Цитата(Olej @ Jan 27 2007, 13:09)  А как вам понравится такое "проходит" Код cmd1 = device1_cmd( CMD4 | CMD5 ); Так ведь это явное приведение. Против этого я и не протестую  Цитата(Olej @ Jan 27 2007, 13:09)  Вы хотите использовать enum совсем не в том качестве, для которого они предназначены спасибо за подробное объяснение. Кажется, проясняется. Цитата(Olej @ Jan 27 2007, 13:09)  Тем более, нереалистично желание переложить контроль допустимости значений на компилятор (как я понял, вы хотели бы чтоб это были проверки времени компиляции а не времени исолнения): значение, которое вы захотите присваивать может динамически измениться на выполнении (как в примере) - кто тогда будет проверять его допустимость? Ну почему же нереалистично? Для классов реалистично "и тут хочу!". А во время исполнения сдуру можно все что угодно через указатели привести и никакой компилятор не спасет. Цитата(Olej @ Jan 27 2007, 13:09)  Цитата(Сергей Борщ @ Jan 26 2007, 23:14)  С классами понятно - добавил конструктор и все. А как с enum?
Вот и определите то что вы хотите внутри класса (вложив внутрь класса) ... определите для него "=" ... Видимо так и придется. Только если я помещу enum внутрь класса, то мне придется постоянно писать command_t::CMD1, а это несколько утомительно и опять же требует помнить какие команды описаны в каком классе. Ладно, буду городить класс. Цитата(Olej @ Jan 27 2007, 13:09)  P.S. предполагаю (чутьё подсказывает  ) - что то что вы хотите вы могли бы определить используя template ... в стиле a'la Александреску  ... Попробуйте. Кто такой Александреску? Можно пример его стиля?
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 27 2007, 22:07
|
Местный
  
Группа: Свой
Сообщений: 351
Регистрация: 11-09-05
Из: Харьков
Пользователь №: 8 458

|
Цитата(Сергей Борщ @ Jan 27 2007, 19:02)  Цитата(Olej @ Jan 27 2007, 13:09)  А как вам понравится такое "проходит" Код cmd1 = device1_cmd( CMD4 | CMD5 ); Так ведь это явное приведение. Против этого я и не протестую  Да, но явное приведение того, что приводится не должно ни в каком случае: среди допустимых значений перечисления device1_cmd - не допускается и не предусмотрено значение 11! (только: CMD4 = 3, CMD5 = 8, CMD6 = 12 - что такое 11 ?). Цитата(Сергей Борщ @ Jan 27 2007, 19:02)  Ну почему же нереалистично? Для классов реалистично "и тут хочу!". А во время исполнения сдуру можно все что угодно через указатели привести и никакой компилятор не спасет. Именно поэтому допустимость приведения (присвоения) нужно контролировать на фазе исполнения, а не компиляции. Цитата(Сергей Борщ @ Jan 27 2007, 19:02)  Видимо так и придется. Только если я помещу enum внутрь класса, то мне придется постоянно писать command_t::CMD1, а это несколько утомительно и опять же требует помнить какие команды описаны в каком классе. Ладно, буду городить класс. Не обязательно оперировать сложными именами типа command_t::CMD1 - вы можете #define определить внутренние имена как простые внешние, так весь QNX переопределён. P.S. может вам покажется полезным кое-что о трюках QNX, которые можно перенести в другие окружения, посмотреть здесь: http://qnxclub.net/modules.php?name=Forums...ewforum&f=7Цитата(Сергей Борщ @ Jan 27 2007, 19:02)  Кто такой Александреску? Можно пример его стиля? Андрей Александреску "Современное проектирование на С++" Пер. с англ. - М. : Издательский дом "Вильямс", 2002. - 336 стр. Много обсуждаемая книжка: одни принимают с восторгом, другия ругаются брызгая слюной  .
|
|
|
|
|
Jan 28 2007, 01:35
|

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

|
Цитата(Olej @ Jan 27 2007, 21:07)  Да, но явное приведение того, что приводится не должно ни в каком случае: среди допустимых значений перечисления device1_cmd - не допускается и не предусмотрено значение 11! ........... Именно поэтому допустимость приведения (присвоения) нужно контролировать на фазе исполнения, а не компиляции. Как говорил мой бывший шеф "Полный" Лемис "сдуру можно и х.. сломать". Мы говорим о разном. Я хотел разрешить совершенно конкретное неявное приведение. Цитата(Olej @ Jan 27 2007, 21:07)  Не обязательно оперировать сложными именами типа command_t::CMD1 - вы можете #define определить внутренние имена как простые внешние, так весь QNX переопределён. Да, можно. Но личная практика показывает что это верный шаг к путанице и трудноотлавливаемым ошибкам. Цитата(Olej @ Jan 27 2007, 21:07)  P.S. может вам покажется полезным кое-что о трюках QNX, которые можно перенести в другие окружения, посмотреть здесь: http://qnxclub.net/modules.php?name=Forums...ewforum&f=7.......... Андрей Александреску "Современное проектирование на С++" Пер. с англ. - М. : Издательский дом "Вильямс", 2002. - 336 стр. Много обсуждаемая книжка: одни принимают с восторгом, другия ругаются брызгая слюной  . Спасибо, пошел изучать. P.S. Тема не закрыта, рад выслушать любые альтернативные идеи.
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 28 2007, 13:41
|
Местный
  
Группа: Свой
Сообщений: 351
Регистрация: 11-09-05
Из: Харьков
Пользователь №: 8 458

|
Цитата(Сергей Борщ @ Jan 28 2007, 02:35)  Да, можно. Но личная практика показывает что это верный шаг к путанице и трудноотлавливаемым ошибкам. Мы, возможно, говорим о несколько разных вещах... Для пояснения того, что я имею в виду - я поискал файлы *.h в определениях QNX (где это встречается "валом"), но как-то сразу не нашёл  ... но и эти определения заимствованы в QNX из FreeBSD и, ещё большим образом, из NetBSD, где этого будет полно... Я не нашёл быстро файлы системных определений, но зато нашёл искусственный пример, который когда-то давненько писал для студентов, для объяснений таких переопределений: Код #include <stdlib.h>
struct PI { void* ____p; int ____i; };
union L{ struct { long long __d1; long long __d2; } I; struct { struct PI __pi1; struct PI __pi2; } V; char c[ 0 ]; }; #define d1 I.__d1 #define d2 I.__d2 #define p1 V.__pi1.____p #define p2 V.__pi2.____p
typedef union L Z;
int main() { Z a; a.d1 = 1; a.d2 = 2; printf( "%X %X\n", a.p1, a.p2 ); int i; for( i = 0; i < sizeof( Z ) / sizeof( *a.c ); i++ ) printf( "%d ", a.c[ i ] ); printf( "\n" ); return 0; }; - обратите внимание - как a2.V.__pi2.____p превратилось в простенькое  a.p2 - это просто синонимическая замена, которую 1 раз сделали - и забыли ("... взула и забула..."(с) - так в рекламе говорят?  ). При чём в определениях ОС (QNX, FreeBSD, NetBSD) перед такими определениями стоят #ifndef с комментариями что-то в роде: "для тех компиляторов С, которые не допускают неименнованные компоненты внутри структур" ... а какие допускают? - стандарт ANSI или K&R не допускают ничего подобного, но я знаю только 1 такой компилятор - Watcom, который позволял и был очень популярный. Т.е. мы просто "вытаскиваем" в глубине определённые составные имена "на поверхность" (вот, собственно, для того, чтобы они потом, когда уже "забудем", никогда не пересеклись с реальными именами - внутренним полям они и дают такие "замысловатые" названия с _ на начале или на конце) - никаких неприятностей после таких статических переопределений ожидать не стоит. P.S. (добавлено позже) вот только сейчас вспомнил, что этот вопрос подробнее обсуждался вот здесь: http://qnxclub.net/modules.php?name=Forums...;highlight=pure
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|