Как стать автором
Обновить

Комментарии 14

Просто мысли в слух. Мне кажется, описанное можно сделать чуть красивее, используя обобщенные гарантировано константные выражения (из С++11). Пример из FAQ от Бьярна Страуструпа:

enum Flags { good=0, fail=1, bad=2, eof=4 };

constexpr int operator|(Flags f1, Flags f2) { 
  return Flags(int(f1)|int(f2)); 
}

void f(Flags x)
{
  switch (x) {
    case bad:         /* ... */ break;
    case eof:         /* ... */ break;
    case bad|eof:     /* ... */ break;
    default:          /* ... */ break;
  }
}
Странно, а почему операция «или»? По контексту вроде должна быть «и» (&)? Или я неправильно понимаю эту конструкцию? Иначе, во-первых, становится не очень понятно, почему bad и eof идут сначала, а потом case «bad или eof». Я понимаю, что если сделать оператор &, который будет конъюнкцией это тоже зло, но мне кажется, что оно меньшее.

enum Flags { good=0, fail=1, bad=2, eof=4 };

/* ВНИМАНИЕ! Оператор & для типа Flags используется как конъюнкция, а не в обычном смысле дизъюнкции */
constexpr int operator&(Flags f1, Flags f2) { 
  return Flags(int(f1)|int(f2)); 
}

void f(Flags x)
{
  switch (x) {
    case bad:         /* ... */ break;
    case eof:         /* ... */ break;

/* Теперь это выражение выглядит логичным, по меньшей мере в switch/case */
    case bad&eof:     /* ... */ break; 

    default:          /* ... */ break;
  }
}
Не думаю, что стоит ещё больше путать человека, вводя &. Уж как сказали, лучше тогда +.
А может быть лучше использовать operator+, так можно притянуть за уши контекст swithc/case не нарушая поведение оператора. Типа case «bad+eof» — «в случае bad [да ещё] плюс eof». =)
В данном примере логическое "И" просто обнулит результат (2 & 4 = 0010b & 0100b = 0). И поэтому здесь надо использовать именно "Или", или обычное сложение.
Это, во-первых, «поразрядное и», во-вторых оно перегружено таким образом, что на самом деле реализует операцию «поразрядное или». В этом то и проблема: считается дурным тоном перегружать операторы неочевидным образом (впрочем есть вполне логичные исключения см. boost::xpressive например).
Тут в чём дело, синтаксически, в примере приведённом Andrey2008 читается как «в случае если bad ИЛИ eof», хотя в реальности (семантически) оно обозначает «в случае если bad И eof». Поэтому я и предложил заменить это оператором & (поразрядное И), изменив его поведение на порязрядное ИЛИ — тогда синтаксически и семантически выражение становится логичным и очевидным. Однако, неочивидным становится поведение, собственно, operator&.
Видимо лучше всё-таки использовать operator+.
Пересмотрел Ваш вариант еще раз, прошу прощения, был невнимателен
Тогда уж лучше оператор запятая попробовать заюзать

...
case (bad,eof):
...


было бы куда читабильнее.
Прошу прощения, не совсем понял мысль с bad | eof в контексте моего примера. Не могли бы Вы пояснить.
Мысль, что возможно с помощью этого можно сделать более лаконично и с меньшим количеством макросов. Хотя не факт конечно. Просто вспомнилась эта новая возможность.
Если параметры имеют тип некоторого перечисления, то действительно ваш пример позволяет сделать код лаконичнее.
Но в моем примере тип параметров int и как как оба параметры могут содержать значения из одного диапазона, то к сожалению нельзя использовать поразрядные операции (ни "И", ни "ИЛИ"), например "case 4 | 6:" и "case 6 | 4:" будут генерировать одно и тоже значение. Чтобы избежать этого и различать эти два случая я применил простенькую хеш функцию, которая сдвигает первый параметр N влево на число разрядов достаточное чтобы вместить максимально возможное значение второго параметра M (Максимальное значение задается константой MAX_M). Функция расчета хеша объявлена с ключевым словом constexpr. Таким образом данная функция возвращает константу времени компиляции.

Ну а макросы я добавил сначала для удобства сокрытия дополнительного кода и проверок, а уже остальные добавил для общей стилистики.
Видимо зря). Спасибо за Ваши комментарии.
Вообще-то побитовые операторы (равно как и все бинарные операторы) являются по грамматике constant_expression даже в plain ansi c, и, следовательно, допустимы в switch-case конструкциях. Короче, если убрать constexpr int operator|, это никак не отразится на работоспособности.

Только странно видеть такие вещи от Страуструпа: на C++11 можно было бы придумать что-нибудь похитрее, например, good | bad -> ошибка компиляции.
Интересный вариант. В продакшене я бы правда не рискнул это применять по соображениям понятности другим разработчикам.

Читая посты на Хабре, наткнулся на такой вопрос

Ту задачу реализовал через множество классов с виртуальными методами. Не сказать что идеальное решение, но поддерживать достаточно удобно, как оказалось.
Вообще такая задача решается с помощью мультиметодов. Одна из реализаций есть у Александреску. Это если ваши м и
н соответствуют классам. Если же это просто числа, можно создать матрицу функторов и динамически инициировать ее необходимыми функциями.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории