Довольно часто, по крайней мере у меня в проектах, есть некоторый протокол через который приложение
общается с внешним миром. Протокол состоит из команд, которых может быть масса.
Соответсвенно в программе есть немерянный switch()... на каждую команду соответственно.
Примерно такой код
void process_command( long cmd_id, .... )
{
switch( cmd_id )
{
case cmd_1: process_cmd_1(...); break;
....
case cmd_N: process_cmd_N(...); break;
}
}
Каждый раз когда я вижу такой switch — case на несколько экранов приходит непреодолимое желание
изменить этот код.
Что хотелось бы, так это избавиться от такой конструкции, заменив это чем нибудь другим.
Вопрос производительности пока не принципиален. Интересны именно подходы.
Мой набросок выглядит примерно так ( пока нигде не применялся )
switch — конструкции, в особенности если cmd_1....cmd_N имеют линейный порядок очень не плохо оптимизирует сам компилятор.
Думаю что Вы это хорошо знаете.
В одной кнторе в которой Я работал, был такой бооольшой свитч по внутренним состояниям приложения... сперва думали переписать это
, потом стало ясно что не будем это делать, поотому что время на это нужно было затратить массу да и компилятор не плохо справлялся тут без нас.
Здравствуйте, Sni4ok, Вы писали:
S>а просто std::map<long, boost::function<...> > не пойдёт?
Наверное пойдет как вариант...описанная проблема это только часть задачи.
Каждая команда при выполнении может принимать данные и может возвращать ответы, вообщем случае разные.
Здравствуйте, nen777w, Вы писали:
N>switch — конструкции, в особенности если cmd_1....cmd_N имеют линейный порядок очень не плохо оптимизирует сам компилятор. N>Думаю что Вы это хорошо знаете. N>В одной кнторе в которой Я работал, был такой бооольшой свитч по внутренним состояниям приложения... сперва думали переписать это N>, потом стало ясно что не будем это делать, поотому что время на это нужно было затратить массу да и компилятор не плохо справлялся тут без нас.
Вот вот... унаследованый код конечно никто трогать не собирается
Но на будущее хотелось бы иметь несколько возможных вариантов решения
Здравствуйте, nen777w, Вы писали:
N>switch — конструкции, в особенности если cmd_1....cmd_N имеют линейный порядок очень не плохо оптимизирует сам компилятор.
ИМХО константный, давно правда дисасемблер не смотрел
Здравствуйте, ioni, Вы писали:
I>Довольно часто, по крайней мере у меня в проектах, есть некоторый протокол через который приложение I>общается с внешним миром. Протокол состоит из команд, которых может быть масса. I>Соответсвенно в программе есть немерянный switch()... на каждую команду соответственно.
Предлагаю свой способ:
CommandHandler command_handlers[MAX_COMMAND_ID] — будет даже быстрее чем switch
Ну или std::map если производительность в самом деле совсем пофиг
Если хочется декларативности то можно извратиться примерно так:
Здравствуйте, ononim, Вы писали:
O>CommandHandler command_handlers[MAX_COMMAND_ID] — будет даже быстрее чем switch
В общем случае не будет.
switch давно уже компиляторами раскладывается в jmp по адресу из таблицы, если значения меток подходят для создания такой таблицы.
Здравствуйте, ioni, Вы писали:
I>Довольно часто, по крайней мере у меня в проектах, есть некоторый протокол через который приложение I>общается с внешним миром. Протокол состоит из команд, которых может быть масса. I>Соответсвенно в программе есть немерянный switch()... на каждую команду соответственно. I>Примерно такой код I>
I>void process_command( long cmd_id, .... )
I>{
I> switch( cmd_id )
I> {
I> case cmd_1: process_cmd_1(...); break;
I> ....
I> case cmd_N: process_cmd_N(...); break;
I> }
I>}
I>
А просто паттерн "команда" применить разве нельзя?
Или паттерн "Стратегия"?
Они ж как раз для таких случаев и предназначены — для разруливания большого переключателя.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, LaptevVV, Вы писали:
LVV>А просто паттерн "команда" применить разве нельзя? LVV>Или паттерн "Стратегия"? LVV>Они ж как раз для таких случаев и предназначены — для разруливания большого переключателя.
ну так чувак фактически и изобрел свой паттерн "команда", предлагая свое решение задачи.
В клиент-серверных приложениях еще есть куча проблем с обратной совместимостью, порядком байт, обработкой обрывов соединений, склеиванием сообщений и так далее.
Посмотрите в сторону, например, ZeroC Ice. Очень хорошая штука.
Здравствуйте, LaptevVV, Вы писали:
LVV>А просто паттерн "команда" применить разве нельзя? LVV>Или паттерн "Стратегия"? LVV>Они ж как раз для таких случаев и предназначены — для разруливания большого переключателя.
Все верно это и есть паттерн команда
но чтобы этим воспользоваться нужен опять же некоторый признак
и в результате приходим к switch
или вы знаете более другой некий способ использования порлиморфизма в данном случае ?
идея в том чтобы на этапе компиляции была известна вся информация о конкректной команде
и по возможности этот switch генерировал компилятор, я не хочу чтобы у меня в коде присуствовал
switch на десяь экранов даже если он будет в отдельном файле
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, ononim, Вы писали:
O>>CommandHandler command_handlers[MAX_COMMAND_ID] — будет даже быстрее чем switch CC>В общем случае не будет. CC>switch давно уже компиляторами раскладывается в jmp по адресу из таблицы, если значения меток подходят для создания такой таблицы.
Угу, а в том месте, куда происходит jmp производится вызов нужного обработчика. Ну разве что обработчики тривиальные и заинлайнены в теле switch.
Здравствуйте, sokel, Вы писали:
S>Угу, а в том месте, куда происходит jmp производится вызов нужного обработчика. Ну разве что обработчики тривиальные и заинлайнены в теле switch.
Что мешает заоптимизить jmp сразу на обработчик?
Здравствуйте, CreatorCray, Вы писали:
CC>Здравствуйте, sokel, Вы писали:
S>>Угу, а в том месте, куда происходит jmp производится вызов нужного обработчика. Ну разве что обработчики тривиальные и заинлайнены в теле switch. CC>Что мешает заоптимизить jmp сразу на обработчик?
Для этого компилятор должен сначала выяснить, что все используемые в switch обработчики однородны, то есть имеют одинаковую сигнатуру + в меcте jump вызывается только обработчик. После этого ему нужно будет построить не jmp таблицу, а таблицу обработчиков + реализовать переход по адресу из таблицы с заполнением нужных параметров вызова. Сомневаюсь, что какой либо из существующих компиляторов имеет такой мозг.