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

[prog.thoughts] О C++ных библиотеках вообще, и о Boost-е в частности

Попрограммировав недавно на Java, я понял “в чем сила” Java – в IDE и библиотеках. Поскольку IDE без библиотек мало чего стоит, то получается, что самым главным достоинством Java является огромное количество готовых библиотек. Но не только наличие, а еще и простота, с которой сторонние библиотеки подключаются к Java-проектам. Берешь jar/zip файл, прописываешь его в CLASSPATH и все. А с использованием Maven2 даже об этом думать не нужно – достаточно включить упоминание нужной библиотеки в список зависимостей проекта и сам Maven2 скачает ее, установит и пропишет в CLASSPATH (eao197: впрочем, Maven2 видел очень издалека, поэтому могу идеализировать).

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

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

Поэтому напрашивается первый вывод: C++ные библиотеки должны распространятся в виде исходных текстов.

Конечно, есть множество закрытых библиотек, разработчики которых никогда не захотят открыть исходные тексты. Ну да и фиг с ними, не о том речь. Речь о библиотеках общего назначения – для работы с датой/временем, регулярными выражениями, таймерами, сокетами и другими видами IPC, FTP/HTTP/SMTP/POP3/IMAP и пр. транспортами и т.д., и т.п.

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

Единая система сборки должна быть кроссплатформенной и легкой в изучении/использовании. Исключительно Unix-овые Autotools, а так же заточенные под конкретный компилятор и make-утилиты Makefile идут в топку. Лично мне хотелось бы, чтобы build-tool сам управлял сборкой (как делает SCons), но вполне бы устроил меня и генератор проектных файлов вроде CMake или MPC.

Но всего этого мало. Еще нужна система дистрибуции библиотек и отслеживания зависимостей между ними. Что-то вроде Maven2-репозиториев или RubyGem-ов. Например, я публикую в каком-то репозитории свои проекты cpp_util-2.5.0 и cls-3.1.0, причем cls-3.1.0 зависит от cpp_util-2.5.0. Если кто-то захочет использовать cls-3.1.0, он автоматом должен получить и cpp_util-2.5.0.

Все три составляющих я считаю очень важными. Но самым важным из них является как раз система дистрибуции. Имхо, без нее нельзя будет заниматься разработкой небольших, заточенных под конкретную задачу, библиотек. Например, если мне нужен генератор UUID, то я хочу получить только маленькую библиотеку с минимальным списком зависимостей, а не полутора мегабайтную POCO, десяти мегабайтную ACE или тридцати мегабайтный Boost.

Ну а раз Boost был помянут, то пора вставить ему очередной пистон. Как по мне, сама организация Boost-а напрочь убивает идею маленьких предметно-ориентированных библиотек. То, что процветает в Ruby и Java, в C++ усилиями Boost-оводов становится невозможным. Например, вам нужна библиотека для UUID? Нет проблем – берите Boost. Недорого, всего 30Mb каждая версия. Не хотите столько таскать со своим проектом? Нет проблем – в Boost-е есть bcp, он вам вырежет только то, что нужно. Ах, со временем вам потребовалось еще и filesystem? Ну что же, вновь берете bcp в зубы и старательно выпиливаете из Boost-а UUID вместе с filesystem.

Может быть для старых пользователей Boost-а это и выглядит нормальным. Но для меня это дикость. Если бы Boost изначально не был одной большой помойкой (в самом хорошем смысле этого слова), а состоял из модулей с отслеживанием зависимостей между ними, то ситуация для пользователей вроде меня была бы проще и удобнее. Скажем, есть модуль Boost.Config. От него зависит модуль Boost.Uuid. Если мне нужен только Boost.Uuid, то я скачиваю именно его и получаю в нагрузку только Boost.Config. Когда со временем мне понадобится Boost.Filesystem, я возьму просто Boost.Filesystem и все, т.к. Boost.Config у меня уже есть.

Естественно, у каждого модуля в Boost-е есть свой набор версий. Для каждой версии указывается, с какими версиями зависимостей он был протестирован. Опять же, Boost-оводы имеют возможность выпускать согласованные обновления. Например, все модули версии 1.47.0 взаимно совместимы друг с другом. При этом какая-то библиотека может иметь собственную историю развития до следующего большого релиза. Скажем, пока Boost.Config остается на версии 1.47.0, какой-нибудь Boost.Asio может развиться до 1.47.0.154 за счет баг-фиксов.

Такая схема позволила бы решить еще одну проблему, с которой Boost-оводы начинают сталкиваться: отказ авторов библиотеки от ее дальнейшего развития. Блин, ежу было понятно, что в мире OpenSource никто не обязан тянуть свой проект оставшуюся часть жизни. Написал несколько версий, потом обстоятельства изменились – забросил нафиг. Кому нужно, тот подхватит и продолжит, или же напишет что-то свое, еще лучшее. Это нормальный процесс. Посему модель развития Boost-а нужно было выбирать так, чтобы это учитывать. Если бы Boost проектировался не как одна большая помойка (опять же в лучшем смысле этого слова), а как репозиторий множества версий частично связанных друг с другом библиотек (внимательно смотрим на RubyGems и CPAN), то все бы решилось автоматически. Забросил автор какой-нибудь AnotherBoostLibrary ее развитие на версии 1.48.0 – ну и пофигу. Вот она такая библиотека, версии 1.48.0. Работает она в ваших условиях – отлично, берете и используете. Не работает – не берете, либо допиливаете, либо форкаете и переделываете.

Кстати, возражения о том, что разрозненные версии – это плохо, что здорово, когда все централизовано тестируется и обновляется, и тому подобные попытки защитить Boost-овскую централизацию, идут лесом. В мире Ruby децентрализация и зависимости между библиотеками отлично работают. В мире Java все это так же работает. Мир C++ ничем не хуже. В нем так же сработает. Нужно только понять, что монстры вроде Boost-а, ACE или Qt – это не выход.

Хотя на счет Qt… А не забить ли на все большой болт и не пересесть ли полностью на Qt4? Вдруг там все хорошо? ;)

26 комментариев:

Сергей комментирует...

о недостатках Boost - это тема следующего BoostCon, судя по аннонсу Дэйва Абрахамс http://lists.boost.org/Archives/boost/2010/03/163252.php

Сергей комментирует...

правильная ссылка тут

eao197 комментирует...

2Sanik: за ссылку огромное спасибо. Тот инструмент, о котором говорит Абрахамс, можно найти на сайте http://www.ryppl.org

Golovach Ivan комментирует...

Я смотрю Вы немного пересмотрели свое отношение к "тяжелым" IDE в Java? :)
Vi то оно конечно vi, но поддержка Maven, Ant, JUnit, Log4j, ... etc "в одном флаконе" - таки сильно упрощает жизнь.

eao197 комментирует...

2Golovach Ivan: не помню, чтобы я как-то сильно наезжал на IDE именно для Java. В основном я говорил об IDE для C++.

В Java IDE есть две составляющие:

1. Сам язык. Давно не писал на Java, забыл насколько он многословный. Писать разных слов (часто повторяющихся) приходится гораздо больше, чем на C++. Поэтому IDE в Java уменьшает количество писанины приблизительно до уровня ViM в C++ :)

2. Интеграция с Ant, JUnit и пр. Вот тут я полный чайник, а разбираться с различными tool-chain-ами в Java времени не было. Поэтому помощь IDE пришлась кстати. А вот в C++, где эти tool-chain-ы я использую уже давно, я уже создал для себя инструментарий, который позволяет обходиться без IDE.

jazzer комментирует...

> Если кто-то захочет использовать cls-3.1.0, он автоматом должен получить и cpp_util-2.5.0.

А так как у него в проекте уже используется во весь рост cpp_util-2.6.7, то он идет пить вотку, грязно матерясь.

У CPAN ровно те же беды с версионными зависимостями. Ничего там райского нет, плавали, знаем.

> Скажем, есть модуль Boost.Config. От него зависит модуль Boost.Uuid. Если мне нужен только Boost.Uuid, то я скачиваю именно его и получаю в нагрузку только Boost.Config.

ключевое слово тут, я так понимаю, "скачиваю"? В бусте, вообще-то, в принципе никакого скачивания нет, это не веб-сервис, а набор бибиотек. Но никто не мешает сделать такой веб-сервис, который под капотом будет запускать bcp и отдавать тебе результат его запуска. Ну и клиента можно наваять, если очень надо, чтоб сам тянул, а ты только галочками отмечал, что именно тебе надо. Поднять такой сайт - работы на день. Только он реально не нужен никому, видимо, раз не делают. Почему-то вон виндовые программы, распространяемые в виде исходников, не поставляются вместе с WinSDK и прочая, предлагается юзеру все скачать самому, и ничего, вроде, все живы.

