Как стать автором
Обновить

Как я стандартную библиотеку C++11 писал или почему boost такой страшный. Введение

Время на прочтение5 мин
Количество просмотров24K
Всего голосов 55: ↑52 и ↓3+49
Комментарии32

Комментарии 32

ну под QNX 6-то был 4.8.2, там C++11 был, но не в полном объеме.
хотя мне первоначально приходилось тащить совместимость кодовой базы из-под четверки с Watcom С++ 11.
stdatomic пришлось наследовать и переопределять, т.к. в оригинале в методах были не-volatile переменные на стеке, что вводило шестерку при вызове метода из обработчика прерывания в полное изумление.
Во первых хотел бы выразить благодарность за комментарии и интерес к статье, это чертовски приятно, как оказалось.

Если по теме: у нас и QNX 6 и QNX 4.25. Причем «одобренного» 4.8.2 под 6-ку нет. Хотя даже если бы был, то я думаю все равно полез бы в эти дебри, потому как в первую очередь есть еще Borland C++ Builder, а так же ведь очень увлекательно это все. Пожалуй увлекательно — в первую очередь даже.

А с QNX постоянно у всех какие то приключения, вы не первый разработчик от кого узнаю про веселые франкенштейны g++ с Watcom под эту платформу. На atomic пока что не замахивался, но похоже он пишется именно под каждую ОС \компилятор свой уникальный, и боюсь здесь никак это не реализовать не скатываясь в постоянные ifdef компилятор_1 elseif…
а что значит «одобренного нет»? через Foundry27 он вполне себе раздавался, сейчас уже не могу посмотреть, т.к. год как в том месте не работаю.
с атомарными операциями в четверке как раз проще все было, там int был атомарный. но ради совместимости API приходилось это прятать под четверкой в макросы, а под шестеркой в шаблоны функций.
это не считая того, что один из разработчиков говорил «классы зло, хочу C-style», другой «а где мое ООП?», и мне в API приходилось удовлетворять их обоих.
«Одобренного» значит что у меня был определенный дистрибутив, с определенным компилятором, и другим собирать было «ни-ни». Сейчас да, уже переползаем на что-то поновее. =)

У вас закрытый проект? Интересно было бы посмотреть на самом деле.
проект закрытый, к тому же мне пришлось его покинуть по естественным причинам. но было много публикаций в открытых источниках. гуглить по ключевым словам «УВК АДВ», «ЦСПА ОЭС Сибири»
И по поводу продолжения сразу скажу — руки завтра, думаю, дойдут до Главы 2 с кодом уже и посерьезнее материалом. Надеюсь что возможность редактирования сохраняется для статей и можно будет сделать оглавление в каждой.
Очень интересно, большая работа, респект. Я делал нечто похожее, реализовывал недостающие части стандартной библиотеки С++11 под Arduino AVR. Но мне было в 100 раз проще, там компилятор поддерживал С++11, только библлиотек и не хватало.

Я не понимаю, как вы могли реализовать С++11 библиотеку без С++11 компилятора. Например, как сделан auto? Я пытался портировать некоторые новые фичи С++17 в свою библиотеку, но быстро понял, что те вещи, которые требуют поддержки именно со стороны компилятора/языка, я реализовать не могу.
Сомневаюсь, что автор реализовывал auto, decltype и самое главное — move semantics. Для этих вещей нужна поддержка компилятора.
Как совершенно верно подметили я, естественно, не реализовывал встроенные в язык фичи компилятора типа auto, так как без доступа к исходникам или внешнего кодогенератора сделать такое не реально. Речь идет именно о реализации стандартной библиотеки по максимуму со следующими ограничениями: без надежды на UB, без реализации через нестандартные макросы компилятора (хотя здесь пришлось сделать исключение, об этом расскажу), с минимальной завязкой на конкретные версии компиляторов (только для определения «степени» поддержки C++). В общем из зарезервированных слов и «языковых» фич компилятора возможно сделать static_assert да nullptr через макросы, и они подключаются в core.h.

