Моё почтение, уважаемые. Не так давно я выкатил статью про проблемы C++. Точнее про проблемы программистов с языком C++, а так же с другими языками, которые якобы все из себя замечательные. Мой очерк был сварганен под влиянием статьи одного МГУшного деятеля, который потрудился измарать кучу бумаги написав, какой C++ стремный язык, но не потрудился накопать достаточно нормальных примеров и вычистить ошибки в своей статье. Мой ответный удар не остался без внимания и на его сайте Борескова за 15 апреля сего года бал подвешен очередной список разоблачений. Новый список оказался ещё смешнее чем предыдущий, ибо теперь он практически полностью состоит из заблуждений и глупостей либо из вещей, которые, в общем-то, не являются очень большой проблемой C++, а иногда являются проблемой не одного только C++. Короче, аргументы скучные, наезды неинтересные. Ни тебе харкания кровью в оппонентов, ни вырывания меха из жопы. Ни-че-го. Из приведенных там ссылок внимания заслуживают только две — раз и два. Вот некоторые представленные там тезисы и замечания...

Моё почтение, уважаемые. Не так давно я выкатил статью про проблемы C++. Точнее про проблемы программистов с языком C++, а так же с другими языками, которые якобы все из себя замечательные. Мой очерк был сварганен под влиянием статьи одного МГУшного деятеля, который потрудился измарать кучу бумаги написав, какой C++ стремный язык, но не потрудился накопать достаточно нормальных примеров и вычистить ошибки в своей статье. Мой ответный удар не остался без внимания и на его сайте Борескова за 15 апреля сего года бал подвешен очередной список разоблачений. Новый список оказался ещё смешнее чем предыдущий, ибо теперь он практически полностью состоит из заблуждений и глупостей либо из вещей, которые, в общем-то, не являются очень большой проблемой C++, а иногда являются проблемой не одного только C++. Короче, аргументы скучные, наезды неинтересные. Ни тебе харкания кровью в оппонентов, ни вырывания меха из жопы. Ни-че-го.

Из приведенных там ссылок внимания заслуживают только две — раз и два.

Вот некоторые представленные там тезисы и замечания:

«Иногда очень хочется иметь возможность компилировать C++ в "безопасном" режиме, с контролем корректности индексов и указателей, с печатью полного stack trace при обнаружении каких-либо проблем.»

Никто не мешает этого делать, M$ давно выпустила managed C++, как видно из названия весь из себя «managed» и весь из себя «безопасный». Оно?

«В самом начале исключений в C++ не было. Затем поддержка исключений появлялась в разных компиляторах в разное время. Затем появился стандартный класс std::exception в качестве базового для всех исключений. Все это привело к тому, что разные C++ библиотеки используют разные подходы к диагностированию и обработке ошибок Из-за этого при разработке C++ с применением разных библиотек приходится использовать в коде разные подходы к обработке ошибок. Это утомительно. К тому же на фоне современных языков это еще и выглядит напрасной тратой времени.»

Нет не приходится. Нормально спроектированная и нормально реализованная система базируется на некотором фреймворке собственного производства. Этот фреймворк состоит из нескольких слоев. Причем этих слоев тем больше чем лучше он спроектирован. Database abstraction layer, File system abstraction layer и так далее. Выкрутасы любой сторонней библиотеки прячутся на одном из слоев вашего фреймворка и для пользователей фреймворка становятся абсолютно невидны, будь то исключения, коды возврата или ещё что-нибудь.

К любому сколь угодно кривому интерфейсу можно написать любой сколь угодно удобный декоратор. Это аксиома. А вот если подключать сторонние библиотеки в слой бизнес логоки, то таки да, вы очень быстро разведете там говнище. Причем это не зависит от выбранного языка. Это произойдет в C++, и в Джаве и ДоДиезе. Поминится в одном моем проекте помимо прочих было две функции. Одна представляла из себя pop3 сервер, а вторая smtp сервер. Обе сделаны на базе MFCшных текстовых сокетов. Расширение сознания было полное )).

«Тем не менее, многие качества шаблонов явились всего лишь побочным явлением их дизайна. В частности метапрограммирование на шаблонах. Мне кажется это ненормальным. Либо шаблоны нужно было ограничить, либо расширить так, чтобы не нужно было писать вагоны кода для создания класса IsFunction или FunctionReturnType. Ничего подобного благодоря затянутым процедурам стандартизации языка не произошло.»

Да, затянутая процедура стандартизации это проблема.

«Я просто не считаю нормальным необходимость писать сотни строк шаблонного кода для того, чтобы обойти органичения стандарта языка и дыры в его реализациях. Чтобы в результате получить решения, в которых способны разобраться избранные продвинутые C++ники.»

