Трюки от КрисаКрис Касперски Язык Си не предоставляет никаких средств для временного отключения блоков кода, и большинство программистов делают это с помощью комментариев. Казалось бы, что может быть проще и о каких трюках тут вообще говорить? Но на самом деле, комментарии - едва ли не самый худший прием среди прочих, о которых мы сейчас и поговорим! Комментарии, ремарки и помаркиСистемы контроля версий как раз и создавались для того, чтобы обеспечить легкий, прозрачный и непротиворечивый механизм безопасной правки исходных текстов, – инвариантный по отношению к самому языку. Однако на практике системы контроля версий используются только для организации совместной работы над проектом, да и то не всегда. Уж слишком много телодвижений приходится совершать всякий раз, а программисты — люди ленивые.
Удовлетворяют ли комментарии указанным требованиям? А вот и нет! Комментарии в стиле Си (/* */) очень удобны, поскольку позволяют отключать огромные блоки кода нажатием всего четырех клавиш, – к тому же они могут располагаться в любом месте строки, а не только в начале. Однако отсутствие поддержки вложенности создает серьезные проблемы. Например:
Попытка выключить цикл for (a,,) ведет к ошибке компиляции — комментарии /* */ не могут быть вложенными. В таких случаях программисты используют альтернативу в виде «//», допускающую вложенность, но, увы, вручную проставляемую в начале каждой строки, что очень утомительно и, мягко говоря, не производительно, если, конечно, не использовать макросы, поддерживаемые средой разработки (а практически все среды разработки их поддерживают). Аналогично осуществляется и снятие комментариев. И все бы хорошо, да вот неоднозначности с уровнем вложенности делают отключение блоков небезопасным. В нашем случае мы имеем три раздельных отключаемых блока кода. Во-первых, это заблокированная проверка принадлежности удаляемого файла к vip_array. Во-вторых, собственно, само удаление файла (заблокированное и замененное отладочной печатью через printf). И, в-третьих, комментарий, пытающийся отключить цикл for(a,,) со всем, что в нем находится. Отключаются блоки кода очень просто, а вот обратное утверждение уже неверно. Никаким автоматизмом тут и не пахнет, и в результате нам приходится разбираться с назначением каждого блока самостоятельно. Впрочем, если немного поколдовать над комментариями… Пусть следом за «//» идет цифра (или буква), указывающая принадлежность текущей комментируемой строки к блоку кода. Продвинутые среды разработки типа Microsoft Visual Studio поддерживают развитый макроязык. Он позволяет выполнять лексический анализ, удаляя только те комментарии, за которыми идет заданная буква/цифра. Это может выглядеть, например, так: Имитация многоуровневой структуры отключаемых блоков исходного кода посредством комментариев Проблема вложенности решена на 100%; проблема многоварианости — на 50% (после удаления комментария //1 мы также должны удалить, а точнее, временно заблокировать следующую за ним строку с отладочной печатью)… в целом, предложенная техника намного более удобна, и единственный серьезный недостаток — привязка программиста к конкретной среде с набором пользовательских макросов. Менее серьезный недостаток — ассемблерные вставки, как правило, не поддерживают Си/Си++ комментариев и потому должны обрабатываться отдельно, усложняя реализацию нашего макродвижка и сводя на нет его преимущества. Директивы условной трансляцииРазработанные для поддержки многовариантного кода директивы условной трансляции оказались практически невостребованными (речь, разумеется, идет только о временном выключении кода). Это очень странно — директивы условной трансляции намного более эффективны, чем комментарии! Пример, приведенный ниже, доказывает этот тезис. Директивы препроцессора, отключающие блоки кода
Проблема вложенности решается сама собой; многовариантность поддерживается очень хорошо, позволяя нам включать/выключать определенные блоки и не затрагивая остальные, причем, при подключении «DeleteFile(name_array[a])» автоматически отключается отладочная печать – и наоборот. Риск развала программы уменьшается до нуля. Самое интересное, что директивы условной трансляции ничуть не хуже работают и с ассемблерными вставками! Директивы препроцессора, отключающие ассемблерные инструкции внутри ассемблерных вставок Конечно, писать «#if def _Dx_» намного длиннее, чем «//» или «/* */», однако это не проблема — клавиатурные макросы на что? Про нежелание связаться с макросами мы уже говорили. Ну да макросы – это еще ладно. Хуже всего, что отключенные блоки кода не попадают в релиз, и если у конечного пользователя программа начнет дико глючить, у нас не будет никакой возможности отключить их без перекомпиляции всего кода. ВетвленияФинальный прием устраняет основные недостатки предыдущего трюка, добавляя к нему свои собственные достоинства, а достоинств у него… Короче, намного больше одного. Идея заключается в использовании конструкции if (_Dx_), а при необходимости и if (_Dx_) else. Оператор «if», стоящий перед одиночным блоком кода, не требует замыкающего «#endif», что ускоряет процесс программирования и не так сильно загромождает листинг. Но это мелочь. Гораздо важнее, что если _Dx_ – константа (например, «1»), то оптимизирующий компилятор выбрасывает вызов if, удаляя лишний оверхид. Если же _Dx_ - переменная (глобальная, конечно), то компилятор оставляет ветвление «как есть», давая нам возможность управлять поведением программы. Если у пользователей возникнут проблемы из-за ошибки в плохо отлаженном блоке кода, то этот блок можно отключить (естественно, если значения флагов вынесены в конфигурационный файл или доступны через пользовательский интерфейс, но это уже несущественные детали реализации). Использование ветвлений для выключения блоков кода
Как мы видим, этот код намного компактнее и нагляднее предыдущего, так что при всем уважении к директивам условной трансляции, они идут лесом. А вот ветвления можно использовать для выключения блока ассемблерных вставок (о чем, кстати говоря, умалчивает штатная документация, но следующий пример компилируется вполне нормально): Использование ветвлений для выключения ассемблерных вставок
Ветвления, конечно, тоже не лишены недостатков, но для временного выключения блоков кода они намного лучше, удобнее и продуктивнее, чем комментарии. Разумеется, существуют и другие средства. Взять хотя бы «return», позволяющий одним движением руки погасить блок кода до самого конца функции. Критикуемый GOTO – отличная штука, но только в малых дозах. Иначе программа превращается в настоящее спагетти, которое практически невозможно распутать. |