Но насчет QТ ты классно отжег :)
Типа АСЕ|Poko тебя не устравивают, а QT вдруг устроит. Что, в QT уже приняли ту схему дистрибуции, о которой ты говоришь?

eao197 комментирует...

>> Если кто-то захочет использовать cls-3.1.0, он автоматом должен получить и cpp_util-2.5.0.

>А так как у него в проекте уже используется во весь рост cpp_util-2.6.7, то он идет пить вотку, грязно матерясь.


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

Но автоматическое отслеживание зависимостей хотя бы скажет -- у вас версия 2.6.7, проекту cls-3.1.0 требуется 2.5.0, эти версии несовместимы, поэтому нужно что-то предпринять.

>> Скажем, есть модуль Boost.Config. От него зависит модуль Boost.Uuid. Если мне нужен только Boost.Uuid, то я скачиваю именно его и получаю в нагрузку только Boost.Config.

>ключевое слово тут, я так понимаю, "скачиваю"? В бусте, вообще-то, в принципе никакого скачивания нет, это не веб-сервис, а набор бибиотек.


Это не набор библиотек. Это одна библиотека. В которой все прибито друг к другу гвоздями и без болгарки (bcp) ничего просто так оттуда не вытащить.

Я уже не помню сколько лет об этом говорю. Но теперь и до самих бустоводов (в лице Абрахамса) это дошло.

>Но никто не мешает сделать такой веб-сервис, который под капотом будет запускать bcp и отдавать тебе результат его запуска.

Ну да, для Boost-а такой сервис, для ACE -- другой, для SObjectizer -- третий. И все нифига не совместимы между собой. Тогда как у Java есть Maven2, а у Ruby -- RubyGems. И никто не говорит, что это никому не нужно.

>Почему-то вон виндовые программы, распространяемые в виде исходников, не поставляются вместе с WinSDK и прочая, предлагается юзеру все скачать самому, и ничего, вроде, все живы.

Это не значит, что нельзя сделать лучше. И что не следует сделать лучше.

>Но насчет QТ ты классно отжег :)

Про QT я в другом ключе упомянул. QT сейчас развивается как совсем отдельная, большая и достаточно целостная платформа. Унифицированная система сборки проектов там есть. Еще добавить туда систему дистрибуции с отслеживанием зависимостей и... И на весь остальной C++ный мир (со всякими ACE, Poco, Boost, wxWidgets и пр.) можно просто забить.

jazzer комментирует...

> Насколько я понимаю ситуацию, сейчас эта проблема вообще нигде не решена. ...... эти версии несовместимы, поэтому нужно что-то предпринять.

А знаешь, что означает "нужно что-то предпринять" в реальном мире? Это означает "а есть тут где-нть место, где можно скачать библиотеки буста, чтоб они точно были совместимы между собой и требовали все одной версии?" И в результате мы автоматически приходим к существующей схеме с мейджор-релизами.

> Это не набор библиотек. Это одна библиотека. В которой все прибито друг к другу гвоздями и без болгарки (bcp) ничего просто так оттуда не вытащить.

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

> QT сейчас ... И на весь остальной C++ный мир (со всякими ACE, Poco, Boost, wxWidgets и пр.) можно просто забить.

Это только тем, кого устраивает лицензия. Ну и заодно - в QT есть MultiIndex? ;)

eao197 комментирует...

>А знаешь, что означает "нужно что-то предпринять" в реальном мире?

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

>Извини, я не вижу принципиальной разницы между отслеживанием зависимостей при помощь специальной тулзовины, которая парсит специальные конфиг-файлы или еще что угодно, и им же при помощь bcp, которая анализирует дерево инклудов.

1. bcp заточена для Boost-а. Можно ли с ее помощью из ACE вырезать, например, только ACE_Time_Value?

2. bcp ничем не поможет, если я вырезал Boost.Filesystem из Boost-1.42.0, а потом кто-то из моего проекта захотел добавить еще и Boost.Uuid из Boost-1.43.0.

