Jump to content

    
Sign in to follow this  
Сергей Борщ

enum в С++

Recommended Posts

задача в следующем:

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. явное преобразование не хочется потому что можно случайно преобразовать совсем не то, что нужно.

Share this post


Link to post
Share on other sites
Теперь если я пытаюсь сделать

device1_cmd = CMD9;

то получаю ошибку a value of type "device2_cmd" cannot be used to initialize an entity of type "device1_cmd". Что правильно, и ради этого все enum и затевались.

 

Это же название типа, а не имя переменной. Определите переменную типа device1_cmd, а уж ей присваивайте значение

Share this post


Link to post
Share on other sites

Ну это ведь действительно в плюсах определения ТИПОВ.

 

device1_cmd = CMD1;

 

device1_cmd aaaa;
......
device1_cmd aaaa = (device1_cmd)CMD1;

По идее должно работать, хотя насколько мне помнится, никаких официальных требований к возможности такого преобразования не накладывается - посему, как "повезет".

Вообще это извращение enum. Для "красоты" (device1_cmd)CMD1 можно в макросик замаскировать.

А уж "НЕЯВНОЕ ПРЕОБРАЗОВАНИЕ" ни в какие ворота не лезет - не будет такого.

Share this post


Link to post
Share on other sites

device1_cmd = CMD9;

Это же название типа, а не имя переменной. Определите переменную типа device1_cmd, а уж ей присваивайте значение

Извиняюсь, кончно же у меня что-то вроде

device1_cmd cmd1;
cmd1 = CMD5;    // проходит, ибо "родное"
cmd1 = CMD9;    // не проходит, и правильно
cmd1 = CMD1;    // не проходит, а я хочу чтоб проходило.

Вопрос - как сделать так, чтобы значение типа command_t неявно(!) при необходимости преобразовывалось к device1_cmd. С классами понятно - добавил конструктор и все. А как с enum?

 

aaaa = (device1_cmd)CMD1;

По идее должно работать

Да, работает. Но меня это не устраивает. Для чего заводится enum? Чтобы ненароком не присвоить переменной значение которого она никогда не может иметь. Ошибка отлавливается компилятором и сразу. Если я делаю приведение типов вручную, компилятор уже ничего не проверяет. Он пропустит и

aaaa = (device1_cmd)CMD9;

а я хочу этого избежать.

Share this post


Link to post
Share on other sites
Для чего заводится enum? Чтобы ненароком не присвоить переменной значение которого она никогда не может иметь.

Ага, и именно по этой причине желаемые НЕЯВНЫЕ прообразования с этим типом не могут быть в принципе.

Если я делаю приведение типов вручную, компилятор уже ничего не проверяет. Он пропустит и

aaaa = (device1_cmd)CMD9;

а я хочу этого избежать.

Лучше вручную, чем неявно.

Fuzzy logic хочется :) :)

Для пущей "безопасности" можно пробовать городить еще макрос для присвоения с наворотами - имя переменной в и CMDx намеспайсинг спрятать - прямо уже не присвоишь.

Share this post


Link to post
Share on other sites

C++ позволяет переопределять операции. ихмо стоит попробовать определить операцию присвоения для типов device1_cmd и device2_cmd .

Share this post


Link to post
Share on other sites
ихмо стоит попробовать определить операцию присвоения для типов device1_cmd и device2_cmd .
1)Пробовал. Или я что-то не так делал, но компилятор ругается "Оператор присваивания должен быть нестатическим членом".

2)Все равно оператор присваивания не решает проблемы, ибо не работает при инициализации структуры через {}

 

 

Ага, и именно по этой причине желаемые НЕЯВНЫЕ прообразования с этим типом не могут быть в принципе.
Ну почему? Я же определяя эту операцию преобразования явно указываю какие именно типы преобразовывать можно. Значит остальные неявно нельзя.

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 не пересеклись.

Share this post


Link to post
Share on other sites
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 фактически не вводит нового класса, а всего лишь определяет синоним, но за счёт именной (а не структурной) типизации С++, между ними запрещены преобразования (но не запрещено присвоить "хрен знает что", что и делается в примере выше). Именно из-за этого вы не можете (и не сможете) переопределить операцию "=" - эта операция должна определяться как член класса (внутри класса) - а класса то нет? :ohmy: :)

P.S. специально не отвечал вчера - засомневался :) , решил перепровериться у Страуструпа - у него утверждается в точности то же самое.

 

Тем более, нереалистично желание переложить контроль допустимости значений на компилятор (как я понял, вы хотели бы чтоб это были проверки времени компиляции а не времени исолнения): значение, которое вы захотите присваивать может динамически измениться на выполнении (как в примере) - кто тогда будет проверять его допустимость?

 

С классами понятно - добавил конструктор и все. А как с enum?

 

Вот и определите то что вы хотите внутри класса (вложив внутрь класса) ... определите для него "=" ... но и тут будут проблемы - ну и что вы будуте делать когда выясниться что присваивается недопустимое значение?

 

P.S. предполагаю (чутьё подсказывает :) ) - что то что вы хотите вы могли бы определить используя template ... в стиле a'la Александреску :) ...

Попробуйте.

Share this post


Link to post
Share on other sites
А как вам понравится такое "проходит"

   cmd1 = device1_cmd( CMD4 | CMD5 );

Так ведь это явное приведение. Против этого я и не протестую :)

Вы хотите использовать enum совсем не в том качестве, для которого они предназначены
спасибо за подробное объяснение. Кажется, проясняется.

 

