Выключка кода комментарием: маленький лайфхак

    Несмотря на простоту (и, в общем-то, тривиальность, если подумать) описываемого решения, наткнулся на него чисто случайно, во время разукрашивания комментарием законченной программы, готовой к сдаче.

    В программистской практике регулярно случается ситуация, когда на время разработки и отладки требуется включать какой-то код и выключать другой. Это несложно делать специальными конструкциями типа #if true ... #else ... #endif, меняя true на false, или прибегая к более изощренным условиям. Однако такая конструкция не позволяет создавать более двух альтернативных участков кода.

    В языках, поддерживающих комментарий типа /* ... */ c помощью слегка нестандартной конструкции /**/ ... /*/ ... /*/ ... /**/ можно создавать сколько угодно чередующихся участков кода, которые будут попеременно включаться и выключаться одним лишь пробелом в первом (стартовом) из комментариев.

    Например:

    /**/
            Console.Write("1");
    /*/
            Console.Write("2");
    /*/
            Console.Write("3");
    /*/
            Console.Write("4");
    /**/
            Console.Write("5");
    

    При выполнении этот код выведет в консоль строку "135". То есть, будут выполнены все нечетные операторы вывода — и последний, находящийся уже за пределами всей конструкции. Но если в стартовом комментарии между второй звездочкой и слешем вставить пробел (или, строго говоря, любой символ, кроме звездочки), то тот же самый код выведет строку "245": будут выполнены только четные операторы, и, опять же, последний, который уже за пределами. (UPD: благодарю FluffyMan за указание на ошибку).

    Синтаксис комментариев-разделителей предельно строг: к конструкции /*/ ничего ни убавить, ни прибавить нельзя, это разрушит ее функционал. Синтаксис стартового и стопового комментария напротив совершенно произволен. Он может быть минималистичен до /**/, а может содержать какие угодно — легальные в смысле языка — комментарии. Откуда ясно, что стартовый и стоповый комментарии строго обязательны, и что внутри самой конструкции просто так использовать легальный комментарий типа /* ... */ нельзя, т.к. он тут же станет стоповым для всей предыдущей последовательности комментариев-разделителей /*/, и стартовым для всей последующей их последовательности. Но осмысленное использование таких вставок может оказаться полезным.

    Однострочные комментарии // на функционал не влияют.

    Dixi :)
    Поделиться публикацией

    Комментарии 33

      +12
      Такой код будет невозможно поддерживать, вы стреляете себе в ногу и называете это лайвхаком.
        +14
        Краткое пособие «Как выстрелить себе в ногу на ровном вместе».

        UPD:
        Ну вот, мой остроумный коммент оказался мало того что не уникальным, так еще и не первым…
          –3
          Чем поддержка такого кода принципиально отличается от традиционного
          #define DEBUG
          #ifdef DEBUG

          #else

          #endif
          #ifdef DEBUG

          #else

          #endif
          ?
            +2

            Тем, что комментарий надо искать и руками править, а DEBUG и любые другие дефайны можно конфигурировать на этапе конфигурирования/сборки, а также о них и их состоянии чаще всего знает ваша IDE. Это очевидно же.

              –2
              Понятно. Разумеется выключка комментированием предназначена исключительно для относительно «легких» текстов, и желательно IDE с подсветкой. Это верно для любой подобной техники. Но в «тяжелых» тоже попадалось.
          +2
          #Ifdef в помощь
            0
              0
              Не уловил логики… А, там тоже про комментарии? :)
                0
                Нет, про подход в решении. На ровном месте зарабатывать новые проблемы, примерно как в шутке про regexp. И это при том, что есть проверенные и тиражируемые методы.
              +1
              Я в школе делал так — создавал int DEBUG=1; и перед операторами которые нужны для дебага писал if(DEBUG){...}.

              Мой подход не идеален, но ваш ужасен.

              Переделывайте лучше на #ifdef. Пока не поздно.
                +3
                С такими комментариями потом бегай и ищи по коду что забыл включить/выключить.
                Нет уж, лучше по-старинке:
                #ifdef DEBUG
                #endif
                  +5
                  Эхх, вспоминается… М-222, ТА-1М. Программа прогонялась в один приём: загоняется колода перфокарт, транслируется и тут же выполняется. Получается единая распечатка кода программы и вслед за ним — результатов выполнения.
                  Печаталось это на барабанном принтере, который интерпретировал CRLF как «очистить буфер»/«продвинуть бумагу на строку».
                  Студенческая работа на алголе. пишем строку кода, потом слово comment, потом пробел и комментарий, очень похожий на код, но неправильный. Строка заканчивается нормальным crlf, ессно. Получив колоду перфокарт, отыскиваем нужный столбец и перевырезаем лезвием пробел на символ CR.
                  Итого: на печати начальная часть строки и слово comment не появляются, так как буфер принтера очищен (cr). Зато неправильный код вот он, на печати, как родной.
                  Сдаём в работу, получаем распечатку, несём преподавательнице.
                  — Незачёт.
                  — Почему?
                  — У вас в программе грубые ошибки.
                  — Не может быть, вот, ниже ведь результат. Он правильный?
                  — Да, правильный. Но так быть не может.
                  — Но вот же распечатка. Транслятор ошибок не выдал, программа выполнилась без ошибок и выдала правильные результаты — почему незачёт?
                  — …
                  было, развлекались пару раз…
                  _________________
                  Связь с постом — вроде бы понятная?
                    0
                    Не очень :)) И как можно пробел (20) перерезать в CR (0D)?

                    Лезвием дырки в перфокарте сверлить — мда, работа для ювелира-труженика :) Эх, времена… Я в немножко похожей ситуации пользовался автоматическим переносом слишком длинной строки. Принтеры были уже матричные.
                      0
                      И как можно пробел (20) перерезать в CR (0D)?

                      Зависит от кодировки перфокарт, а она была разная.
                      В некоторых случаях пробел кодировался как раз отсутствием пробивок.
                      В КПК-12 (иногда неправильно назывался ДКОИ-12, а 12 потому, что каждый символ кодировался пробивками или их остутствием в 12 строках) вроде оно невозможно (если не путаю), поскольку код пробела имел отверстие, которого не должно было быть в CR


                      Лезвием дырки в перфокарте сверлить — мда, работа для ювелира-труженика

                      Это да. У нас специальный пробойник имелся.

                        0
                        Лишние дырки затыкались картонными прямоугольничками, которые после пробойника оставались. Часто так было быстрее и проще поправить ошибку, чем ждать, пока тебе программу перепробьют. Лезвие и пакетик с затычками было наше все…
                          0
                          именно так :-)
                          пакетик с затычками
                          который наполнялся, когда удавалось оказаться возле Бармалея (перфоратора перфокарт).
                            0

                            Отож, лезвия недостаточно.
                            А мы лишние дырки заклеивали, потому что просто вставленные обратно прямоугольники часто вылетали.

                          0
                          пробел (20)

                          ну, положим, не 20, а 40
                          перерезать в CR (0D)?

                          как и написал ув. yurrig, лишние дырки заклеивалась, нужные — прорезались. Одной дырочкой не обходилось.
                            0
                            ну, положим, не 20, а 40

                            Я там в hex выражался, а не в oct. Вспомнил позже, что да, пробелы были вообще без пробивок. А вот какими были коды CR ил LF не помню совсем.
                              0
                              Я там в hex выражался, а не в oct
                              уфф. Какой такой hex в М-222? Восьмерично-десятичные символы. И вообще, речь про перфокарты — со своим кодом. Кстати, точно уже не помню, в каких позициях что было. Больше 40 лет прошло, всё-таки. А подделывать гугленьем не хочу.
                                0

                                Тут вам не ANSI. Так что именно 0х40 (ДКОИ-8).
                                А пробел в разных перфокартных кодировках разным был.
                                В КПК-12 пробел вроде был 12-9-8-5, а CR — 8-4 (могу путать, давно прошли времена, когда перфокарты по дырочкам мог читать).

                          +5
                          Не более 2? Оо
                          #define mode  0
                          #if mode == 0
                          #elif mode == 1
                          #elif mode == 2
                          ...
                          #elif mode == 999999999
                          #endif
                            0
                            Я иногда использую подход через case, делаю глобальную переменную DebugMode = n;
                            case DebugMode of
                            0: Begin
                            //// код 1
                            End;
                            1: Begin
                            //// Код 2
                            End;
                            2: Begin
                            //// Код 3
                            End;

                            PS: Простите за паскаль
                              0
                              Мне кажется такая структура (в отличии от директив препроцессора) не очень хороша, т.к. все в блоке будет компилироваться в программу, хоть и не будет доступна для выполнения. Только если оптимизатор во время компилирования не выкинет недоступный код.
                            0
                            Но если в стартовом комментарии между второй звездочкой и слешем вставить пробел

                            может между звездочками? иначе тогда не понимаю как это работает.
                              +1
                              Оу, кажется, единственный комментатор, который обратил внимание на механизм :)

                              Нет, пробел между звездочками ничего не изменит, именно перед слешем. Это превращает */ в * / и дезавуирует последовательность как завершающий комментарий токен. Таковым становится следующий в очереди /*/, который до этого наоборот начинал следующий блок комментария, и т.д. по цепочке до завершающего /**/. Лучше всего это наблюдать в IDE с подсветкой синтаксиса.
                                0
                                Понял. Но тогда у Вас перепутано. Ведь этот код:

                                /**/
                                        Console.Write("1");
                                /*/
                                        Console.Write("2");
                                /*/
                                        Console.Write("3");
                                /*/
                                        Console.Write("4");
                                /**/
                                        Console.Write("5");

                                выведет 135, но после вставки пробела между звездочкой и слэшэм уже будет 245:

                                /** /
                                Console.Write("1");
                                /*/
                                 Console.Write("2");
                                 /*/
                                Console.Write("3");
                                /*/
                                 Console.Write("4");
                                 /**/
                                Console.Write("5");


                                P.S. <source lang="cpp"> помог)

                                Спасибо за разъяснение.
                                  +2
                                  Перепроверил в vi.

                                  Браво! Выходит автор выстрелил себе в ногу даже в статье в которой рассказывал как это сделать:)
                                    +1
                                    Выходит автор выстрелил себе в ногу даже в статье в которой рассказывал как это сделать:)

                                    :)))) Мда, выходит. Но заодно рикошетом зацепил целую кучу завсегдатаев гиков, которые из-за этого осерчали, и оказались не гиками, а нёрдами, и понаставили кучу минусов за несоответствие их идеалам. Вместо того, чтобы просто похихикать :)
                                      0
                                      Я не ставил.:)

                                      Дело то не в идеалах.
                                      Реально ж очень легко запутаться где там пробел. Потому и заминусили статью.
                                        +1
                                        Да я ж не в претензиях :) Пусть их.

                                        Никто не мешает использовать вместо пробела строчку DEBUG — эффект будет точно тот же самый. Равно как никто не мешает вместо #define DEBUG использовать #define d и запутаться не меньше. Дело вкуса, а компилятору пофиг.
                                    +1
                                    Но тогда у Вас перепутано.

                                    Так точно. Этот самый попутал, который с рожками. Спасибо за поправку. Внесу Вашу коррекцию в текст.
                                0
                                Однако такая конструкция не позволяет создавать более двух альтернативных участков кода.

                                Директивами препроцессора (#ifdef, #if defined() и т.д.) можно создавать сколько угодно альтернативных участков кода. У меня, например, поддерживается около 18 вариантов одной программы. Причем во многих IDE можно создавать отдельные конфигурации построения под каждый вариант и построить все или выбранные варианты в несколько кликов мыши :)

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое