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



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

В программистской практике регулярно случается ситуация, когда на время разработки и отладки требуется включать какой-то код и выключать другой. Это несложно делать специальными конструкциями типа #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):

  1. Gedweb
    /#19546896 / +4

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

  2. AllexIn
    /#19546904 / +4

    Краткое пособие «Как выстрелить себе в ногу на ровном вместе».

    UPD:
    Ну вот, мой остроумный коммент оказался мало того что не уникальным, так еще и не первым…

    • pythe
      /#19547784 / -3

      Чем поддержка такого кода принципиально отличается от традиционного
      #define DEBUG
      #ifdef DEBUG

      #else

      #endif
      #ifdef DEBUG

      #else

      #endif
      ?

      • iroln
        /#19547834 / +1

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

        • pythe
          /#19547902 / -2

          Понятно. Разумеется выключка комментированием предназначена исключительно для относительно «легких» текстов, и желательно IDE с подсветкой. Это верно для любой подобной техники. Но в «тяжелых» тоже попадалось.

  3. REPISOT
    /#19546948 / +1

    #Ifdef в помощь

    • pythe
      /#19547736

      Не уловил логики… А, там тоже про комментарии? :)

      • acyp
        /#19548402

        Нет, про подход в решении. На ровном месте зарабатывать новые проблемы, примерно как в шутке про regexp. И это при том, что есть проверенные и тиражируемые методы.

  4. Vantela
    /#19547034

    Я в школе делал так — создавал int DEBUG=1; и перед операторами которые нужны для дебага писал if(DEBUG){...}.

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

    Переделывайте лучше на #ifdef. Пока не поздно.

  5. GennPen
    /#19547036

    С такими комментариями потом бегай и ищи по коду что забыл включить/выключить.
    Нет уж, лучше по-старинке:
    #ifdef DEBUG
    #endif

  6. Bedal
    /#19547056 / +2

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

    • pythe
      /#19547864

      Не очень :)) И как можно пробел (20) перерезать в CR (0D)?

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

      • geher
        /#19548374

        И как можно пробел (20) перерезать в CR (0D)?

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


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

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

        • yurrig
          /#19549696

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

          • Bedal
            /#19549786

            именно так :-)

            пакетик с затычками
            который наполнялся, когда удавалось оказаться возле Бармалея (перфоратора перфокарт).

          • geher
            /#19550430

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

      • Bedal
        /#19549790

        пробел (20)

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

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

        • pythe
          /#19550398

          ну, положим, не 20, а 40

          Я там в hex выражался, а не в oct. Вспомнил позже, что да, пробелы были вообще без пробивок. А вот какими были коды CR ил LF не помню совсем.

          • Bedal
            /#19550480

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

          • geher
            /#19550506

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

  7. Capacitor10n
    /#19547708

    Не более 2? Оо

    #define mode  0
    #if mode == 0
    #elif mode == 1
    #elif mode == 2
    ...
    #elif mode == 999999999
    #endif

    • Keremet_2030
      /#19552648

      Я иногда использую подход через case, делаю глобальную переменную DebugMode = n;
      case DebugMode of
      0: Begin
      //// код 1
      End;
      1: Begin
      //// Код 2
      End;
      2: Begin
      //// Код 3
      End;

      PS: Простите за паскаль

      • GennPen
        /#19554132

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

  8. FluffyMan
    /#19549062

    Но если в стартовом комментарии между второй звездочкой и слешем вставить пробел

    может между звездочками? иначе тогда не понимаю как это работает.

    • pythe
      /#19550184

      Оу, кажется, единственный комментатор, который обратил внимание на механизм :)

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

      • FluffyMan
        /#19550728

        Понял. Но тогда у Вас перепутано. Ведь этот код:

        /**/
                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"> помог)

        Спасибо за разъяснение.

        • Vantela
          /#19550758

          Перепроверил в vi.

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

          • pythe
            /#19552746

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

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

            • Vantela
              /#19552784

              Я не ставил.:)

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

              • pythe
                /#19552850

                Да я ж не в претензиях :) Пусть их.

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

        • pythe
          /#19552680

          Но тогда у Вас перепутано.

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

  9. Andy_Big
    /#19551576

    Однако такая конструкция не позволяет создавать более двух альтернативных участков кода.

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