Коль уж пошла такая синтаксическая пьянка, то хочу (просто в порядке фантазии) обсудить конструкцию switch в C++. Главная, как мне кажется, неприятность этой конструкции в том, что нужно после каждого case писать по break. Это и забывают нередко, к тому же.
Но, с другой стороны, необязательность break даёт две фишки:
— использовать один case, как фрагмент другого
— ставить несколько меток одному и тому же коду
Чтоб не быть голословным, придумаю пример — по коду причины отказа нужно вычислить текст отказа и некоторый условный "вес", тяжесть этой причины. Код несколько надуманный, но уж что пришло в голову под конец рабочего дня
string text; // текст причиныunsigned reasonWeight = 0; // "тяжесть" причиныswitch( reasonCode )
{
case REASON_1:
case REASON_2:
text = "Текст главной причины отказа";
reasonWeight = 1000;
break;
case REASON_3:
text = " (но с дополнением)";
reasonWeight = 500;
case REASON_4:
text = "Текст второстепенной причины" + text;
reasonWeight += 1000;
break;
case REASON_5:
text = "А, считай и не причина"; // вес остался нулевым по умолчаниюbreak;
default:
text = "(встречен отказ с неизвестным кодом)";
reasonWeight = REASON_WEIGHT_MAX;
}
Фактически, мы тут имеем goto на метку. Не знаю, как вам, но мне поведение case REASON_3, case REASON_4 кажется несколько ненаглядным, что ли...
Предлагаю вариант улучшения номер раз.
string text; // текст причиныunsigned reasonWeight = 0; // "тяжесть" причиныswitch( reasonCode )
{
mixin addAppendix
{
reasonWeight += 500;
text += " (но с дополнением)";
}
case REASON_1, REASON_2:
text = "Текст главной причины отказа";
reasonWeight = 1000;
case REASON_3: addAppendix();
case REASON_4:
text = "Текст второстепенной причины"
reasonWeight = 1000;
addAppendix();
case REASON_5: text = "А, считай и не причина";
default:
text = "(встречен отказ с неизвестным кодом)";
reasonWeight = REASON_WEIGHT_MAX;
};
Мало того, что код стал компактней, так за счёт миксина (видимого только внутри данного switch) он ещё и приобрёл привычный процедурный стиль, addAppendix теперь выделен в особую сущность с чётко определёнными функциями. Не нравятся миксины? Ну, никто не отменял макросы
Однако, мне тут продолжает не нравится нагромождение ключевых слов case... тут бы по пустой строке между ними вставить, но это почти убивает пользу от исключения break...
Поэтому, смотрите вариант номер два.
string text; // текст причиныunsigned reasonWeight = 0; // "тяжесть" причины
mixin addAppendix
{
reasonWeight += 500;
text += " (но с дополнением)";
}
switch( reasonCode )
REASON_1, REASON_2 {
text = "Текст главной причины отказа";
reasonWeight = 1000;
}
REASON_3 { addAppendix(); }
REASON_4 {
text = "Текст второстепенной причины"
reasonWeight = 1000;
addAppendix();
}
REASON_5 { text = "А, считай и не причина"; }
default {
text = "(встречен отказ с неизвестным кодом)";
reasonWeight = REASON_WEIGHT_MAX;
};
Обращаю внимание на ";" в конце — это признак конца оператора.
Что лучше не стало? Жаль. У кого какие ещё будут фантазии на тему?
Здравствуйте, tarkil, Вы писали:
T>Коль уж пошла такая синтаксическая пьянка, то хочу (просто в порядке фантазии) обсудить конструкцию switch в C++. Главная, как мне кажется, неприятность этой конструкции в том, что нужно после каждого case писать по break. Это и забывают нередко, к тому же.
Это точно.
Теперь чуть-чуть фантазии.
НИКОГДА не пишите после метки оператора выбора несколько операторов. Тогда всегда будет процедурный стиль (извините, что не даю ваших фраз).
Получим тогда ()
switch( reasonCode )
{
case R1: func1(); break;
case R2: func1(); break;
case REASON_3: func2(); break;
default: ...
}
Между прочим в Delphi не нужно писать break, но там приходится ставить begin и end, если хочешь написать более одного оператора — то же не очень.
Здравствуйте, BlackBox, Вы писали:
BB>Здравствуйте, tarkil, Вы писали:
T>>Но, с другой стороны, необязательность break даёт две фишки: T>>- использовать один case, как фрагмент другого
BB>За это убивать сразу
Между прочим такие вещи и в MFC можно встретить. УБИТЬ Microsoft и программистов, писавших компиляторы Си.
Здравствуйте, tarkil, Вы писали:
T>Здравствуйте, BlackBox, Вы писали:
T>>>- использовать один case, как фрагмент другого BB>>За это убивать сразу
T>В этом есть здравое зерно
Здравствуйте, FDSC, Вы писали:
FDS>НИКОГДА не пишите после метки оператора выбора несколько операторов. Тогда всегда будет процедурный стиль (извините, что не даю ваших фраз).
FDS>Получим тогда () FDS>switch( reasonCode ) FDS>{ FDS> case R1: func1(); break; FDS> case R2: func1(); break; FDS> case REASON_3: func2(); break; FDS> default: ... FDS>}
В этом тоже есть сермяга. Но писать несколько функций, с кучей (двумя в нашем случае ) параметров, вызываемых только один раз... Не знаю, что-то тут не то — писать четыре доп. функции, каждая из двух строк, вызываемые из единственного места...
Здравствуйте, FDSC, Вы писали:
FDS>Между прочим такие вещи и в MFC можно встретить. УБИТЬ Microsoft и программистов, писавших компиляторы Си.
Хорошее предложение. Я в доле.
Здравствуйте, tarkil, Вы писали:
T>В этом тоже есть сермяга. Но писать несколько функций, с кучей (двумя в нашем случае ) параметров, вызываемых только один раз... Не знаю, что-то тут не то — писать четыре доп. функции, каждая из двух строк, вызываемые из единственного места...
Мысль правильная, но по моему опыту, если количество операторов больше 1, то появятся ещё. Потом всё равно функции создавать придётся.
Hello, tarkil!
You wrote on Thu, 09 Jun 2005 11:13:45 GMT:
t> Коль уж пошла такая синтаксическая пьянка, то хочу (просто в порядке t> фантазии) обсудить конструкцию switch в C++. Главная, как мне кажется, t> неприятность этой конструкции в том, что нужно после каждого case писать t> по break. Это и забывают нередко, к тому же.
t> Но, с другой стороны, необязательность break даёт две фишки: t> — использовать один case, как фрагмент другого t> — ставить несколько меток одному и тому же коду
Заменить break на какой-нибудь fall_through — всего-то и делов. И так, если
бряк нарочно опускаешь, коммент такой писать приходится, иначе на ошибку
сильно смахивает. Правда, легаси код при этом пойдет лесом, а потому такого
изменения в языке не будет никогда.
With best regards, Sergey.
Posted via RSDN NNTP Server 1.9
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Здравствуйте, tarkil, Вы писали:
T>Здравствуйте, MShura, Вы писали:
MS>>А как будет выглядеть с таким синтаксисом классическая memset?
MS>>
MS>>void* memset( void *dst, int val, size_t count )
MS>>{
MS>> char* beg = (char*)dst;
MS>> char* end = beg + count;
MS>> switch (count & 7)
MS>> {
MS>> case 0:
MS>> while (beg != end)
MS>> {
MS>> *beg++ = (char)val;
MS>> case 7: *beg++ = (char)val;
MS>> case 6: *beg++ = (char)val;
MS>> case 5: *beg++ = (char)val;
MS>> case 4: *beg++ = (char)val;
MS>> case 3: *beg++ = (char)val;
MS>> case 2: *beg++ = (char)val;
MS>> case 1: *beg++ = (char)val;
MS>> }
MS>> }
MS>>}
MS>>
T>Три минуты пытался понять, что эта хрень делает. Десять лет на C++ пишу, а такого ещё не видел. Неисчерпаемый язык
T>А подобной классики есть какие-то преимущества перед новаторскими технологиями:
T>
T>Что, на самом деле восемь присваиваний подряд эффективнее их же, но в цикле?
В коде, который привел я, в среднем количество jump в 8 (!) раз меньше.
Цена jump в большинстве процессоров большая (даже на x86), а на некоторых ОЧЕНЬ большая, поэтому первый код работает существенно быстрее.
Здравствуйте, MShura, Вы писали:
MS>В коде, который привел я, в среднем количество jump в 8 (!) раз меньше. MS>Цена jump в большинстве процессоров большая (даже на x86), а на некоторых ОЧЕНЬ большая, поэтому первый код работает существенно быстрее.
Разворачивать циклы при программировании не принято. Хотя это и стандартная операция оптимизации, всё-таки код становится полностью не читаемым.
Ни один компилятор не додумается как скомпилировать приведённую Вами кострукцию (хотя, почему бы и нет?). По крайней мере это путь многократного умножения ошибок.
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, MShura, Вы писали:
MS>>В коде, который привел я, в среднем количество jump в 8 (!) раз меньше. MS>>Цена jump в большинстве процессоров большая (даже на x86), а на некоторых ОЧЕНЬ большая, поэтому первый код работает существенно быстрее.
FDS>Разворачивать циклы при программировании не принято. Хотя это и стандартная операция оптимизации, всё-таки код становится полностью не читаемым.
FDS>Ни один компилятор не додумается как скомпилировать приведённую Вами кострукцию (хотя, почему бы и нет?). По крайней мере это путь многократного умножения ошибок.
Зачем строить догадки, когда можно скопилить и посмотреть на результат.
Сравнение такого алгоритма (правда memcpy) можно найти здесь
Между прочим есть еще более быстрые алгоритмы memcpy и memset, но они уже зависят от процессора, поскольку используют знания размеров кэша разных уровней.
Здравствуйте, MShura, Вы писали:
MS>Между прочим есть еще более быстрые алгоритмы memcpy и memset, но они уже зависят от процессора, поскольку используют знания размеров кэша разных уровней.
Не буду спорить, точнее, соглашусь. Кстати, можно и через assembler написать с использованием строковых команд, тогда размер кэша знать не надо
Здравствуйте, MShura, Вы писали:
MS>В коде, который привел я, в среднем количество jump в 8 (!) раз меньше. MS>Цена jump в большинстве процессоров большая (даже на x86), а на некоторых ОЧЕНЬ большая, поэтому первый код работает существенно быстрее.
Если только компилятор не умеет делать раскрутку цикла или использовать спец. инструкции процессора.
... << RSDN@Home 1.1.4 beta 7 rev. 466>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.