Можно ли избавиться от вложенного switch в C++

Рейтинг: 0Ответов: 2Опубликовано: 23.03.2023

Допустим, на вход приходит структура struct с 3мя полями a, b, c. Есть switch :

switch(struct.type)
case a:
   switch(a.type)
      case type1:
      {
 
      } 
      case type2:
      {
 
      } 
case b:
   switch(b.type)
      case type3:
      {
 
      } 
      case type4:
      {
 
      } 
case c:
   switch(c.type)
      case type5:
      {
 
      } 
      case type6:
      {
 
      }

Соответственно, type у каждого поля а, b и с может быть не по 2, а больше, да и самих полей может быть больше трех. Вопрос в том, можно ли в данном примере избавиться от вложенного switch и сделать код более читаемым и элегантным?

Ответы

▲ 0Принят

В принципе нет. Единственное, возможно проще написать два линейных switch - один по struct.type, второй по struct.type.type. Т.к. struct.type - это один тип данных, а не разные объекты. Но это зависит от задачи (если действия по struct.type.type не зависят от действий по struct.type)

// выглядеть будет не так
switch(struct.type)
case a:
   switch(a.type)
case b:
   switch(b.type)
case c:
   switch(c.type)

// а так
switch(struct.type)
case a:
   switch(struct.type.type)
      case type1:
case b:
   switch(struct.type.type)
      case type3:
case c:
   switch(struct.type.type)
      case type5:

// и возможно проще будет сделать 2 линейных последовательно
switch(struct.type)
case a:
case b:
case c:

switch(struct.type.type)
case type1:
case type2:
case type3:

В любом случае вопрос академический, без привязки к конкретной задаче точно сказать нельзя.
А если у вас разные типы, то тогда у вас struct.type - это std::variant или std::any. Но это уже область метапрограммирования.

▲ 4

Прежде всего следует помнить, что выражение в switch должно быть целочисленным, а в case константным (т.е. конкретная величина известна во время компиляции).

Поэтому, если у в вашей задаче значения struct.type и подтипов (a.type, b.type, ...) помещаются, скажем, в 16 бит, то можно написать следующий код:

  uint32_t gen_type = (struct.type << 16);

  switch (struct.type) {
  case a: gen_type |= a.type; break;
  case b: gen_type |= b.type; break;
    ...
  }
#define GEN_TYPE(T1,T2) (((T1) << 16) | (T2))
  switch (gen_type) {
  case GEN_TYPE(a_type, type1) :
     ....
  case GEN_TYPE(a_type, type2) :
     ....
  case GEN_TYPE(b_type, type3) :
     ....
     ....
  }

Вероятно, в реальной пограмме имеет смысл выделить код, формирующий переменную gen_type, в отдельную функцию. Тогда получим что-то вроде

  switch ((gen_type = get_type(struct, a, b, c))) {
  case GEN_TYPE(a_type, type1) : 
     ....
  case GEN_TYPE(a_type, type2) :
     ....
  case GEN_TYPE(b_type, type3) :
     ....
     ....
  }