PS: спасибо за добрые слова, и с поддержкой 11 стандарта конечно реализация была бы полной. Очень не хватает decltype в основном для завершения type_traits.
(оффтоп)
Никогда не понимал, зачем было сначала (придумывая c++ из c) портить NULL и заодно вводить опасное неявное int->pointer преобразование без варнингов для испорченного NULL'а, а потом придумывать новое слово (nullptr) для обозначения того, что раньше обозначалось как NULL.
Дык NULL же «улучшили»! Почти во всех реализациях NULL — это "((void*)0)". Но не во всех. И без заголовочных файлов этого и не выяснить.

Вот Страуструп решил, что обучить компилятор превращать число 0 в NULL будет удобно. Не нужно никаких хедеров, компилятор всё и так знает.

Несколько лет в начале пытались поддерживать то разделение, которое де-факто имеется в GCC, к примеру: стандартная библиотека отдельно, компилятор — отдельно.

Но потом оказалось, что отделать стандартную библиотеку C++ от компилятора всё сложнее и сложнее (так что сейчас, де-факто, каждый компилятор работать только со «своей» библиотекой, с некоторыми исключениями, когда компилятор «подвешивается» на чужую библиотеку как ICC) — а вот зато от того, что у NULL'а тип int всем было реально плохо… Так что собрались — и исправили…

P.S. Забавно тут другое: в C уже был подобный прецедент по подбным же историческим причинам. 'a' имеет тип int. Соотвественно sizeof('a') будет на большинстве современных платформ 4. C++ — это исправил… и тут же породил путаницу с NULL'ом… Может Страуструп был большим поклонником Ломоносова?
Так ведь в C++ запретили implicit cast из void* к любому указателю, отсюда и проблемы пошли с Сишной имплементацией NULL как ((void*)(0)), а так как хотели библиотеку std отвязать от компилятора по максимуму и совместимость с Си сохранить, то вот он новый NULL как 0 — рыцарь в сияющих доспехах. Ну а раз 0 это int, то здравствуй неявное преобразование 0->pointer (заметим что не int->pointer все же).
Ну так сделали ж 0->pointer, могли бы вместо него сделать ((void*)0) -> pointer и зафиксировать в стандарте что NULL = ((void*)0), а остальное не трогать.
Черт его знает, если честно. Может создателям компиляторов на тот момент это было не удобно так реализовывать. А может еще что знал товарищ Страуструп, чего мы не знаем.
Не успел комментарий отредактировать. Есть еще один тонкий момент, ведь как заметил khim, не во всех реализациях NULL был ((void*)(0)). Потому всеравно нужно было что то свое городить.

Реализация собственной стандартной библиотеки это и правда тяжёлая и весьма скорпулезная работа, требующая глубокого понимания стандарта. Но позвольте задать вопрос несколько выходящий за рамки текста и пересекающийся с одним из комментариев. Скажем, есть компилятор поддерживающий последний стандарт, но стоит он на системе с отсутствием стандартной библиотеки для этого стандарта (свой компилятор на старом линухе, например) — есть ли здесь решения кроме как написание своей библиотеки?

Все зависит от того как реализован компилятор. К примеру тот же GCC имеет добротную открытую стандартную библиотеку, и в основном что требуется от компилятора так это поддерживать все их специфичные built-in'ы. Так же важно понимать на каком железе работает данный компилятор, к примеру вопрос атомарности операций в разных архитектурах будет решаться по разному и здесь нужно учитывать есть ли GCC именно под вашу если заимствовать стандартную библиотеку из него.
А так по сути вариантов немного, либо свою, либо часть заимствовать, но близкие к компилятору вещи писать опять свои.
Можно воспользоваться чужой работой. Вот тут есть интересная штука: GCC 7 со стандартной библиотекой реализованной как «аддон» к библиотеке GCC 4.4+.