Тем более, нереалистично желание переложить контроль допустимости значений на компилятор (как я понял, вы хотели бы чтоб это были проверки времени компиляции а не времени исолнения): значение, которое вы захотите присваивать может динамически измениться на выполнении (как в примере) - кто тогда будет проверять его допустимость?
Ну почему же нереалистично? Для классов реалистично "и тут хочу!". А во время исполнения сдуру можно все что угодно через указатели привести и никакой компилятор не спасет.

С классами понятно - добавил конструктор и все. А как с enum?

Вот и определите то что вы хотите внутри класса (вложив внутрь класса) ... определите для него "=" ...

Видимо так и придется. Только если я помещу enum внутрь класса, то мне придется постоянно писать command_t::CMD1, а это несколько утомительно и опять же требует помнить какие команды описаны в каком классе. Ладно, буду городить класс.

 

P.S. предполагаю (чутьё подсказывает :) ) - что то что вы хотите вы могли бы определить используя template ... в стиле a'la Александреску :) ...

Попробуйте.

Кто такой Александреску? Можно пример его стиля?

Share this post


Link to post
Share on other sites

А как вам понравится такое "проходит"

   cmd1 = device1_cmd( CMD4 | CMD5 );

Так ведь это явное приведение. Против этого я и не протестую :)

Да, но явное приведение того, что приводится не должно ни в каком случае: среди допустимых значений перечисления device1_cmd - не допускается и не предусмотрено значение 11! (только: CMD4 = 3,

CMD5 = 8, CMD6 = 12 - что такое 11 ?).

 

Ну почему же нереалистично? Для классов реалистично "и тут хочу!". А во время исполнения сдуру можно все что угодно через указатели привести и никакой компилятор не спасет.

Именно поэтому допустимость приведения (присвоения) нужно контролировать на фазе исполнения, а не компиляции.

 

Видимо так и придется. Только если я помещу enum внутрь класса, то мне придется постоянно писать command_t::CMD1, а это несколько утомительно и опять же требует помнить какие команды описаны в каком классе. Ладно, буду городить класс.

Не обязательно оперировать сложными именами типа command_t::CMD1 - вы можете #define определить внутренние имена как простые внешние, так весь QNX переопределён.

P.S. может вам покажется полезным кое-что о трюках QNX, которые можно перенести в другие окружения, посмотреть здесь:

http://qnxclub.net/modules.php?name=Forums...ewforum&f=7

 

Кто такой Александреску? Можно пример его стиля?

Андрей Александреску "Современное проектирование на С++" Пер. с англ. - М. : Издательский дом "Вильямс", 2002. - 336 стр.

Много обсуждаемая книжка: одни принимают с восторгом, другия ругаются брызгая слюной ;).

Share this post


Link to post
Share on other sites
Да, но явное приведение того, что приводится не должно ни в каком случае: среди допустимых значений перечисления device1_cmd - не допускается и не предусмотрено значение 11!

...........

Именно поэтому допустимость приведения (присвоения) нужно контролировать на фазе исполнения, а не компиляции.

Как говорил мой бывший шеф "Полный" Лемис "сдуру можно и х.. сломать". Мы говорим о разном. Я хотел разрешить совершенно конкретное неявное приведение.

Не обязательно оперировать сложными именами типа command_t::CMD1 - вы можете #define определить внутренние имена как простые внешние, так весь QNX переопределён.
Да, можно. Но личная практика показывает что это верный шаг к путанице и трудноотлавливаемым ошибкам.

 

P.S. может вам покажется полезным кое-что о трюках QNX, которые можно перенести в другие окружения, посмотреть здесь:

http://qnxclub.net/modules.php?name=Forums...ewforum&f=7

..........

Андрей Александреску "Современное проектирование на С++" Пер. с англ. - М. : Издательский дом "Вильямс", 2002. - 336 стр.

Много обсуждаемая книжка: одни принимают с восторгом, другия ругаются брызгая слюной ;).

Спасибо, пошел изучать.

 

P.S. Тема не закрыта, рад выслушать любые альтернативные идеи.

Share this post


Link to post
Share on other sites
Да, можно. Но личная практика показывает что это верный шаг к путанице и трудноотлавливаемым ошибкам.

 

Мы, возможно, говорим о несколько разных вещах...

Для пояснения того, что я имею в виду - я поискал файлы *.h в определениях QNX (где это встречается "валом"), но как-то сразу не нашёл :angry2: ... но и эти определения заимствованы в 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 превратилось в простенькое :tongue: a.p2 - это просто синонимическая замена, которую 1 раз сделали - и забыли ("... взула и забула..."(с) - так в рекламе говорят? :laugh: ). При чём в определениях ОС (QNX, FreeBSD, NetBSD) перед такими определениями стоят #ifndef с комментариями что-то в роде: "для тех компиляторов С, которые не допускают неименнованные компоненты внутри структур" ... а какие допускают? - стандарт ANSI или K&R не допускают ничего подобного, но я знаю только 1 такой компилятор - Watcom, который позволял и был очень популярный. Т.е. мы просто "вытаскиваем" в глубине определённые составные имена "на поверхность" (вот, собственно, для того, чтобы они потом, когда уже "забудем", никогда не пересеклись с реальными именами - внутренним полям они и дают такие "замысловатые" названия с _ на начале или на конце) - никаких неприятностей после таких статических переопределений ожидать не стоит.

 

P.S. (добавлено позже) вот только сейчас вспомнил, что этот вопрос подробнее обсуждался вот здесь:

http://qnxclub.net/modules.php?name=Forums...;highlight=pure

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this