Ну и повторю, я считаю очень полезным иметь систему дистрибуции библиотек, которая будет универсальной и не привязанной к конкретному проекту. Чтобы с ее помощью можно было распространять ACE, Poco, Boost.*, ICU и т.д.

>Ну и заодно - в QT есть MultiIndex? ;)

В Boost-е есть средства для работы с XML? ;)

jazzer комментирует...

> Я знаю, что реальный мир очень разный. И то, что у тебя выливается в работу с мажорными релизами вовсе не обязательно является таковым для меня или Васи Пупкина.

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

> 1. bcp заточена для Boost-а. Можно ли с ее помощью из ACE вырезать, например, только ACE_Time_Value?

А CPAN заточен для перла. Т.е. претензия к бустовской утилите заключается в том, что она сделана для буста? :)
Тебя как-то бросает с одной стороны на другую. То ты говоришь, что в самом бусте все плохо и одну библиотеку не достать, то теперь говоришь, что ее нельзя применить для АСЕ. Ты уж определись, что ли...
Кстати, вполне может быть, что bcp сможет выдрать ACE_Time_Value.

> 2. bcp ничем не поможет, если я вырезал Boost.Filesystem из Boost-1.42.0, а потом кто-то из моего проекта захотел добавить еще и Boost.Uuid из Boost-1.43.0.

Тут есть радикальная разница с, скажем, CPAN - в бусте есть мажорные релизы, в которых гарантированно все со всем совместимо и оттестировано.
Так что достаточно переехать целиком на Boost-1.43.0, и проблем не будет. В случае же CPAN решения в виде согласованного набора версий всех тех библиотек, которые тебе нужны, может и не существовать.

> Ну и повторю, я считаю очень полезным иметь систему дистрибуции библиотек, которая будет универсальной и не привязанной к конкретному проекту. Чтобы с ее помощью можно было распространять ACE, Poco, Boost.*, ICU и т.д.
Ну кто бы спорил :)

>>Ну и заодно - в QT есть MultiIndex? ;)
>В Boost-е есть средства для работы с XML? ;)
Ты правильно уловил мою мысль ;)
На весь остальной мир просто забить не получится.

eao197 комментирует...

>Т.е. претензия к бустовской утилите заключается в том, что она сделана для буста?

Претензия к Boost-у у меня такая: если уж бустоводы так озадачились счастливым будущим C++, то было бы разумно в рамках Boost-а делать инструменты, полезные большому количеству C++ разработчиков. Вот shared_ptr и multi_index -- хорошие примеры. Дальше нужно сделать еще один шаг -- создать для С++ универсальную систему дистрибуции библиотек в исходных текстах.

Мне кажется, что было бы логично, если бы такая система появилась именно в рамках Boost. Но ее там нет. А то что будет опять окажется заточенной под Boost.

Понятно, что Boost никому и ничего не должен. Но уж если Boost претендует на важную роль в развитии С++, то...

jazzer комментирует...

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

Ну и потом, даже если такая мега-тулзовина и будет написана (в чем я, лично, сомневаюсь в принципе - все библиотеки имеют свои собственные хитрые системы сборки, с генерящимисяс файлами и прочими радостями), какие-нибудь АСЕ-овцы могут сказать, что они не обязаны пользоваться бустовскими поделками, и не будут добавлять поддержку для нее - и чего? Бустоводы должны подсуетиться, иначе это опять им будет претензия, типа недостойны взятой на себя миссии?

eao197 комментирует...

У бустоводов проявляется тенденция к выбору неправильных решений в области огранизации библиотеки. Сначала Boost.Jam в качестве сборки. Потом Boost как большая свалка разных библиотек (от чего сам Абрахамс сейчас предлагает отказаться). Теперь вот еще свой ryppl.

Что до общего признания какого-нибудь мегатула, то это эволюционный процесс. Ant в Java не сразу стал де-факто стандартом. А теперь, наверное, все Java IDE генерируют Ant-овский build.xml. Тот же Maven2 пока еще не стандарт, но постепенно набирающий авторитет инструмент. Тут ведь очень многое будет значить использование в крупных и знаковых проектах.

Например, выбрал бы Boost в качестве системы сборки SCons, CMake или qmake. И начал бы пропагандировать необходимость использования одного общего инструмента -- глядишь, и потянулся бы народ. Я бы и сам пошел бы на отказ от mxx_ru в пользу чего-нибудь общепринятого (в первую очередь SCons, но пойдет и CMake).

