пятница, 14 мая 2010 г.

[prog] NativeClient от Google продолжает мутировать в интересном направлении

Google-воды явно понимают, что если все приложения для их Chromium OS будут писаться на JavaScript, то ничего хорошего из этого не выйдет. Поэтому они активно развивают NativeClient – инструмент для запуска нативного кода внутри браузера. Т.е. часть Web-приложения пишется на JavaScript, а часть – на C/C++. И нативная часть работает через NativeClient, что должно обеспечивать должную скорость и безопасность.

Если мне не изменяет мой склероз, первые версии NativeClient были очень ограничены по функциональности. Но со временем они стали позволять C/C++ программистам делать все больше и больше.

На днях Google выпустил очередную бета версию своего NativeClient SDK. Похоже, что NativeClient превращается в эдакий гипервизор для нативного кода. При полученном на тестах падении производительности в 3% обеспечивается высокий уровень безопасности: разработчикам доступен только ограниченный набор API-шных вызовов для взаимодействия с системой + к тому NativeClient контролирует выход за пределы отведенной нативному коду памяти.

Такими темпами из NativeClient может получиться практически виртуальная машина для C/C++. Особенно учитывая планы по подтягиванию к этому делу LLVM. Тогда вообще можно будет получить систему a la Java – С++ные приложения транслируются в LLVM-овский байт-код, который и передается на сторону клиента. Там он транслируется в нативный и исполняется с максимальной скоростью.

Опять же, если доверять моему склерозу, то Михаэль Франц, ученик Никлауса Вирта, когда-то критиковал Java за то, что там не было сделано именно так. Первые Java были интерпретаторами, HotSpot появился позднее. А Франц, помнится, тогда разработал для Компонентного Паскаля похожий подход – код транслировался в высокоуровневое бинарное представление, которое было кроссплатформенным. На конкретной платформе это представление специальными оптимизированными под платформу/архитектуру компиляторами транслировалось в машинный код. Причем вся эта кухня к моменту появления первых версий Java уже работала.

[life.sport.photo] Монако – страна контрастов :)

Гонщик Формулы-1, Дженсон Баттон, тренируется на трассе Монако. Поскольку трасса проходит по улицам города, то и возникают такие контрасты.

Что удивительно: как эти милые дамы умудряются беседовать, когда в нескольких метрах под ними проносятся болиды Формулы-1? Похоже, что анекдот “А если дамы замолчат, то мы услышим оглушающий шум Ниагарского водопада” возник не на пустом месте :)

Снимок найден в очередном выпуске WSJ’s Picture of the Day.

четверг, 13 мая 2010 г.

[life.memories] Винные мемуары от humus-а

Нравятся мне хорошо написанные байки-воспоминания про студенческие пьянки и другие приключения взрослых недорослей. На выходных наткнулся на забавные воспоминания тов.humus-а под названием “Винные мемуары”. Залпом прочел тринадцать или пятнадцать выпусков. Может быть, если найдется время, то дочитаю и остальное.

Интересные впечатления возникают, когда сталкиваешься с подобными воспоминаниями. Во-первых, восхищение: “Ну и память!” :) И, во-вторых, радость от того, что сам я такой фигней в молодости не страдал. Т.е. пьянки-то были, но не до такой степени ;)

среда, 12 мая 2010 г.

[prog.stupidity] Не могу не украсть цитату про отсутствие багов

Найдено в обсуждении очередного выплеска сознания мыщъх-а (aka Крис Касперски) на RSDN:

Как известно, в написанной программе багов нет. Появляются они при ее улучшении, расширении и модификации.

[life.health] Еще на тему переработок и стрессов: неврастения

Написано с большим юмором и, похоже, очень точно: заметка “Неврастения” в “Блоге добрых психиатров”. Имхо, всем желающим поработать в режиме 12x7 читать обязательно. В особенности последний абзац, про средства лечения ;)

вторник, 11 мая 2010 г.

[prog] Почему я предпочитаю один стиль оформления кода другому

Эта заметка содержит описание причин, по которым мой стиль кодирования изменялся так, как я показал на примерах в предыдущей заметке.

Итак, какое-то время я писал следующим образом:

Образец #1
if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) ) {

   try_enqueue_trx( trx );
   start_new_child_process_if_necessary();
}

Открывающая фигурная скобка на предыдущей строке (т.е. на строке, в которой находится if, switch, for, список параметров функции и т.д.) – это красиво и удобно. Но только на маленьких фрагментах кода. Вроде такого:

if( is_valid_trx_id( trx ) ) {
   try_enqueue_trx_id( trx );
   start_new_child_process_if_necessary();
}

В таких маленьких фрагментах хорошо видно, где именно находится открывающая фигурная скобка (и есть ли она вообще). Но, стоит только условному выражению внутри if-а расположиться на нескольких строках, да еще и содержать 4-5-6 закрывающих круглых скобок перед открывающей фигурной, как обнаружить месторасположение фигурной скобки оказывается уже сложнее.

Еще одна проблема с открывающей фигурной скобкой на предыдущей строке: если условие в if (for, while) занимает несколько строк, а затем еще и в составном операторе находится много кода, то бывает сложно выделить место начала составного оператора:

size_t items_viewed = 0;
for( awaiting_trx_container_t::const_iterator it = m_awaiting_trx.begin();
   it != m_awaiting_trx.end() && trx_to_process.size() < max_trx_at_once;
   ++it, ++items_viewed ) {
   log_current_trx( m_logger, m_action_description, *it );
   const trx_checking_result_t & checking_result = check_current_trx( *it );
   if( checking_result.is_processable_trx() ) {
      log_processable_trx( m_logger, m_action_description, *it );
      trx_to_process.push_back( *it );
      m_awaiting_trx.erase( *it );
   }
   // Здесь может быть еще несколько строк с какими-то действиями.
}

Чтобы устранить эту проблему, приходилось вставлять в самом начале составного оператора пустую строку, чтобы показать разделение между for-ом и его телом:

size_t items_viewed = 0;
for( awaiting_trx_container_t::const_iterator it = m_awaiting_trx.begin();
   it != m_awaiting_trx.end() && trx_to_process.size() < max_trx_at_once;
   ++it, ++items_viewed ) {

   log_current_trx( m_logger, m_action_description, *it );
   const trx_checking_result_t & checking_result = check_current_trx( *it );

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

Вот так я пришел к следующему стилю кодирования:

Образец #2
if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) )
{
   try_enqueue_trx( trx );
   start_new_child_process_if_necessary();
}

Этот стиль лишен недостатков предыдущего. Но в нем есть проблемы, например, с “частоколом” из if-else-if или внутри switch-а:

if( can_process_trx( trx ) )
{
   // Какие-то действия.
}
else if( can_delay_trx( trx ) )
{
   // Еще какие-то действия.
}
else if( can_reroute_trx_to_another_host( trx ) )
{
   // Еще какие-то действия.
}
else if( can_throw_trx_away( trx ) )
{
   // Еще какие-то действия.
}
else
{
   // И еще какие-то действия.
}

Здесь в крайней левой позиции находится слишком много символов. Хочется видеть только if-else-if, а приходится еще и на фигурные скобки смотреть. В какой-то момент времени я понял, что мне это мешает, а вот GNU-тый стиль кодирования содержит нужное мне решение – отступ для составного оператора. С этим отступом “частокол” if-else-if становится более заметным, на мой взгляд:

if( can_process_trx( trx ) )
   {
      // Какие-то действия.
   }
else if( can_delay_trx( trx ) )
   {
      // Еще какие-то действия.
   }
else if( can_reroute_trx_to_another_host( trx ) )
   {
      // Еще какие-то действия.
   }
else if( can_throw_trx_away( trx ) )
   {
      // Еще какие-то действия.
   }
else
   {
      // И еще какие-то действия.
   }

Вот так я пришел к следующему стилю:

Образец #3
if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) )
   {
      try_enqueue_trx( trx );
      start_new_child_process_if_necessary();
   }

Но и здесь оставалась одна закавыка: если действие под if-ом занимало одну строку, а само условие в if-e несколько строк, то было сложно различить где заканчивается условие и начинается действие:

if( is_valid_trx_id( context_storage.get_current_context(),
   trx.id(),
   user_manager.find( trx.user_id() ) ) )
   try_enqueue_trx( trx );

Поэтому при переносе длинных выражений на следующей строке я стал делать не один отступ табуляцией, а два. Т.о. продолжение выражение стало явно видно именно как продолжение:

if( is_valid_trx_id( context_storage.get_current_context(),
      trx.id(),
      user_manager.find( trx.user_id() ) ) )
   try_enqueue_trx( trx );

Ну а дальше осталось сделать последний шаг – разобраться с тем, как переносить вызовы функций с большим количеством аргументов. Сейчас я поступаю так: если вызов функции умещается на одну строку, то я записываю его в одну строку. Если нет – то каждый аргумент (начиная с первого), записывается на новой строке:

// В одну строку.
const ACE_Date_Time finish_at = minimum( trx.finish_at(), default_lifetime );
// В несколько строк. При переносах двойной отступ.
so_4::api::send_msg_safely(
      so_query_name(),
      "msg_next_turn",
      new msg_next_turn( current_context ),
      m_cfg.m_turn_size );

Вот так я и пришел к своему нынешнему стилю:

Образец #4
if( is_valid_trx_id(
      context_storage.get_current_context(),
      trx.id(),
      user_manager.find( trx.user_id() ) ) )
   {
      try_enqueue_trx( trx );
      start_new_child_process_if_necessary();
   }

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

Ну и чтобы два раза не вставать, еще пару моментов.

Во-первых, я использую табуляцию для отступов вместо пробелов. Пришел к табуляции очень-очень давно. Году в 1992-м или 1993-м. Причем сначала причина была вообще очень примитивной и жестокой – C++ные исходники с табуляцией занимали раза в полтора меньше места, чем с пробелами. Во времена 1.2Mb 5-дюймовых дискет и медленных 286-х процессоров это было актуально. Потом добавился еще один бенефит – исходник с табуляциями было легко переформатировать для вставки в курсовые/дипломные работы. Не влезает исходник по ширине в A4 при размере табуляции в 4 пробела – ерунда, выставляем размер в 2 пробела и все помещается :) Кстати говоря, это до сих пор прекрасно работает при подготовке различного рода документации.

Во-вторых, я уже лет десять использую стиль, при котором тип возвращаемого функцией/методом значения записывается на отдельной строке. Это, на первый взгляд, дико выглядит для методов, которые возвращают int или std::string, но зато очень хорошо работает для более длинных имен типов:

class db_iface_t
   :  private ACE_Copy_Disabled
   {
   public :
      virtual ~db_iface_t();

      virtual void
      detect_known_and_unknown_packages(
         const package_id_list_t & full_id_list,
         package_id_list_t & unknown_ids,
         package_id_list_t & known_ids ) = 0;

      virtual std::auto_ptr< outgoing_message_list_t >
      select_outgoing_messages_for_sending(
         unsigned int message_count,
         const ACE_Date_Time & attempt_timestamp ) = 0;

      virtual std::auto_ptr< undelivered_SR_package_info_list_t >
      select_undelivered_SR_packages_for_resending(
         unsigned int package_count,
         const ACE_Date_Time & time_border ) = 0;

Disclaimer. Все вышеизложенное, вероятно, не очень актуально для работы с текстом программы исключительно за экраном компьютера. Умные редакторы, которые могут сами устанавливать высоту и размер скобочек, величину отступов и менять шрифты для разных элементов (как это делает, например, SourceInsight) + разные виды folding-а + различные браузеры исходных текстов – все это хорошо, но это только одна сторона медали. Вторая сторона – это распечатанные и/или включенные в документацию фрагменты кода. Мне с этим довольно часто приходится сталкиваться. Поэтому я придерживаюсь такого стиля кодирования, чтобы мой исходник можно было один-в-один вставить в качестве примера в документацию/блог и не думать о переформатировании.

понедельник, 10 мая 2010 г.

[prog.flame] Почему жесткий coding style guide ущемляет индивидуальность

Отвечаю на вопрос ув.тов.Stan в комментарии к заметке о форматировании кода в Java:

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

Я вижу две главные причины:

Комфортность. Уже давно говорят, что программистам нужно обеспечивать комфортные условия работы. По крайней мере об этом пишут ДеМарко и Листер в “Человеческом факторе” (первое издание вышло в 1987).

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

Оформление исходного кода программы – это такая же мелкая составляющая комфорта на рабочем месте, как возможность разместить настольную лампу в нужном месте стола. Мелкая, но очень важная.

Например, за годы работы у меня выработалась привычка ставить открывающую круглую скобку сразу после ключевого слова или имени функции. Т.е. я привык писать “if(“ или “add_item(“, а не “if (“ и “add_item (“. Эта привычка сказывается во время чтения исходников. Т.е. я уже автоматом выделяю синтаксические конструкции при беглом просмотре года. Но, когда я попадаю в чужой код, набранный в другом стиле, скорость восприятия снижается, я уже не вижу знакомых мне конструкций.

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

Право выбора. Способность придумывать конструкцию изделия – это способность выбирать. Факториал можно подсчитать рекурсивно, а можно итеративно. Какой способ предпочесть в данном конкретном случае? Очередь транзакций можно хранить в ОП, можно в файле на диске, можно во встраиваемой СУБД, можно в клиент-серверной СУБД. Что выбрать?

Чем опытнее становится разработчик, тем более важные решения ему придется принимать. Самостоятельно принимать и нести за это ответственность. Но с чего все начинается? С мелочей. Разработчик сам должен научиться делать выбор между именами i и index в разных контекстах. Точно так же он должен сам решить для себя, удобнее ли ему делать пробел между if и открывающей круглой скобкой и должна ли открывающая фигурная скобка располагаться на той же строке или на следующей. Человек делает свой выбор и берет на себя ответственность.

Право выбора и комфортность дают еще одну важную возможность – возможность смены предпочтений. Время идет, мы меняемся. Например, я с 1992-го по 2001 год использовал стили CamelCase и camelCase. Но потом перешел на lower_case, поскольку стали сильно уставать глаза при работе с camelCase – больше нужно было усилий на восприятие идентификаторов вида IllegalIndex, в отличии от варианта illegal_index. Затем, уже используя lower_case я менял свои предпочтения по поводу переноса длинных выражений на новую строку и по поводу блоков в фигурных скобках. Было время, я писал так:

if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) ) {

   try_enqueue_trx( trx );
   start_new_child_process_if_necessary();
}

Потом я пришел к выводу, что проще читать код, написанный вот так:

if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) )
{
   try_enqueue_trx( trx );
   start_new_child_process_if_necessary();
}

Еще спустя какое-то время я пришел к другому стилю:

if( is_valid_trx_id( context_storage.get_current_context(), trx.id(),
   user_manager.find( trx.user_id() ) ) )
   {
      try_enqueue_trx( trx );
      start_new_child_process_if_necessary();
   }

А потом и к такому:

if( is_valid_trx_id(
      context_storage.get_current_context(),
      trx.id(),
      user_manager.find( trx.user_id() ) ) )
   {
      try_enqueue_trx( trx );
      start_new_child_process_if_necessary();
   }

Я набирался опыта, учитывал прошлые ошибки, улучшал свой стиль кодирования, осознано принимал решения по его изменению (я могу объяснить, почему каждый следующий из показанных выше примеров для меня лучше предыдущего). И я постоянно создавал для себя более комфортные условия. Что, как я надеюсь, сказывалось и на качестве создаваемых мной программ.

Все это было возможно благодаря тому, что я не был связан рамками жесткого coding style guide.

Однако, право выбора и комфортность для конкретного разработчика должно находиться в строгих рамках. Например, мне все равно, в каком из нижеперечисленных стилей пишет человек:

if( is_valid_trx_id(
      context_storage.get_current_context(),
      trx.id(),
      user_manager.find( trx.user_id() ) ) ) ...

if (is_valid_trx_id (context_storage.get_current_context(),
      trx.id(),
      user_manager.find (trx.user_id()))) ...

if (is_valid_trx_id (context_storage.get_current_context(), trx.id(),
      user_manager.find (trx.user_id()))) ...

if(is_valid_trx_id(context_storage.get_current_context(), trx.id(),
      user_manager.find(trx.user_id()))) ...

Прочитать такой код можно (некоторые фрагменты чуть проще, некоторые чуть сложнее). Но все это приемлимо.

А вот за такое нужно уже давать по рукам:

if(is_valid_trx_id(context_storage.get_current_context(),trx.id(),user_manager.find(trx.user_id()))) ...

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

Поэтому я лично считаю, что в оформлении кода нужно поступать с программистами как с маленькими детьми. Давать свободу выбора, но из нужного нам набора вариантов. Объясняя при этом, откуда возникает каждое ограничение. Что-то вроде: “На улице сегодня прохладно, поэтому под штанишки нужно одеть колготки. Можно одеть эти или эти, выбирай, какие ты оденешь. И мы сегодня пойдем в новых ботиночках, поскольку старые тебе уже маленькие и если ты наденешь их на колготки и носки, то тебе будет больно ходить.” При таком стиле общения мы и своей цели достигаем (не выпускаем чадо на улицу плохо одетым), и даем ребенку возможность почувствовать себя личностью с правом выбора.

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

Кстати говоря, выбирать все равно будут далеко не все. Изрядное количество возьмет за основу рекомендованный стиль и не будут париться. Зато меньшинство, которое по причине каких-то собственных комплексов захочет выебну создать свой стиль, вот они, как маленькие дети, почувствует себя личностями с правом выбора ;)