Rambler's Top100 Service калинин.ru / программирование / c и c++ /  << 30.08.00 >>

Комментарии

Плохое комментирование исходных текстов является одним из самых тяжелых заболеваний программ. Причем программисты зачастую путают "хорошее" комментирование и "многословное". Согласитесь, комментарии вида:

i = 10; // Присваиваем значение 10 переменной i.

выглядят диковато. Тем не менее, их очень просто расставлять и, поэтому, этим часто злоупотребляют. На мой взгляд, хороший комментарий не должен находится внутри основного текста подпрограммы. Комментарий должен располагаться перед заголовком функции; пояснять что и как делает подпрограмма, какие условия накладываются на входные данные и что от нее можно ожидать; визуально отделять тела функций друг от друга. Потому что при просмотре текста программы зачастую незаметно, где заканчивается одна и начинается другая подпрограмма. Лично я оформляю такие комментарии подобным образом:

/**********************************************
 * function
 */
void function()
{ 
}

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

Я использую этот комментарий в любом случае, даже если из названия подпрограммы понятно, что она делает --- продублировать ее название не тяжелый труд. Единственное, из моего опыта следует, что не надо переводить название функции на русский язык; если уж писать комментарий, то он должен что-то добавлять к имеющейся информации. А так видно, что комментарий выполняет декоративную роль.

Чем короче функция, тем лучше. Законченный кусочек программы, который и оформлен в виде независимого кода, значительно легче воспринимается, чем если бы он был бы внутри другой функции. Кроме того, всегда есть такая возможность, что этот коротенький кусочек потребуется в другом месте, а когда это требуется программисты обычно поступают методом cut&paste, результаты которого очень трудно поддаются изменениям.

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

Комментарии внутри тела подпрограммы могут появляться только в том случае, если ее тело все-таки стало длинным. Такое случается, когда, например, пишется подпрограмма для разбора выражений, там от этого никуда особено не уйдешь. Комментарии внутри таких подпрограмм, поясняющие действие какого-либо блока, не должны состоять из одной строки, а обязательно занимать несколько строчек для того, что бы на них обращали внимание. У меня это обычно выглядит следующим образом:

{
  /*
   * Комментарий.
   */
}

Тогда он смотрится не как обычный текст, а именно как нужное пояснение.

Остальной текст, который я призываю не комментировать, должен быть понятным. Названия переменных должны отображать их сущность и, по возможности, быть выполнены в едином стиле. Не надо считать, что короткое имя лучше чем длинное; если из названия короткого не следует сразу его смысл, то это имя следует изменить на более длинное. Кроме того, для C++ я обычно использую области видимости для создания более понятных имен. Например, dictionary::Creator и index::Creator: внутри области видимости можно использовать просто Creator (что тоже достаточно удобно, потому что в коде, который имеет отношение к словарю и так ясно, какой Creator может быть без префикса), а снаружи используется нужный префикс, по которому смысл имени становится понятным.

Кроме того, должны быть очень подробно прокомментированы интерфейсы. В последнее время стал замечать, что в моих программах иерархии классов фактически вырождаются. То есть у меня достаточно редки дети "в третьем колене" и ниже. Ален Голуб в своих "правилах" совершенно резонно заметил, что иерархии классов должны быть широкими, а не глубокими. Все дело в подходе: вы описываете интерфейс, которому должны удовлетворять объекты, а после этого реализуете конкретные виды этих объектов. Обычно все, что видит пользователь --- это только определение базового класса такой "широкой" иерархии классов, поэтому оно должно быть максимально понятно для него. Кстати, именно для возврата объектов, удовлетворяющих определенным интерфейсам, я и использую "умные" указатели.

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

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

Следовательно, надо предоставить читателю возможность отвлеченного ознакомления с кодом. Я под этим подразумеваю, возможность удобного листания комментариев или распечаток программной документации. Подобную возможность обеспечивают программы автоматизации создания программной документации. Таких программ достаточно много, для Java, например, существует JavaDoc, для C++ --- doc++ и doxygen. Все они позволяют сделать по специального вида комментариям качественную документацию с большим количеством перекрестных ссылок и индексов.

Вообще, тут мне хотелось бы немного отклониться от основной темы и пофилософствовать. Хороший комментарий сам по себе не появляется. Он является плодом тщательнейшей проработки алгоритма подпрограммы, анализа ситуации и прочее. Поэтому когда становится тяжело комментировать то, что вы хотите сделать, то это означает, скорее всего, то, что вы сами еще плохо сознаете, что хотите сделать. Из этого логичным образом вытекает то, что комментарии должны быть готовы до того, как вы начали программировать.

