Комментарии 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+.
Тут в чём дело, синтаксически, в примере приведённом Andrey2008 читается как «в случае если bad ИЛИ eof», хотя в реальности (семантически) оно обозначает «в случае если bad И eof». Поэтому я и предложил заменить это оператором & (поразрядное И), изменив его поведение на порязрядное ИЛИ — тогда синтаксически и семантически выражение становится логичным и очевидным. Однако, неочивидным становится поведение, собственно, operator&.
Видимо лучше всё-таки использовать operator+.
Тогда уж лучше оператор запятая попробовать заюзать
было бы куда читабильнее.
...
case (bad,eof):
...
было бы куда читабильнее.
Прошу прощения, не совсем понял мысль с bad | eof в контексте моего примера. Не могли бы Вы пояснить.
Мысль, что возможно с помощью этого можно сделать более лаконично и с меньшим количеством макросов. Хотя не факт конечно. Просто вспомнилась эта новая возможность.
Если параметры имеют тип некоторого перечисления, то действительно ваш пример позволяет сделать код лаконичнее.
Но в моем примере тип параметров int и как как оба параметры могут содержать значения из одного диапазона, то к сожалению нельзя использовать поразрядные операции (ни "И", ни "ИЛИ"), например "case 4 | 6:" и "case 6 | 4:" будут генерировать одно и тоже значение. Чтобы избежать этого и различать эти два случая я применил простенькую хеш функцию, которая сдвигает первый параметр N влево на число разрядов достаточное чтобы вместить максимально возможное значение второго параметра M (Максимальное значение задается константой MAX_M). Функция расчета хеша объявлена с ключевым словом constexpr. Таким образом данная функция возвращает константу времени компиляции.
Ну а макросы я добавил сначала для удобства сокрытия дополнительного кода и проверок, а уже остальные добавил для общей стилистики.
Видимо зря). Спасибо за Ваши комментарии.
Но в моем примере тип параметров int и как как оба параметры могут содержать значения из одного диапазона, то к сожалению нельзя использовать поразрядные операции (ни "И", ни "ИЛИ"), например "case 4 | 6:" и "case 6 | 4:" будут генерировать одно и тоже значение. Чтобы избежать этого и различать эти два случая я применил простенькую хеш функцию, которая сдвигает первый параметр N влево на число разрядов достаточное чтобы вместить максимально возможное значение второго параметра M (Максимальное значение задается константой MAX_M). Функция расчета хеша объявлена с ключевым словом constexpr. Таким образом данная функция возвращает константу времени компиляции.
Ну а макросы я добавил сначала для удобства сокрытия дополнительного кода и проверок, а уже остальные добавил для общей стилистики.
Видимо зря). Спасибо за Ваши комментарии.
Вообще-то побитовые операторы (равно как и все бинарные операторы) являются по грамматике constant_expression даже в plain ansi c, и, следовательно, допустимы в switch-case конструкциях. Короче, если убрать
Только странно видеть такие вещи от Страуструпа: на C++11 можно было бы придумать что-нибудь похитрее, например, good | bad -> ошибка компиляции.
constexpr int operator|
, это никак не отразится на работоспособности.Только странно видеть такие вещи от Страуструпа: на C++11 можно было бы придумать что-нибудь похитрее, например, good | bad -> ошибка компиляции.
Интересный вариант. В продакшене я бы правда не рискнул это применять по соображениям понятности другим разработчикам.
Ту задачу реализовал через множество классов с виртуальными методами. Не сказать что идеальное решение, но поддерживать достаточно удобно, как оказалось.
Читая посты на Хабре, наткнулся на такой вопрос
Ту задачу реализовал через множество классов с виртуальными методами. Не сказать что идеальное решение, но поддерживать достаточно удобно, как оказалось.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Switch для двух параметров в С++