Не надо разбираться в исходниках Буста. Его надо просто качать и использовать так как написано в документации. Или не качать и не использовать. Библиотеки они для того и пишутся, чтобы подключил и начал использовать. Без особого геморроя и плясок с бубном.

«Стоит добавить в проект пару-тройку шаблонных классов и использовать STL, как время компиляции увеличивается чуть ли не на порядок. По сравнению со скоростью компиляции того же D или даже Java скорость компиляции C++ оказывается слишком, слишком медленной.»

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

«Следствием того, что язык C++ никогда не имел большой стандартной библиотеки и при этом развивался в течении длительного времени (имеется в виду добавление в язык шаблонов и исключений) стало наличие целого зоопарка библиотек для выполнения одних и тех же действий. Например, только для GUI можно вспомнить Qt, wxWidgets, FOX Toolkit и Fltk (не считая переодически возникающих молодых конкурентов). Аналогичная картина для работы с XML. Аналогично для системных средств (т.к. многопоточность, файловая система, IPC): ACE, POCO, Boost, Qt.»

Это же хорошо — есть из чего выбрать.

«Все это сопровождается разными подходами к управлению памятью и информированием об ошибках. Так, Qt и FOX Toolkit навязывают разработчику собственную политику использования памяти. POCO выбрасывает исключения для информирования об ошибках, в то время как ACE использует коды возврата и errno»

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

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


Короче:

«Препроцессор - ничего не знающий о типах, о пространствах имён, о приоритетах операторов. Это действительно серьёзный недостаток C++, и препроцессор я в своём коде использую по минимуму.»

Да это проблема. Хотя если его действительно использовать по-минимуму, то и проблем будет минимуму.

«Контекстно-зависимая грамматика.»

Спорный момент. Любой естественный язык на котором общаются люди, является контекстно-зависимым. Причем некоторые языки очень контекстно-зависимые. Что однако не мешает пятилетним детям без учебников, специализированных курсов и мастерклассов научиться более менее сносно объясняться на нем. Странно что у взрослых дядек языки с контекстно-зависимой грамматикой вызывают недетские истерики.

«Модель разделения на файлы заголовков и реализации. Это порождает массу проблем...»

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

«Недостаточно полное стандартное RTTI (implement-specific typeid) и как следствие отсутствие в явном виде reflection (что легко лечится при помощи typeof + явного базового класса + описателя) и, в принципе никто не мешает его поддержать в языке - проблема медленного процесса стандартизации.»

Да, есть такое. Да, решается именно так. Некритично.

«Кривая реализация спецификации исключений (run-time with unexcepted_exception вместо compile-time). Проблема целиком и полностью рук, писавших компиляторы. В языке C++ я, да и не только я, предпочли бы видеть именно compile-time спецификации исключений.»

Да, это проблема. Тоже в общем-то некритично. Вряд ли будет когда-нибудь внедрено. Ибо тезис о том что «мы не платим за то что не используем» никто не отменял.

«Отсутствие анонимных функций и замыканий. Проблема медленного процесса стандартизации.»

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

«Недостаточно полная стандартная библиотека - что вполне закрывается boost'ом.»

Да, Буста вполне хватает.


В конце той новости Боресков все-таки не сдержался, и откровения из него начали бить тугой струёй. Вот избранное:

"Замечательный пример - в якобы статически типизированном языке можно объявить функцию с параметром типа const std::string &, и передать ей значение false:

#include    
#include    

void f ( const std::string& s )
{
    printf ( "%s\n", s.c_str () );
}

int main ( int argc, char * argv [] )
{
    f ( false );
   
    return 0;
}

Скорее всего это все полностью соответствует стандарту, но ведь это бред - значение одного типа легко переводится в значение совершенно другого типа. И никаких warning, все успешно компилируется и при запуске падает."

Борескову невдомек, что согласно определению C++ является статически типизированным языком (http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D1%82%D0%B8%D0%BF%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F).

Более того такое поведение полностью соответствует стандарту. False трактуется как NULL, а NULL как NULLовский char *. В итоге никакого нарушения в логике работы программы или функционирования компилятора не происходит — говно написали, говно получили. Наука не придумала ещё способа превращать говнокод в шедевр кодирования и алгоритмизации. Хотя возможно Борескову и открылась эта несомненно божественная истина. Кто знает.

«Большая просьба к тем, кто увидев это захочет сказать, что я ничего не понимаю в таком прекрасном языке как С++...»

Таких заявлений я делать не буду ибо с автором лично не знаком.

«... сколько всего на нем написано и т.д...»

На нем действительно очень много всего написано и это зачастую является решающим фактором в выборе языка разработки. Это верно для любой промышленного софта. Хотя незатейливые демки по компьютерной графике можно клепать хоть на Коболе. В этом случае фзык не важен вообще. «...прочтите для начала пару ссылок по objective-C, и сравните с С++. Some Nice Features of the Objective-C Language и моя статья по objective-C.»

Первая статья шлак — просто набор лозунгов. Вторую статью разберу позже.

«Просто, чтобы понять альтернативу.»

Уссачка полная. Сразу вспомнилась боянистая картинка про малолетних идиотов:

Что касается objective-c++:

«Язык был придуман Брэдом Коксом (Brad Cox) в начале 80-х годов прошлого века. Целью Кокса было создание языка, поддерживающего концепцию software IC. Под этой концепцией понимается возможность собирать программы из готовых компонент (объектов), подобно тому как сложные электронные устройства могут быть легко собраны из набора готовых интегральных микросхем (IC, integrated curcuits).

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

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

«Одной из целей было также создание модели, в которой сами классы также являются полноценными объектами, поддерживалась бы интроспекция и динамическая обработка сообщений.»

За подобные объединения сущностей надо жечь на костре.

«...в язык С просто добавлены новые возможности для объектно-ориентированного программирования. При этом любая программа на С является программой и на Objective-C (для языка С++ это не верно).»

С++ является «почти» расширением языка C. Просто взяли и выкинули из C фичи, которые явно не нужны или приводили к ошибкам. Поэтому возникла некоторая несовместимость. В Объектный С весь этот шлак видимо вошел в полном объеме. Мой привет авторам )).

«Еще одной из особенностей языка является то, что он message-oriented в то время как С++ - function-oriented. Это значит, что в нем вызовы метода интерпретируются не как вызов функции (хотя к этому обычно все сводится), а именно как посылка сообщения (с именем и аргументами) объекту, подобно тому, как это происходит в Smalltalk-е. Такой подход дает целый ряд плюсов - так любому объекту можно послать любое сообщение. Объект может вместо обработки сообщения просто переслать его другому объекту для обработки (так называемое делегирование)...»

Ах, делегирование это само по себе прелесно. Однако ни одна фича в серьезных проектах не используется сама по себе, а всегда в сплаве с другими фичами. Не всем известно, что такая суперпозиция фич приводит к интересным эффектам. Например взять Python. Там тоже есть делегирование, есть сборка мусора и есть функция del, которая позволяет принудительно удалять созданные объекты. Теперь следите за ловкостью рук: создаем объект, делегируем вызов какого-либо из методов этого объекта другому объекту, для первого объекта вызываем del. Что произойдет при вызове функции del? В больших проектах мы этого сказать не сможем. Потому что при делегировании был передан не только указатель на метод но и неявно был передан указатель на объект, делегирующий вызов своего метода. Ну это вполне логично, потому что метод может использовать поля объекта, и для этого ему нужен указатель на сам объект. Теперь дальше: del удалет объект если счетчик ссылок на него равен 1. Т.е. Кроме ссылки передаваемой del никаких других ссылок нет. А в нашем примере есть. Поэтому удаления не произойдет (кстати dtlete в C++ ведет себя гораздо более честно — нам дана гарантия что для корректного указателя будет корректно освобождена память). Более того, о том что удаление было сорвано мы никак не узнаем — del тупо ничего не возвращает и не кидает исключений. Есть две возможности обойти эту проблему — либо руками лезть в кишки GC механизма, либо для каждого класса делать функцию Release, которая сама будет принудительно удалять выделенные ресурсы. В итоге от чего пытались уйти к тому же и пришли — ручное управление ресурсами, практически ничем не отличающееся от того что реализуется в каждой C++ программе.


«в частности именно так можно легко реализовать распределенные объекты (т.е. объекты находящиеся в различных адресных пространствах и даже на разных компьютерах).»

Очень интересно посмотреть как это реализуется и в какой оверхед обходится. Отчего-то Боресков обо всем этом умолчал.

«Язык Objective-C поддерживает нормальную работу с метаинформацией - так у объекта непосредственно на этапе выполнения можно спросить его класс, список методов (с типами передаваемых аргументов) и instance-переменных, проверить, является ли класс потомком заданного и поддерживает ли он заданный протокол и т.п.»

Ну отлично.

«Судя по заявлениям в новой версии Mac OS X 10.5 будет кроме gcc (используемого для всех предыдущих версий) еще и компилятор от Intel'а. Также по слухам в этой версии операционной системы будет использован Objective-C 2, куда в частности войдет поддержка сборки мусора (garbage collection)»

Значит ручной мэнэджмент ресурсов остался. Интересный тандем из высокоуровневой message-oriented парадигмы и низкоуровневого управления ресурсами.

«Язык Objective-C позволяет снабжать метками каждый аргумент, что заметно повышает читаемость кода и снижает вероятность передачи неправильного параметра.»

А чтоже компилятор? Неужто он не следит за правильностью передачи параметров?

«В языке Objective-C нет встроенного типа для булевский величин, поэтому обычно такой тип вводится искусственно. Далее я буду для логических величин использовать тип BOOL с возможными значениями YES и NO (как это делается в операционных системах NextStep, Mac OS X).» Тяжелое наследие C. Это несомненно делает Объектный С самым замечательным языком в мире.

«Первым достаточно серьезным применением языка Objective-C было его использование в операционной системе NextStep. Для этой системы было написано большое количество различных классов на Objective-C, многие из которых до сих пор используются в Mac OS X.»

Автору невдомек, что первое серьезное применение C++ состоялось 35 лет назад. С тех пор он был использован на множестве систем в том числе и яблочных операционках. Для C++ было огромное количество классов (не всего лишь «достатосно большое», а огромное).

«В версии runtime от Apple все классы имеют общего предка - класс NSObject, содержащий целый ряд важных методов.»

Почему-то наличие метаинформации и ретроспекции не позволило избавиться от этого уродства.

«Для ускорения поиска сообщений по dispatch table используется кэширование, позволяющее заметно снизить затраты на пересылку сообщений.»

В C++ для вызова метода через vtable ничего не надо кэшировать. Т.к. стоимость вызова виртуального метода это стоимость всего одного перехода по этой таблице. Это позволяет вызывать методы с минимальным оверхедом.

«В отличии от языка С++ (в котором несмотря на то, что всем известно, что метод - это просто функция с дополнительным параметром this, нет никакого официально документированного способа получить указатель на эту функцию) в языке Objective-C можно по селектору метода получить адрес реализующей его функции (именно как функции языка С).»

Очень интересно послушать про ситуацию когда адрес функции получен (а следовательно и указатель на объект), сам объект удален, а мы начали полученную функцию «мацать» по всякому.

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

Автор даже к середине своей статьи не может определиться, большой оверхед у посылки сообщений или маленький, зло это или рулез. Расширение сознания полное. Санитары в Кащенко плачут от зависти и рвут на себе белые халаты.

«В языке Objective-C поддерживается обработка исключений очень похожая на используемую в языках C++ и Java.»

Это чтоже-то получается? В белом и пушистом Объектном С используются исключения как в богомерзком C++? Господи да куда мир катится!!!

«Язык Objective-C поддерживает синхронизацию для многопоточных приложений. При помощи директивы @synchronized () можно защитить фрагмент кода от одновременного выполнения сразу несколькими нитями.»

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


«В самом языке Objective-C нет специальных команд для создания и уничтожения объектов (подобных new и delete). Эта задача ложится на runtime-библиотеку и реализуется при помощи механизма посылки сообщений.»

Это прелесно.

«Mac OS X (как и NextStep) для управления временем жизни объектов используют reference counting - каждый объект содержит внутри себя некоторый счетчик, при создании устанавливаемый в единицу. Посылка объекту сообщения retain увеличивает значение этого счетчика на единицу (так все контейнерные классы библиотеки Foundation при помещении в них объекта, посылают ему сообщение retain). Установившейся практикой является посылка объекту сообщения retain всеми, заинтересованными в нем сторонами (объектами), т.е. если вы запоминаете ссылку на объект, то следует послать ему сообщение retain. Когда объект перестает быть нужен, то ему просто посылается сообщение release.»

Подобное несомненно повышает безопасность программ и резко снижает количество ошибок допускаемых при управлении ресурсами. Если Объектный С полностью совместим с С то что будет если я вызову malloc? Что будет если я вызову realloc?

«Язык Objective-C обладает крайне редко встречающейся возможностью добавлять новые методы к уже существующим классам. При этом не требуется исходников класса и добавленные методы автоматически становятся доступными всем классам, унаследованным от изменяемого.»

Очень инетересно узнать, что произойдет если две библиотеки от разных производителей начнут добавлять методы в какой-либо third-side класс, причем не просто добавлять новые методы а переопределять уже существующие. Очень интересно как будет происходить отладка этого приложения. Есть мнение что для отладки декалитрами кофе уже не обойтись. Понадобится феназепам, а может что-нибудь ещё по-круче.

«Еще одной интересной особенностью является возможность инициализации class object'ов - в начале работы приложения каждому class object'у посылается сообщение (класса) initialize.»

Что? Опять ручное управление ресурсами?

В конце ещё раз повторюсь, что не было желания кого-то или что-то закозлить а кого-то или что-то оправдать. Просто было желание показать, что врут все кто говорят, что недостатков нет, все кто говорят что проблем нет, никогда c настоящими проблемами не сталкивались, все кто говорят что реализована замечательная фича, никогда не задумывались о том что лежит в основе этой фичи. Ибо любое внедрение козырной фичи в конце концов приводит к жесткому самоограничению. Так было с наследованием, с множественным наследованием, там было с глобальными переменными и пр.