Это предполагает, что вы сначала пишите документацию, а потом по ней строите исходный текст. Такой подход (в моем вольном изложении) называется "литературным программированием" и автор данной концепции сам Дональд Кнут (тот самый, который написал "Искусство программирования" и сделал TeX). У него даже есть программа, которая автоматизирует этот процесс, она называется Web. Изначально она была разработана для Pascal'я (и на ней написан TeX --- очень рекомендую посмотреть исходники), но потом появились варианты для других языков. Например, СWeb --- это Web для языка C.

Используя Web вы описываете работу вашей программы, сначала самым общим образом. Затем описываете какие-то более специфические вещи и т.д. Кусочки кода появляются на самых глубоких уровнях вложенности этих описаний, что позволяет говорить о том, что и читатель, и вы, дойдете до реализации только после того, как поймете действие программы. Сам Web состоит из двух программ: для создания программной документации (получаются очень красивые отчеты) и создания исходного текста на целевом языке программирования. Кстати сказать, TeX написан на Web'е, а документацию Web делает с использованием TeX'а... как вы думаете, что из них раньше появилось?

Web в основном применятся программистами, которые пишут сложные в алгоритмическом отношении программы. Сам факт присутствия документации, полученной из Web-исходника, упрощает ручное доказательство программ (если такое проводится). Тем не менее, общий подход к программированию должен быть именно такой: сначала думать, потом делать. При этом не надо мерять работу программиста по количеству строк им написанных.

Несмотря на то, что использование Web'а для большинства "совсем" прикладных задач несколько неразумно (хотя вполне возможно, кто мешает хорошо программировать?), существуют более простые реализации той же идеи.

Я рассмотрю doxygen в качестве примера.

/**
 * \bref Краткое описание.
 * 
 * Этот класс служит для демонстрации
 * возможностей автодокументации.
 */
class AutoDoc
{
public:
    int foo() const; //!< Просто функция. Возвращает ноль. 
   
    /**
     * \brief Другая просто функция.
     *
     * Назначение непонятно: возвращает ноль. foo() ---
     * более безопасная реализация возврата нуля.
     *
     * \param ignored --- игнорируется.
     *
     * \warning может отформатировать жесткий диск.
     *
     * \note форматирование происходит только по пятницам.
     *
     * \see foo().
     */
    int bar(int ignored); 
};

Комментарии для doxygen записываются в специальном формате. Они начинаются с "/**", "/*!" или "//!<". Весь подобный текст doxygen будет использовать в создаваемой документации. Он автоматически распознает имена функций или классов и генерирует ссылки на них в документации (если они присутствуют в исходном тексте). Внутри комментариев существует возможность использовать различные стилевые команды (пример достаточно наглядно демонстирирует некоторые из них), которые позволяют более тщательным образом структурировать документацию.

doxygen создает документацию в форматах:

  • html;
  • LaTeX;
  • RTF;
  • man.

Кроме того, из документации в этих форматах, можно (используя сторонние утилиты) получить документацию в виде MS HTML Help (наподобие MSDN), PDF (через LaTeX).

В общем использовать его просто, а результат получается очень хороший.

Кроме того (раз уж зашла об этом речь), существует такая программа, называется pcGRASP, которая позволяет "разрисовать" исходный текст. В чем это заключается: все видели красивые и очень бесполезные блок-схемы. pcGRASP делает кое-что в этом духе: к исходному тексту он добавляет в левое поле разные линии (характеризующие уровень вложенности), ромбики (соответствующие операторам выбора), стрелочки (при переходе с одного уровня вложенности на другой, например, return) и т.д. В принципе, можно использовать для редактирования программ, но мне не понравилось. Самое приятное, так это распечатки, полученные таким образом. Учитывая то, что indent (отступы) pcGRASP делает сам (не ориентируясь на то, как оно было), это делает подобные распечатки исключительно ценными. Например, один раз подобная распечатка позволила мне выявить глупейшую ошибку вида:

if(cond1)
  if(cond2)
  { }
else
 { }

Условие if(cond2) я добавил не подумав и else стал относится к нему. Просматривая программу в редакторе (который честно сделал нужные отступы у строк с внутренним if'ом, но не тронул else, потому что я об этом его не просил), я этого не видел. Тем не менее, я ее заметил сразу же, как открыл распечатку, когда ехал с работы домой. При этом в тестировании я тогда еще не добрался до тех ситуаций, когда эта ошибка дала бы мне знать о ней.

Резюме

Комментарии надо писать так, что бы потом самому было бы приятно их читать. Никакого распускания соплей в исходном тексте --- тот, кто будет читать его, прекрасно знает что делает операция присваивания и нечего ему это объяснять.

Красиво оформленная программная документация является большим плюсом, по крайней мере потому, что люди, принимающие исходный текст, будут рады увидеть текст с гиперссылками. Тем более, что не составляет труда подготовить исходный текст к обработке утилитой наподобие doxygen.


Версия для печати


  Ссылки по теме:
Бъерн Страуструп
   Язык программирования C++, 3 издание.
/comment/books/27_08_00.shtml
   C и C++. Правила программирования.
http://www.doxygen.org
   Официальный сайт программы doxygen.
  Рядом в разделе:
volatile (12.09.00)
   И опять я публикую здесь текст из конференции SU.C_CPP. Впору заводить для этого отдельный раздел ;) Автор этого письма, Александр Кротов,...   >>>>
Свойства (23.08.00)
   Я уже говорил о и хотя эта заметка не будет посвящена ему, тем не менее я о нем вспомню. Когда только...   >>>>
  Рядом по дате:
Автомобиль в городе (31.08.00)
   Я думаю, все помнят фразу "автомобиль не роскошь, а средство передвижения"? Она давно стала нарицательной и поэтому потеряла весь свой первоначальный...   >>>>
Камень (29.08.00)
   Вот так всегда. Эти великие хергиани крючки лупят, а ты мешки подноси. Ты туда-сюда обратно, а им там приятно. Чужие сапоги...   >>>>
  Содержание:
Заглавная страница
Мой блог
Мое резюме
Дайджест
Программирование
   C&C++
Сети
Unix
Алгоритмы
Оптимизация
Соревнования
Отвлеченно
XML
TeX
Просто так
Студенческое
Туризм
  Байки
Фотографии
Комментарии
   Книги
Web-ресурсы
Фильмы
Интернет
Программное обеспечение
Жизнь
Благодарности
Форум
Хронология
 
  В этом разделе:
Простой, но полезный аллокатор памяти (18.02.03)
   Эта заметка --- продолжение "Postfix изнутри" в том смысле, что в качестве примера опять берется postfix. Но если в прошлый раз...   >>>>
C или C++? (09.07.01)
   Существуют два диаметрально противоположенных, но одинаково распространенных мнения, которые можно выразить как "C++ это C с классами" и "C++ и C...   >>>>
Религия и goto (14.04.01)
   Начнем несколько издалека. В программировании существует тенденция к алгоритмизации самого процесса программирования. То есть, выведение некоторых универсальных правил, использование которых в...   >>>>
ploticus (16.10.00)
   Есть такая программа, предназначенная для создания графиков различных видов из командной строки, называется ploticus. Программа сама по себе достаточно удобная ---...   >>>>
Шаманство, или ошибки работы с памятью (25.09.00)
   Когда программа становится внушительной по своему содержанию (то есть, не по количеству строчек, а по непонятности внутренних связей), то ее поведение...   >>>>
Библиотека консорциума W3, libwww (20.09.00)
   Популярный нынче термин "веб-программирование" обычно подразумевает под собой программирование, в лучшем случае, на perl, в худшем --- на PHP, в совсем...   >>>>
Инварианты внутри программы (18.09.00)
   Вы когда-нибудь задумывались, над тем, как вы пишите программы? Если нет, то, я думаю, сегодняшняя заметка будет вам полезна. Итак, как...   >>>>
Содержание раздела полностью...
   Примерно в тоже время
Автомобиль в городе (31.08.00)
   Я думаю, все помнят фразу "автомобиль не роскошь, а средство передвижения"? Она давно стала нарицательной и поэтому потеряла весь свой первоначальный...   >>>>
Камень (29.08.00)
   Вот так всегда. Эти великие хергиани крючки лупят, а ты мешки подноси. Ты туда-сюда обратно, а им там приятно. Чужие сапоги...   >>>>
Хронология полностью...
   Содержание
Заглавная страница
Мой блог
Мое резюме
Дайджест
Программирование
  C&C++
Сети
Unix
Алгоритмы
Оптимизация
Соревнования
Отвлеченно
XML
TeX
Туризм
  Байки
Фотографии
Комментарии
  Книги
Web-ресурсы
Фильмы
Интернет
Программное обеспечение
Жизнь
Студенческое
Просто так
Благодарности
Форум
Хронология
© 2000-2008, Andrey L. Kalinin
mailto:andrey@kalinin.ru
Rambler's Top100