Некоторое время назад в блоге был пост Наткнулся на образчик кода из категории "Да не дай боже такое сопровождать!". Я там прошелся по качеству кода от одного из RSDN-овских форумчан. Код, как по мне, из категории "лучше бы такого никогда не видеть, тем более не сопровождать".
А на днях этот же форумчанин открыл на RSDN-е новую тему, от которой я испытал что-то вроде шока. Почему шока постараюсь объяснить ниже (хотя не уверен, что это получится).
Итак, суть в том, что у человека образовалась цепочка из if-else в количестве 200 (двухсот(!!!)) штук. Из-за чего VC++ отказался компилировать данную развесистую конструкцию с ошибкой "MSVC C1061: compiler limit: blocks nested too deeply". И автор топика спрашивает у читателей что можно сделать в такой ситуации.
В одном из своих ответов в начатом обсуждении автор темы привел пример того, с чем он имеет дело:
|
else if ( opt.setParam("VAR:VAL") || opt.isOption("set-var") || opt.isOption("set-condition-var") || opt.isOption('C') || opt.setDescription("Set variable valie for conditions and substitutions")) { if (argsParser.hasHelpOption) return 0; if (!opt.hasArg()) { LOG_ERR<<"Setting condition variable requires argument (--set-condition-var)\n"; return -1; } auto optArg = opt.optArg; if (!appConfig.addConditionVar(optArg)) { LOG_ERR<<"Setting condition variable failed, invalid argument: '" << optArg << "' (--set-condition-var)\n"; return -1; } return 0; } |
При этом автор заявляет буквально следующее:
Обработка ключей командной строки.
На какой-нибудь map с хэндлерами не переделать, потому что у меня в режиме --help пробегается по этим условиям и собирает выдаваемую потом информацию. Тут в if сразу и длинный ключ set-var задаётся, и короткий C, и подсказка по формату значения VAR:VAL и описание опции Set variable valie for conditions and substitutions — в одном месте всё задаётся
Это, собственно, и подвигло меня написать данный пост. Т.е. сперва я был шокирован самим фактом появления данной темы на RSDN: это же сделал профессиональный программист, которому деньги платят за написание кода. Написание нормального кода. Т.е. задачей программиста изначально является то, чтобы такого говна в коде не было. Вот просто от слова совсем. И если ты этого обеспечить не можешь, то может нужно профессию сменить? В менеджмент уйти, к пример. Ну или подучиться немного этой самой профессии...
Но вишенкой на торте стало это самое "На какой-нибудь map с хэндлерами не переделать". Тут уж мое терпение лопнуло и решился таки пожертвовать частью своего выходного дня, чтобы написать данный текст.
Я даже не буду затрагивать тему того, что в продвинутых инструментах для работы с аргументами командной строки многие проблемы уже решены "by design". Например, можно посмотреть на Lyra или args, или даже boost::program_options. Мало ли какие условия заставили человека использовать что-то специфическое (хотя, подозреваю, что им же и написанное). Поэтому сконцентрируемся только на длинной цепочке if-else.
В общем, поскольку в нашем распоряжении только небольшой кусочек демонстрационного кода, то будем отталкиваться именно от него. Из-за чего предлагаемые ниже решения, скорее всего, не подойдут на 100% автору RSDN-новского топика, т.к. наверняка есть какие-то неозвученные на RSDN-е моменты. Но и нет цели показать 100% решение, смысл в том, чтобы показать тот вариант, который лично мне сразу же пришел в голову. И от которого можно оттолкнуться, чтобы избежать длинной портянки из if-else.
Суть в том, что с каждым if-ом у нас есть два связанных блока кода. Первый отвечает за описание одного аргумента командной строки (он помещается внутри условия в if-е). Второй отвечает за обработку аргумента (он помещается внутри then-ветки для if-а).
А раз так, то пусть каждый из блоков представляется в виде std::function. И оба эти блока пусть объединяются в рамках одной структуры: