eao197 on the Web
Сайт Евгения Охотникова
[ Главная | Проекты | Описания | Об авторе ]

В поисках лучшего языка / Почему я ищу новый язык?

В поисках лучшего языка
Почему я ищу новый язык?
Что не так с C++?
Что хочется найти?
Прощай C++?
Связано ли это с SObjectizer?
Языки
Тестовые программы

Ну что можно сказать?

Меня C++ вполне устраивает. Я использую его с 1992 года и практически всю свою профессиональную жизнь я был C++ программистом. Может быть поэтому у меня нет проблем с использованием C++. Но...

Программы не пишут в одиночку

На C++ хорошо писать в одиночку или с двумя-тремя коллегами, которые знают его лучше тебя. Тогда код свободен от детских ошибок новичков C++, нет утечек памяти, нет повисших указателей, практически нет ручного управления памятью, разумно используются шаблоны, исключения и STL. В общем тогда все хорошо.

Однако, найти готовых C++ программистов достаточного уровня сложно. А чем больше проект тем больше их нужно. В результате на C++ программируют люди, которые изучают его прямо в процессе работы. Как следствие в коде высок процент детских ошибок, являющихся следствием недостаточного знания идиом языка. Например, ручное управление памятью и другими ресурсами вместо использования std::auto_ptr и идиомы RAII. Возврат указателей или ссылок на временные объекты из функций. Передача аргументов по значению вместо использования ссылок и т.д.

Все это является следствием сложности самого языка. C++ небезопасен, именно таким он создавался, именно этим он и ценен. Но когда у человека нет времени на изучение C++, то C++ оказывается слишком опасным инструментом.

Кроме того, сейчас у людей не только нет времени на изучение C++, но и нет желания. Поскольку более безопасных конкурентов у C++ предостаточно. Так зачем же кому-то учить старый C++, если есть гораздо более безопасная Java и иже с ней?

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

Программы требуют инструментов

Времена меняются и уже невозможно написать что-нибудь более-менее серьезное с нуля. Мир потихоньку сходит с ума -- в погоне за увеличением скорости разработки происходит невообразимое наслоение технологий. Когда две программы обмениваются данными в формате XML через HTTP протокол (да еще с подписью XML и с защитой HTTP по SSL), то объем кода в библиотеках поддержки XML/HTTP может оказаться гораздо больше, чем объем самого прикладного кода. Значимость готовых, легко доступных, хорошо документированных и поддерживаемых библиотек в таких условиях сложно переоценить.

Но чем дальше технологии движутся в сторону накручивания сложности, тем меньше готовых качественных библиотек оказывается для C++.

Другим классом инструментов, обеспечивающих значительную степень комфорта в использовании языка, является средства разработки, IDE (Integrated Development Environment). Я давно обхожусь без IDE, vim+bash -- это практически все, что мне нужно. Но количество подобных мне приверженцев минимализма является бесконечно малой величиной, которой можно пренебречь.

Особенности языка C++ не позволяют создать для него IDE, сравнимых по удобству использования и "навороченности" с IDE для таких языков, как Java и C#. Многие пользователи IDEA и Resharper рьяно доказывают, что IDE очень серьезно увеличивает их производительность и нет повода им не верить. Раз уж кому-то для написания качественного кода необходимы всплывающие подсказки, контекстно-чувствительное автодополнение, средства автоматизированого рефакторинга и пр., то ничего не поделаешь -- это тот фактор, с которым нужно считаться.

В итоге, C++ прогрывает своим конкурентам как в области библиотек, так и в области IDE.

Язык оказывает самое непосредственное влияние на надежность и отказоустойчивость

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

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

К сожалению, именно катастрофичность проявления ошибок в C++, очень серьезно усложняет разработку приложений 24x7 (а именно этим я занимаюсь последние годы). Многопоточный сервер падает из-за обращения по нулевому указателю в одной из десятков его нитей. Падает в самый неподходящий момент, через пару месяцев непрерывной работы, без stack trace или каких-либо иных следов, облегчающих нахождение причины проблемы. Еще хуже, если ошибка не приводит к мнгновенному краху приложения, а портит какие-нибудь внутренние структры. Из-за чего крах случается совсем в другом месте и совсем в другое время...

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

Просто уже хочется пользоваться этими благами цивилизации :)

Объем имеет значение

Говорят, что плотность ошибок на 1000 строк кода является величиной постоянной и не зависит от используемого языка программирования. Следовательно, чем меньше кода язык позволяет писать для решения задачи, тем меньше в нем будет ошибок. Отсюда даже возник лозунг -- code less -- и к нему хочется присоединиться.

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

a = [ ... ]
b = []
c = []
a.each do |item|
  if <...проверка с использованием item...>
    b << item
  else
    c << item
  end
end

В C++ можно либо использовать старый добрый for:

typedef std::vector< SOME_TYPE > vector_t;

vector_t a; ...
vector_t b;
vector_t c;
for( vector_t::iterator it = a.begin(), it_end = a.end();
    it != it_end; ++it )
  if( <...проверка с использованием *it...> )
    b.push_back( *it );
  else
    c.push_back( *it );

Либо, если привлечь функторы:

typedef std::vector< SOME_TYPE > vector_t;

class selector_t
  : public std::unary_function< const SOME_TYPE &, void >
  {
    vector_t & b_;
    vector_t & c_;
  public :
    selector_t( vector_t & b, vector_t & c )
      : b_( b ), c_( c )
      {}
    result_type operator()( argument_type item )
      {
        if( <...проверка с использованием item...> )
          b_.push_back( item );
        else
          c_.push_back( item );
      }
  };

vector_t a; ...
vector_t b;
vector_t c;
std::for_each( a.begin(), a.end(), selector_t( b, c ) );

Нужно признаться, я предпочитаю последний вариант, даже не смотря на его многословность. Потому что в самом коде работы с a, b, c использвание std::for_each гораздо более понятно и обозримо, чем for.

© 2007-2008 Е.А. Охотников
LastChangedDate: 2007-10-27 21:54:45
e-mail

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

Hosted by uCoz