jazzer комментирует...

> Например, выбрал бы Boost в качестве системы сборки SCons, CMake или qmake.

Уже есть экспериментальная сборка буста на CMake, с предпоследней версии, по-моему.

Ну что, перейдешь теперь на CMake?

eao197 комментирует...

Экспериментальная не считается. :)

Но вне зависимости от Boost-а события развиваются так, что переход с mxx_ru на CMake уже выглядит для меня нормальным менеджерским решением.

Сергей комментирует...

CMake неоффициален, потому что перцы типа Вовы Прюса, недовольны уходом от буст.билда. Но это не значит что он не жизнеспособен.
Я так понял единственная твоя претензия к Ryppl - это git. По мне - так это плюс. Git значительно удобнее и продвинутее SVN.
Кроме того, в Ryppl используется CMake, как система билдов.

eao197 комментирует...

>Я так понял единственная твоя претензия к Ryppl - это git. По мне - так это плюс. Git значительно удобнее и продвинутее SVN.

А вот я вообще против распределенных VCS - в моих условиях это лишний геморрой. И Svn-ом с Git-ом набор VCS не ограничивается. Есть Perforce, есть Mercurial. С какой стати отдавать преимущество именно Git-у?

>Кроме того, в Ryppl используется CMake, как система билдов.

Ну вот, умнеют на глазах.

Сергей комментирует...

> А вот я вообще против распределенных VCS - в моих условиях это лишний геморрой. И Svn-ом с Git-ом набор VCS не ограничивается. Есть Perforce, есть Mercurial. С какой стати отдавать преимущество именно Git-у?

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

eao197 комментирует...

>ты просто не умеешь его готовить :)))

100%

>а что за условия, которые доставляют тебе геморой с распределенными VCS?

Постоянно доступный Svn-сервер, хорошие каналы связи к нему. И коллеги, которые иногда умудряются лажать даже в svn.

Плюс, в соседних коммандах широко используется интеграция Jira и Confluence с Svn-ом.

Сергей комментирует...

> И коллеги, которые иногда умудряются лажать даже в svn.

это не проблема VCS-а, как ты сам понимаешь

> Плюс, в соседних коммандах широко используется интеграция Jira и Confluence с Svn-ом.
Он отлично интегриться и с Git. Было бы неожиданно, если бы Atlassian не поддерживал его.

Rustam комментирует...

В Boost-е есть средства для работы с XML? ;)

Чуть-чуть уже есть http://www.boost.org/doc/libs/1_42_0/doc/html/boost_propertytree/parsers.html#boost_propertytree.parsers.xml_parser

А так конечно согласен с Евгением, даже поработав на питоне у которого тоже есть проблемы с версионностью библиотек и т. п. при возврате на C++ грустно.

eao197 комментирует...

>это не проблема VCS-а, как ты сам понимаешь

Не VCS-а. Но если люди с простым инструментом не сильно справляются, то стремно им в руки сложный давать.

Сергей комментирует...

> Не VCS-а. Но если люди с простым инструментом не сильно справляются, то стремно им в руки сложный давать.

странны слова твои :) люди имеют способность к обучению. обучай. это вопрос продуктивности. пример - лопата - землеройная машина.
если с обучением проблема и ты не знаешь что с этим делать - енфорси правила пре/пост коммит хуками. данная функциональность имеет место во всех современных VCS.

а лучше используй обе возможности ;)

eao197 комментирует...

2Sanik: как бы то ни было, кто-то предпочитает Git, кто-то Svn, кто-то Mercurial, кто-то Perforce и пр. Тема отдельного флейма почему это так.

Но вот в ryppl решили привязать разработку к Git. А в RubyGems и Maven2 такой привязки нет. И я считаю, что в ryppl идут по неправильному пути. Так же, как они шли используя Boost.Jam.

Сергей комментирует...

Ryppl делается на Python.
Python уже имеет кучу модулей поддерживающих множество VCS. Пока Трой (автор) обкатывает на Git, поскольку ему так проще и он знает как его использовать.
Вариации могут быть следующие
1) появиться поддержка других VCS, если это понадобится кому либо (например eao197) и тул будит стоить того
2) у git есть куча поддерживаемых VCS, которые позволяют импорт/экспорт дельты кода из/в оригинальный репозиторий

eao197 комментирует...

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