То есть то, что в GCC 4.4 если в разделяемой библиотеке — используется оттуда, то, чего не хватает — поступает из статической библиотеки. Как результат — можно использовать C++17 и при этом собирать бинарники, которые на каком-нибудь RHEL 6, выпущенном 7 лет назад, запустятся.

Конечно чтобы прикрутить это к вашему дистрибутиву — потребуется напильник весьма немаленьких размеров, но… дорогу осилит идущий.
НЛО прилетело и опубликовало эту надпись здесь
Настолько просто и понятно о буднях. Нет, чтобы перевернуть землю, и заставить утилиты собирать boost. Никакого чувства романтики.
Мне нравится простое и понятное, и потому смотря на потроха boost я каждый раз грущу от нагромождения макросов и восхищаюсь красотой костылей. А заставить утилиты собирать boost — это разобрать весь boost по косточкам и добавить своих проверок, переписав местами чужой код. При том возможно и не заведется все равно.
Вы правы, никакой романтики.
При том возможно и не заведется все равно.
Не, не заведётся. Вот тут замечательная история описана: мы пилили-пилилили, пилилили-пилили, 40 багов исправили за 3 года, упарились… но вот вам ещё заплатки на два десятка оставшихся багов, с ними boost::hana кое-как заводится.

И тут ведь шла речь о разработчиках компилятора, которые вольны были выбирать — будут они править баг в компиляторе или заплатку в библиотеку вкручивать!

А если у вас компилятор старый и кривой? Вообще без шансов…
А вот это уже романтика и увлекательная история. Спасибо =)
Извините за неудачный комментарий. Я вообще — то на эзоповском языке хотел сказать, что собирать буст намного лучше, чем писать статьи, как это не делать. В моем скромном понимании. Мне очень жаль, что так вот сложился разговор.
Да все в порядке с комментарием. На мою статью можно смотреть и под углом «автор не осилил сборку буста, потому решил писать велосипед», и это нормально, хотя не совсем так.

Другое дело что если складывается впечатление что я призываю не собирать буст, а пользоваться моим, и уж тем более если вдруг кажется что я советую вообще писать свое, то значит что то не так в моей подаче материала. Я могу с уверенностью сказать что:
1. Используйте современные компиляторы и среды разработки с современными стандартными библиотеками.
2. Если п.1 не доступен, то пишите на своем стандарте C++ и не связывайтесь с boost без необходимости.
3. Если п.2 доставляет много боли и хочется все же приключений, то используйте boost.
4. Если п.3 актуален, но boost просто не собирается под вашу задачу, то ищите библиотеки где все велосипеды написаны, и грабли обезврежены для вашей задачи.
5. Если п.4 не выполнен, то смиритесь и возвращайтесь к п.2. Если же всеравно хочется приключений, или задача требует их, то милости просим в мою статью.

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

PS: Это все в последней части в выводах хотел включить, но раз уж развернулась такая дискуссия…
Интересная штука, надо будет посмотреть. Когда искал что-то похожее находил вот что: github.com/PollardBanknote/cppbackport может кому будет интересно еще.
Полезная ссылка, спасибо.
Очень похожая штука, разве что сильно заточена под GCC и его особенности. И, судя по дефайнам, под Windows многое работать не будет на старых компиляторах.
Надо посмотреть, спасибо.

Вот это нагромождение макросов конечно у него в каждой реализации и снова для определения версий компилятора, поддерживаемых «фич» языка и т.п.
Зато реализации можно использовать независимо, а не целым бандлом. Вот, скажем, нужны мне только expected — взял один .h-ник и ни о чём больше не заморачиваюсь. В общем, есть в этом подходе что-то разумное. Особенно если с каким-нибудь folly сравнить, которая для того же expected чёрта лысого тащит.
Да, этот момент мне тоже нравится, у себя в библиотеке тоже старался по максимуму «отвязать» каждый заголовочный файл от других (например не тащить весь type_traits, когда нужен только is_const). Но при реализации конкретных пропозалов это проще, чем при реализации целых стандартных заголовочных файлов конечно.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации