Похоже, мечтам о создании единого бездиалектного C++ не суждено будет сбыться.
Будущее C++ уже не первый год служит поводом для огромного количества разногласий.
Возможно, вы подумаете, что я говорю о профильных тредах на Reddit — местная публика давно славится своей многополярностью и любовью к спорам ради спора, так что разговоры на повышенных тонах там в принципе никогда не утихают. Однако гораздо страннее их видеть даже на официальных заседаниях комитета по стандартизации C++. Ниже я приведу несколько красноречивых примеров.
Текущее положение дел C++
Вот в какой интересной ситуации мы оказались в последнее время:
-
C++’s Evolution Working Group (EWG) недавно выпустила документ P3466 R0 — (Re)affirm design principles for future C++ evolution. Особенно интересно в разрезе статьи следующее:
Принято решение не менять ABI в целях сохранения совместимости с C и прошлыми версиями C++.
Не появится т.н. «паразитных аннотаций» (например, аннотаций времени жизни).
Целый набор взаимоисключающих целей: например, и ABI не поменяется, и zero-overhead principle по-прежнему в строю.
В то же самое время происходят другие, не менее интригующие события:
-
Правительство США выступает против использования языка C++:
Кроме шуток, различные ветви государственной власти США выпускают документ за документом, пропагандируя отказ от повсеместного использования языков с небезопасным доступом к памяти.
-
Представители Big Tech вовсю лоббируют переход к Rust:
Microsoft активно переписывает свои ключевые библиотеки на Rust.
Google также вкладывается в Rust, и фактически начала работу над двунаправленным инструментом взаимодействия C++/Rust.
AWS применяет Rust в своих продуктах.
И так далее, и тому подобное.
К слову о Big Tech, вы заметили, что Герб Саттер покинул Microsoft, а MSVC как-то чересчур неторопливо внедряет функции C++ 23 и просит у сообщества помощи в определении приоритетов.
После печально известного пражского ABI-голосования («В C23 ABI не претерпит изменений, и неясно, случатся ли они вообще») Google значительно сократила свое участие в процессе развития C и вместо этого начала работать над собственным языком-преемником C++. У них даже имеется документ с описанием проблем, с которыми они столкнулись при попытке «улучшить» C++.
В сообществе набирают популярность истории людей, которые в течение многих лет изо всех сил стремились принять участие в работе комитета по стандартизации C++, но их попросту пережевывали и выплевывали.
-
Модули до сих пор не имплементированы [Are we modules yet?](https://arewemodulesyet.org/)
«Профили безопасности» до сих пор находятся в состоянии «ни жива, ни мертва» и не получили какой-либо реализации. Шон Бакстер занял позицию против профилей и назвал C++ «недоспецифицированным».
Не знаю, какие выводы из этой информации сделали вы, но, на мой взгляд, все это напоминает развал C++. Кажется, будто комитет по C++ из последних сил справляется со своими обязанностями. Впрочем, я не хочу, чтобы вы поняли меня превратно. C++ сам по себе никуда не денется – речь идет именно о том, какое развитие он получит в ближайшие годы.
Две разные культуры C++
Всем приходится самостоятельно искать новые решения.
Возьмем для примера уже упомянутую выше компанию Google. Очевидно, Google потеряла веру в развитие C++ после голосования по ABI. Это не потеря веры в сам язык, у Google одна из самых больших кодовых баз на C++ в мире. Этот язык сослужил компании невероятную службу. Скорее, это потеря веры в способность языка развиваться в условиях давления сразу с нескольких сторон (актуальные и потенциальные постановления правительства, наличие языков-конкурентов, желание добиться более высокой производительности, гарантии безопасности от ключевых игроков и т. д.).
Но в чем же корневая проблема? Почему нельзя просто собраться всем миром, взять и…изменить C++ в нужную сторону?
Никакого секрета здесь нет. Просто взгляните, что Герб Саттер писал в своей статье о профилях:
Мы должны свести к минимуму необходимость изменения существующего кода. Что касается внесения изменений в существующий код, то многолетний опыт показывает, что большинство владельцев крупных кодовых баз не захотят и не станут менять даже 1% от общего объема кода для удовлетворения новых правил строгости, даже из соображений безопасности. Разве что их принудят к этому нормативные требования». — Герб Саттер
Круто. Кого-то это удивляет? Меня – ни капельки.
А теперь для контраста ознакомимся с фрагментом биографии Чендлера Каррута, представленной на его страничке на WG21:
Я руководил разработкой инструментария для C++ и систем автоматического рефакторинга, построенных на основе Clang и теперь являющихся частью проекта Clang.
[...]
В Google я возглавил направление, занимавшееся масштабированием автоматизированных инструментов рефакторинга на основе Clang на всю нашу кодовую базу, а это более 100 миллионов строк C++ кода. Теперь мы можем провести анализ, а затем и рефакторинг всей кодовой базы всего за 20 минут.
Ого. Видите это? (Конечно, видите, ведь я специально выделил жирным нужные части текста).
Речь идет про некий «автоматизированный инструментарий». Но за этими словами стоит гораздо больше. Автоматизированный инструментарий – это лишь вершина айсберга, единичный яркий пример.
По сути, сейчас мы наблюдаем конфликт между двумя кардинально отличающимися лагерями пользователей C++:
Относительно современные, компетентные технологические корпорации, которые понимают, что их код – это актив. (Это не обязательно представители big tech. Любой вменяемый стартап, пишущий свежий код на C++, также попадает в эту категорию).
Все остальные. Замшелые конторы, где люди до сих пор спорят о том, как правильно делать отступы, а молодой инженер умоляет руководство разрешить ему установить линтер.
Первая группа сможет адаптироваться к переходу на новые стандарты с наименьшими проблемами, поскольку она будет в состоянии собрать собственный стек C++ из исходников, полученных из репозиториев GitHub-проектов и пакетных менеджеров. (Примечание переводчика: Автор оригинальной статьи использует термин «версионированный источник») А вторая, по-прежнему использующая устаревшие предсобранные библиотеки из 1998 года, будет обречена на страдания.
Умение собрать весь стек зависимостей из исходников нужных версий (желательно еще и с автоматизированными тестами) – это, пожалуй, самое важное отличие между двумя лагерями.
На практике, конечно, чаще встречается нечто среднее. Представляю, сколько пота, слез, денег и крови утекло, чтобы превратить огромные кодовые базы из ужасающих клубков грязи в полууправляемые, собираемые, линкуемые и чуть менее ужасающие клубки грязи.
Теперь, оглядываясь назад, легко рассуждать о неизбежности этих процессов: существовало явное несоответствие между потребностями корпораций вроде Google (которые используют относительно современный C++, имеют автоматизированный инструментарий и тестирование, а также современную инфраструктуру), и огромным интересом всего остального мира к обратной совместимости.
Если говорить на чистоту, то идея единого, свободного от диалектов и унифицированного C++, похоже, умерла уже много лет назад. C++ сейчас существует в мире в двух агрегатных состояниях:
Любой относительно современный C++. Все собирается из исходников подходящей версии посредством специального унифицированного сборщика, который, по крайней мере, немного сложнее, чем голый CMake, и выглядит как вполне адекватно работающий, если слегка прищуриться. Всякие статические анализаторы, форматировщики, линтеры. Любое соглашение о необходимости поддерживать кодовую базу чистой и современной в этом случае бесценно. Возможно, даже C++17, с unique_ptr, constexpr, optional и лямбдами. Но это не самое главное. Важнее всего инструментарий.
Устаревший C++. Все, что отличается от написанного выше. Любой C++, который засел в древних, запыленных серверах какого-нибудь банка. Любой C++, который опирается на кусок непонятно кем и когда скомпилированного кода. Исходники давно утеряны, а авторы наверняка на том свете. Любой C++, развернутый на любительском сервере настолько криво, что его перенос на новое место займет у инженера целый месяц, поскольку ему придется разобраться со всеми неявными зависимостями, конфигурациями и переменными окружения. Любая кодовая база, которая больше напоминает не актив, а черную дыру, засасывающую деньги. Любой код, в котором сборка бинарника из исходного кода требует больше пары нажатий кнопок или вообще невозможна.
Обратите внимание, основное различие заключается не в самом C++. В первую очередь это инструментарий и возможность сборки из исходного доступного через систему контроля версий кода любым чистым, четко определенным способом. В идеале, даже возможность развертывания без необходимости помнить о флагах или переменных окружения, которые обычно устанавливал предыдущий разработчик, чтобы все не рухнуло.
То, насколько кодовая база Google соответствует «современным» стандартам C++, в значительной степени вторично по отношению к тому, насколько хорош ее инструментарий и можно ли собрать проект из исходников без танцев с бубном.
Многие здесь возразят, мол, комитет по стандартизации C++ никак не отвечает за инструментарий, и они будут правы. Это сделано намеренно: комитет в ответе за спецификацию языка C++, а не конкретные реализации.
К слову о Go. Если в Go и есть что-то хорошее, так это инструментарий. C++ по сравнению с ним – это натуральный мамонт. У C++ нет единой системы сборки, ничего даже близко похожего на единую систему управления пакетами, а еще его невероятно сложно разбирать и анализировать (это очень важно с точки зрения инструментария).
Между описанными выше лагерями (хороший инструментарий, можно без усилий собрать всё из исходников, против плохого инструментария, нельзя ничего собрать из исходников) существует огромный, постоянно увеличивающийся разрыв, и я, честно говоря, не думаю, что в ближайшее время он будет ликвидирован.
Комитет по C++, кажется, очень привержен поддержанию обратной совместимости, поэтому и не считается с ценой, которую приходится за нее платить.
Я не могу здесь однозначно принять чью-то сторону. Многим обратная совместимость совершенно необходима, причем по крайне веским причинам. Другим она попросту не нужна. Нельзя говорить, что кто-то из них не прав: это всего лишь разные, взаимоисключающие взгляды на проблему.
Последствия
Именно поэтому вопрос с профилями уже почти два года никуда не движется: их имплементация нужна не для разрешения проблем современных, продвинутых в технологическом плане корпораций, использующих C++. Перед ними стоит совершенно другая задача: привнести в язык улучшения, не требующие переписывать старый код.
То же самое касается и модулей. Предполагается, что можно будет «всего-навсего» импортировать заголовочный файл как модуль, и при этом не возникнет никаких проблем с обратной совместимостью.
Конечно, всем нравятся функции, улучшающие язык, внедрение которых не повлечет за собой необходимость переписывать старый код. Поэтому, очевидно, и разрабатываются они с прицелом на «устаревший» C++. И любая функция, которая потребует перехода с «устаревшего» C++, для комитета C++ не имеет смысла, поскольку, как сказал Герб Саттер, нельзя ожидать, что все просто возьмут и перейдут на новую версию языка.
(Повторюсь, создавать функции с учетом «устаревшего» C++ вовсе не плохо. Это вполне разумное решение.)
Вот, о чем я стараюсь помнить, когда читаю статьи о C++: у него есть две большие целевые аудитории. Одна – современная, другая – традиционная. Эти два лагеря сильно расходятся во мнениях, и многие статьи пишутся с учетом потребностей какой-то одной конкретной группы.
Это порождает в сообществе споры и кривотолки: что бы там кому ни казалось, профили безопасности и Safe C++ пытаются решить совершенно разные проблемы двух разных аудиторий.
Комитет по C++ стремится не допустить увеличения этого раскола. Вероятно, поэтому все, что делает Шон Бакстер в ключе Safe C++, для них не имеет смысла. Это радикальное, масштабное изменение, которое может создать принципиально новый способ работы с C++.
Конечно, здесь может возникнуть другой вопрос: а не являются ли конкретные члены комитета по стандартизации C++ обыкновенными упрямцами, которые хватаются за последнюю соломинку, лишь бы не допустить в языке эволюции, с которой они лично эстетически не согласны.
Не мне судить кого-либо, но я не впервые слышу, что комитет по C++ придерживается двойных стандартов. Вот, например: «Мы ожидаем от вас полной, рабочей реализации в нескольких компиляторах, если вы хотите, чтобы это предложение было одобрено, но при этом с радостью возьмем на себя обязательства по ряду обширных проектов (например, по модулям или профилям), которые до сих поре не имеют работающей реализации».
Если так и есть, то я не знаю, как долго аудитория C++ еще просуществует в относительно целостном виде.
И всё это – при условии, что ABI в ближайшее время не будет подвергаться изменениям.
Комментарии (91)
NeoCode
19.12.2024 15:48Непонятно в чем проблема иметь несколько ABI. Старые, новые, от сторонних компиляторов и языков программирования...
Tim7456
19.12.2024 15:48В том, что существует куча софта который уже не собирается последними компиляторами, либо существует ТОЛЬКО в бинарном виде. Этот софт (суммарно) тянет на триллионы долларов (стоимость замены). Если начнете играть с совместимостью нескольких ABI, то это все придется изобрести заново. И кто-то должен будет за эти работы заплатить. А добровольцев платить что-то не видно.
slonopotamus
19.12.2024 15:48У меня для вас новости! Несколько ABI и так уже есть: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
И в Windows тоже их несколько. Видели вот эти вот msvcrt2005, msvcrt2008, msvcrt2010, etc? Это ни что иное как несовместимые между собой по ABI версии стандартной библиотеки C++: https://learn.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=msvc-170
vadimr
19.12.2024 15:48Вообще не понимаю, зачем кому-либо может понадобиться гипотетический С++ без обратной совместимости. Писать всё заново с нуля на языке идейно во многом 50-летней давности, который даже и в момент своего появления 50 лет назад был далеко не самым продвинутым... чтобы что?
Sun-ami
19.12.2024 15:48Напрашивается вывод, что нужен отдельный язык со всеми современными возможностями C++, но без груза совместимости, которая тянет за собой небезопасность из-за многочисленных вариантов неопределённого поведения.
vadimr
19.12.2024 15:48А какие полезные возможности есть в C++ по сравнению с другими языками, кроме кодовой базы?
Если по-честному, то C++ сам по себе - это гибрид недосмолтока 90-х с недохаскелем 2000-х, примотанный изолентой поверх Си.
Sun-ami
19.12.2024 15:48Универсальность, которая позволяет совмещать высокоуровневые и низкоуровневые подходы в рамках одной программы. Другими словами, писать большие программы, и максимально оптимизировать их там, где это требуется, причём принимать решение о необходимости оптимизации в любой момент, и не слишком сложно выполнять его.
KivApple
19.12.2024 15:48В C++ в отличии от того же Rust есть, например, ООП с наследованием и т. п. Есть varidadic templates.
Многие решения C++ не очень элегантны (или даже очень уродливы), но он умеет буквально всё всеми способами (кроме разве что рефлексии, но и это уже добавляют) и при этом является компилируемым языком с ручным управлением памятью (что важно для задач, где требуется производительность).
Его конкуренты в основном предлагают гораздо более opionated решения типа "вы обязаны выбирать композицию вместо наследования".
vadimr
19.12.2024 15:48Хотелось бы отметить, что слухи о влиянии ручного управления памятью на производительность сильно преувеличены. Я как раз недавно в статье приводил пример, когда в очень неудобной экпоненциально сложной задаче происходил перебор огромнейшего количества динамически создаваемых и разрушаемых списков (в несколько раз больше объёма оперативной памяти). И вот в такой задаче автоматическая сборка мусора заняла всего 50% времени выполнения. Это по сути вообще ни о чём, на реализацию ООП в C++ уходит гораздо больше оверхеда (по сравнению с С).
На мой взгляд, ручное управление памятью в наше время (т.е. при том обычном соотношении ресурсов процессора и памяти, которое есть сейчас, и при современном состоянии алгоритмов сборки мусора) – это либо очень узкая область низкоуровневых задач (но даже там лучше память выделять вообще в статические массивы), либо лютый бабушкин анахронизм.
Никто, за редчайшими исключениями, не пишет на ассемблере, чтобы ускорить свою программу в 2 раза, хотя это вполне реально. Но мусор почему-то многие собирают руками. Парадокс.
Плохая репутация автоматической сборки мусора сложилась во времена машин с очень ограниченной оперативной памятью, когда каждый байт был на счету, и мусор приходилось собирать очень часто. А теперь действует инерция.
KReal
19.12.2024 15:48А можно ссылку на статью? Как-то прошла мимо меня..
vadimr
19.12.2024 15:48https://habr.com/ru/articles/859758/
В разделе недетерминированного программирования. Правда, я это не сразу туда дописал, так что если Вы прочли статью в первой редакции, то этого материала просто не было.
feelamee
19.12.2024 15:48Но мусор почему-то многие собирают руками
не понял о C++ ли вы, но я ОЧЕНЬ редко пишу free/delete.
Фактически мусор собирает компилятор благодаря raii
eao197
19.12.2024 15:48И вот в такой задаче автоматическая сборка мусора заняла всего 50% времени выполнения. Это по сути вообще ни о чём
Наверное, я чего-то не понимаю в программировании, но когда половина времени уходит не на полезную работу, а на менеджмент памяти, то в этом нет ничего хорошего. А когда это еще и характеризуют как "по сути ни о чём", то такое ощущение, что не понимаю я вообще ничего.
это либо очень узкая область низкоуровневых задач (но даже там лучше память выделять вообще в статические массивы), либо лютый бабушкин анахронизм.
Либо работа с большими объемами данных, где экономия 4-х байт на каждый объект приводит к снижению потребления памяти на сотни мегабайт. А с учетом того, что быстрые кэши маленькие, а загрузка в них данных из ОП медленная, то экономия на потребляемых объемах как-то сама собой трансформируется в выигрыш по скорости.
vadimr
19.12.2024 15:48Надо ж, какой пафос! Я б ещё, может, и мог с огромным усилием поверить, что в ваших программах негде сэкономить 4 байта, кроме как на счётчике ссылок, и вот именно он разрушил бы всё оптимальное размещение в памяти. Но уж никак не в объектно-ориентированном языке C++ с повсеместными таблицами виртуальных методов, длинами векторов и прочей шнягой поддержки выполнения. С Фортраном 77 такая аргументация прошла бы, а тут вряд ли.
Наверное, я чего-то не понимаю в программировании, но когда половина времени уходит не на полезную работу, а на менеджмент памяти, то в этом нет ничего хорошего. А когда это еще и характеризуют как "по сути ни о чём", то такое ощущение, что не понимаю я вообще ничего.
Ну ничего не могу поделать. Для большинства программ мне действительно совершенно неважно, будут или их действия выполняться за одну или за две миллисекунды. А вот время, затраченное на кодирование и отладку, и надёжность программы могут представлять интерес.
eao197
19.12.2024 15:48Надо ж, какой пафос!
Вы в моих словах нашли пафос? Перечитайте, пожалуйста.
Я б ещё, может, и мог с огромным усилием поверить, что в ваших программах негде сэкономить 4 байта, кроме как на счётчике ссылок, и вот именно он разрушил бы всё оптимальное размещение в памяти.
С чего вы взяли, что речь идет о счетчиках ссылок?
Ручное управление памятью (по крайней мере в моем мире) -- это не только ручной вызов new/delete (или malloc/calloc/free), и даже не деление на стек/хип. Это еще и использование union-ов, пулы/арены памяти и placement new. А так же агрегирование данных, когда вы объект класса A можете сделать полем класса B и память для A будет частью памяти для B (тогда как в языках с GC если у вас A -- это ссылочный тип, то частью другого ссылочного типа B вы его не сделаете).
Но уж никак не в объектно-ориентированном языке C++ с повсеместными таблицами виртуальных методов, длинами векторов и прочей шнягой поддержки выполнения.
Зная как вы не можете в обоснование собственных утвержений я даже и не стал заострять внимание вот на этом: "на реализацию ООП в C++ уходит гораздо больше оверхеда (по сравнению с С)". Но раз вы сами завели эту шарманку, то может поделитесь цифрами о том, насколько рукопашная реализация ООП на чистом Си выгоднее, чем поддерживаемая компилятором и рантаймом в С++?
vadimr
19.12.2024 15:48А я разве говорил про рукопашную организацию ООП? Я имею в виду чисто процедурный стиль.
eao197
19.12.2024 15:48А я разве говорил про рукопашную организацию ООП?
Вы сказали вот что:
"Это по сути вообще ни о чём, на реализацию ООП в C++ уходит гораздо больше оверхеда (по сравнению с С)."
Это сложно воспринять как-то иначе как накладные расходы на ООП в C++. А так как в чистом Си ООП нет, то когда в Си нам придется использовать ООП, то мы будет делать это вручную и обойдется нам это дешевле, чем в C++.
Я имею в виду чисто процедурный стиль.
Тогда при чем здесь ООП в C++? На C++ вы точно так же (даже надежнее и безопаснее) сможете использовать процедурный стиль, как и в Си.
Или вы в C++ видите какие-то дополнительные издержки даже на процедурный стиль в сравнении с Си?
vadimr
19.12.2024 15:48Если в C++ писать в стиле C, то зачем тогда C++?
А если писать в стиле C++, то получается оверхед. Я не против оверхеда самого по себе, но не надо тогда говорить о великой эффективности.
eao197
19.12.2024 15:48Если в C++ писать в стиле C, то зачем тогда C++?
Я не говорил о том, чтобы писать на C++ в стиле Си. Читайте внимательнее, пожалуйста.
Речь шла о том, что в C++ вы запросто можете программировать в процедурном стиле, как и на Си. Только делать это будет удобнее и надежнее. За счет, например, наличия ad-hoc полиморфизма, ссылок и отсутствия неявных преобразований из void*.
vadimr
19.12.2024 15:48Против отсутствия неявных преобразований из void* я не возражаю. Но для аргументации в пользу C++ в целом этого маловато.
vadimr
19.12.2024 15:48Разве в питоне или джаве нельзя сделать агрегированный тип?
eao197
19.12.2024 15:48А вы понимаете разницу между ссылочными типами (reference types) и типами-значениями (value types) в языках с GC?
Если у вас в Java тип A -- это reference type, т.е.:
public class A {...}
то когда вы делаете член типа A в классе B (который так же reference type):
public class B { private A a; ... }
то в B у вас хранится только ссылка на экземпляр A. И вы не можете повлиять на накладные расходы, связанные с хранением самого A и представлением ссылки на A внутри B.
vadimr
19.12.2024 15:48На накладные расходы, связанные с хранением самого A, вы в любом случае не можете повлиять, агрегируя A в другой класс.
А где у вас хранится в памяти тело агрегированного объекта относительно тела агрегирующего - это просто деталь реализации. В C++ всё равно так и так любой объект должен иметь возможность представляться ссылкой, так что сложно представить, как это различие можно практически эксплуатировать без ub. Сэкономить один указатель?
eao197
19.12.2024 15:48На накладные расходы, связанные с хранением самого A, вы в любом случае не можете повлиять, агрегируя A в другой класс.
Правда? А если смогу?
А где у вас хранится в памяти тело агрегированного объекта относительно тела агрегирующего - это просто деталь реализации.
Ну да, ну да. Абсолютно несущественная, особенно если брать в расчет cache locality.
В C++ всё равно так и так любой объект должен иметь возможность представляться ссылкой, так что сложно представить, как это различие можно практически эксплуатировать без ub.
Простите не распарсил.
А можно встречный вопрос: вы вообще на C++ программировали?
vadimr
19.12.2024 15:48Ну да, ну да. Абсолютно несущественная, особенно если брать в расчет cache locality.
Вы так уверены, что вам всегда будут одновременно нужны для обработки все поля агрегирующего объекта, в том числе и агрегируемый? Может, наоборот, вы забьёте кеш лишними данными?
А можно встречный вопрос: вы вообще на C++ программировали?
Нет, что вы, так рассуждаю. Но ваша правда в том, что погружаться в это тянет редко.
Простите не распарсил.
Так как любой объект в C++ (как и в других ООП языках) представляется таким образом, чтобы могла существовать самостоятельная ссылка на него, даже если он агрегирован в другой объект, то остаётся только простое его перемещение в памяти внутрь и наружу тела агрегирующего объекта, чем вы ничего существенного не достигнете.
Даже если предположить, что локальность в памяти агрегируемого объекта относительно агрегирующего что-то позволит выиграть в плане оптимизации (что ниоткуда само по себе не следует), то никто не мешает компилятору разместить в памяти вынесенный агрегированный объект сразу до или после агрегирующего. Фактически это скорее всего и произойдёт, так как они размещаются одновременно. Разница будет в один указатель.
Medeyko
19.12.2024 15:48А не вписываются ли Rust и/или Carbon в то, что Вы сформулировали?
Rust уже получил определённое распространение, о чём говорится и в обсуждаемой статье. Но достаточно заметно отличается от C++.
Carbon ещё в процессе начальной разработки, minimum viable product версия 0.1 ожидается в 2025 году, а production ready 1.0 - в 2027. Но зато создаётся под эгидой упомянутого в обсуждаемой статье Чендлера Каррута в Google, с сохранением двусторонней совместимости с C++ (за счёт автоматической конвертации; по синтаксису, на мой взгляд, больше выглядит похожим на Rust, чем на C++). Тоже упомянут в обсуждаемой статье.
Эти языки не являются решениями, соответствующими сделанному Вами выводу?
NeoCode
19.12.2024 15:48Сейчас все новые языки будут "похожими на Rust" - как я понимаю, в целях упрощения парсинга и исключения неоднозначностей фундаментально отказались от "сишного" стиля объявления переменных и функций.
fen-sei
19.12.2024 15:48Полноценной поддержки ООП в Расте, увы, нет. :-(
(про Карбон не знаю, а в Гоу кастрация под корень)
feelamee
19.12.2024 15:48все кроме наследования, которое итак почти является анти-паттерном и во многих местах лучше использовать композицию/агрегацию.
Вполне полноценное ООП как по мне
vadimr
19.12.2024 15:48Наследования в расте нет.
Полиморфизма полноценного нет ни в расте, ни в C++, потому что он требует динамической типизации.
ООП = struct ?
Тогда самый ООП язык – это Кобол, там инкапсуляции больше всего.
slonopotamus
19.12.2024 15:48Полиморфизма полноценного нет ни в расте, ни в C++, потому что он требует динамической типизации.
Шаблоны/дженерики - статический полиморфизм. dyn trait/virtual - динамический полиморфизм. Чем это неполноценно?
vadimr
19.12.2024 15:48Вообще ООП и его терминология пошли из Смоллтока. Что такое полиморфизм с точки зрения Смоллтока? Это такое положение дел, когда объект априорно ничего не знает о типе другого объекта, с которым он взаимодействует.
Например, условно, я определяю функцию half(x) как x/2. И при этом мне нафиг не нужно знать, в чём состоит смысл и реализация операции деления (а также двойки) и какой тип у переменной x. Потом когда-нибудь кто-то реализует, скажем, арифметику рациональных чисел со своим делением, а моя функция half всё так же будет работать с этими рациональными числами. И для этого даже перекомпиляция half будет не нужна, не говоря уж об априорном знании типа аргумента.
eao197
19.12.2024 15:48Вообще ООП и его терминология пошли из Смоллтока.
Конкретно в C++ ООП и его терминология пришли из Simula 67, который появился за несколько лет до начала работ над SmallTalk-ом.
vadimr
19.12.2024 15:48Синтаксис объектно-ориентированных конструкций в С++ действительно имеет много общего с Симулой 67. Однако сам термин ООП и основные принципы ООП были придуманы Аланом Кеем при работе над Смоллтоком. Симула 67 была фактически зачислена в объектно-ориентированные языки задним числом, но никто там про тройку инкапсуляция-наследование-полиморфизм не думал.
eao197
19.12.2024 15:48Ну да, ну да. ООП было, а слова такого не было, ага.
То, что Алан Кей хорош в самовосхвалении не означает, что ООП -- это только то, что вышло из SmallTalk и имеет отношение к SmallTalk.
Но даже это не важно. Вы сослались на SmallTalk, но так и не объяснили почему для полиморфизма нужна динамическая типизация.
И вряд ли это у вас получится, т.к. полиморфизм бывает разный. И, сюрприз, C++ поддерживает несколько их видов. Можно выбирать на вкус.
KivApple
19.12.2024 15:48На практике антипаттерн наследование или нет зависит от предметной области и задачи.
C++ даёт выбор. Rust не даёт.
Sun-ami
19.12.2024 15:48Rust не поддерживает ООП так, как С++. С Carbon ситуация вроде бы лучше, но у него синтаксис довольно существенно отличается от С++. Нужен такой язык, перенос на который кода с C++, в котором применяются только актуальные практики программирования, не потребует вообще никаких правок кода. То есть это должен быть не совсем новый язык, а просто более строгая версия C++, ограниченно-совместимая с просто С++. Какой-то Safe C++.
slonopotamus
19.12.2024 15:48Если в Go и есть что-то хорошее, так это инструментарий.
Я аж кофе подавился. Нет, в Go плохой инструментарий.
feelamee
19.12.2024 15:48будьте осторожны с кофе.
Но не забывайте аргументировать свои утверждения
slonopotamus
19.12.2024 15:48Ну неплохо бы сначала увидеть аргументацию изначального утверждения.
feelamee
19.12.2024 15:48далее автор и описал, чем он руководствовался когда говорил хороший:
К слову о Go. Если в Go и есть что-то хорошее, так это инструментарий. C++ по сравнению с ним – это натуральный мамонт. У C++ нет единой системы сборки, ничего даже близко похожего на единую систему управления пакетами, а еще его невероятно сложно разбирать и анализировать (это очень важно с точки зрения инструментария).
Не могу сказать что имелось ввиду хороший объективно (хотя все познается в сравнении), но как минимум хороший на фоне C++
ForestDront
19.12.2024 15:48В го плохо то, что это гуглёвское изделие. В любой момент его может запретить на территории РФ либо власть гугля, либо местная власть.
ClayRing
19.12.2024 15:48Как вы себе представляете исполнение запрета гугла на использование го на территории РФ?
JBFW
19.12.2024 15:48я представляю, но не напишу, а то кто-то подумает что ему инструкцию подогнали...
ForestDront
19.12.2024 15:48У Go свой рантайм. Гугель туда мог напихать каких угодно закладок. А это ж гугель. Неужели он удержался?
feelamee
19.12.2024 15:48ответ на ваш вопрос лежит прямо в https://github.com/golang/go
По прочтении - расскажите. Наверняка всем будет интересно узнать каких закладок напихал туда гугл
AbitLogic
19.12.2024 15:48Я потратив неделю переписал небольшой проект с Си (stm32f7) на rust с их hal, если кто смотрел HAL от stm это просто ужас, hal у Rust оказался очень простым и прозрачным, да и код проекта вышел крайне лаконичным, без этого callcxpt и прочих loopback, в итоге бинарник вместо 27Кб весит 1.4Кб, вот даже в смятение, а не пора бы прекратить мучения и в больших проектах и перейти на Rust, останавливает только слабое знание так называемой "идиоматики" Rust, всё время срываюсь и вместо функционального стиля начинаешь вложенные циклы шпарить
Sun-ami
19.12.2024 15:48Монструозность и неоптимальность - это свойства STM32 HAL, к языку C они почти не имеют отношения, а на C++ HAL может быть вообще красивым и при этом очень оптимальным, примеры этого для STM32 есть.
AbitLogic
19.12.2024 15:48Да понятно, что если мне cargo выплюнул набор инструкций их можно повторить на Си, другой вопрос какой ценой, самому лупить деструктуры, следить за памятью, замыкания, контейнеры? Тут всё инклюзив, больше думаешь над алгоритмом, а не как это всё разместить и передать... Если где-то ошибся - компилятор тебя по рукам ударит, а не выискивать потом откуда UB, откуда nullptr, почему Segmentation fault, оно же работало 5 суток подряд
По поводу HAL на C++ можно предложить достойный вариант? Мне не попадался как-то, а может и не искал, потому что и мысли не было что такое бывает, я пытался на голом STL что-то делать, он мне тупо память нафграментировал через какое-то время, не смог больше выделить кусок и я забил писать на С++ в STM
Sun-ami
19.12.2024 15:48Конкретно для stm32f7 не подскажу - то, что мне встречалось в свободном доступе, поддерживало в основном более старые контроллеры. Вообще, таких библиотек много, но все они не настолько универсальны как HAL от ST в плане поддержки всго многообразия их контроллеров. Они просто очень часто выпускают новые семейства, универсальным библиотекам угнаться за этим можно только если начинать ещё до начала их массового выпуска.
Panzerschrek
19.12.2024 15:48Насколько я понимаю, желание сохранять ABI совместимость в ущерб развитию языка проистекает из практик некоторых популярных GNU/Linux дистрибутивов. Они так устроены, что там почти весь более-менее доступный пользовательский софт с открытыми исходниками собирается разработчиками системы и складывается в централизованный репозиторий. При этом для экономии места разработчики пытаются переиспользовать общие библиотеки-зависимости, чтобы несколько приложений грузили одну и ту же разделяемую библиотеку. Соответственно необходимо, чтобы ABI там совместимым был. Это касается в том числе и C++ библиотек. Но самая засада состоит в том, что в таком подходе необходима ещё и обратная совместимость компилятора, которым на пользовательской машине может собираться какой-то частный/проприетарный софт с библиотеками, которые установлены в системе (в том числе C++ библиотеками). Отсюда и требование совместимости ABI, вытекающие иногда в проблемы, что какой-нибудь std::unordered_map теперь не оптимизировать, т. к. это сломает ABI.
Решением этих пробелем мне видится изменение модели функционирования подобных дистрибутивов GNU/Linux - ближе к модели Windows, когда каждое приложение тянет с собою все зависимости и эти зависимости разработчик может собрать сам любой удобной ему версией компилятора. Стабильный ABI в таком подходе нужен только от базовых системных библиотек с голым Си интерфейсом - вроде glibc или kernel32.dll. Сборка всех зависимостей из исходников, кстати, это то, что в этой статье хвалится. Пример того, как этот подход работает, это язык Rust. Там ABI не стабилизирован и все зависимости собираются с приложением, что местами конечно не быстро, но в целом того стоит.
vadimr
19.12.2024 15:48В расте это работает, потому что компилятор один и библиотек мало.
Собрать сложное C++ приложение с внешними зависимостями из исходников – очень нетривиальная задача, и иногда и неразрешимая. О чём, кстати, пишет автор статьи.
Panzerschrek
19.12.2024 15:48Собирать всё из исходников - единственно-рабочий на долгую перспективе путь. Сам лично работал над подобными проектами, кода и внутренняя кодовая база и внешние зависимости достаточно внушительными были. Но да, подобных подход требует изначальных инвестиций времени, чтобы сборку по-грамотному организовать.
vadimr
19.12.2024 15:48В долгой перспективе часть исходников рано или поздно будет тем или иным образом утрачена. Или просто файлы потеряются, или актуальные версии сторонних библиотек разойдутся до несовместимости.
А кроме того, рано или поздно может встать задача подцепить ваши данные новой программой на другом языке.
Panzerschrek
19.12.2024 15:48Чтобы исходники потерялись надо проявить уж очень выдающиеся навыки безответственности/халатности. Обычно исходники все под контролем git или аналогов, включая централизованный репозиторий и копию у каждого разработчика. Так что такой случай можно исключить и развивать C++ дальше, жертвуя бинарной совместимостью. К тому же, если кто-то умудрился исходники потерять, то и бинарники он тоже может с той же лёгкостью утратить.
Расхождение версий библиотек при сборке их из исходников это как раз не проблема. Можно не обновлять библиотеки, если не надо, а если они обновляются, то и при необходимости пропатчить их можно. А вот при зависимости от библиотек, установленных в систему, проблема как раз и проявляется, вроде приложение на новой ОС падает, потому, что установленная там libsomestuff.so не той версии.
Чтение данных другой программой - это сколько угодно можно, если форматы стабильны, в т. ч. бинарные. Но упомянутая в статье стабильность ABI к этому отношения не имеет.vadimr
19.12.2024 15:48Стабильность ABI – это в том числе и внутреннее представление объектов.
Panzerschrek
19.12.2024 15:48Вот именно - объектов. Вроде всяких там std::unordered_map. Но в файлы их никто не пишет, их сериализуют или в текстовом формате, или в бинарном, состоящих из простых структур. А на бинарное представление структур никто не собирается покушаться.
Вообще, стандартом ни размер не внутреннее представления большинства классов стандартной библиотеки не гарантируется, как раз для того, чтобы был простор для оптимизации. Но случился ад зависимостей, как я написал ранее, и стало страшно это внутреннее представление менять.
С пользовательскими структурами же другая история. Там расположение в памяти жёстко задано и если структура грамотно составлена, то ничего сломаться не может.vadimr
19.12.2024 15:48Но в файлы их никто не пишет, их сериализуют или в текстовом формате, или в бинарном, состоящих из простых структур.
“Может, бросить всё и уехать в Урюпинск?”
Вот прямо сейчас вторую неделю сижу над расшифровкой hex-дампов сетевых пакетов одной унаследованной программы на C++. Какой-то мусор там находится в некоторых местах в векторах, не могу понять принцип его расположения.
Panzerschrek
19.12.2024 15:48Видимо да, в конкретно вашем случае кто-то поступил не хорошо, сериализуя не так, как это было бы правильно. Но я всё же не считаю правильным тормозить развитие языка из-за того, что кто-то много лет назад какую-то программу не очень хорошим образом написал.
vadimr
19.12.2024 15:48Так эти кривые программы и являются основным активом языка С++, хочется нам этого или нет.
e_u_g
19.12.2024 15:48Вброшу...
На мой взгляд, ещё одна существенная проблема С/С++ это синтаксис, преисполненный разными символами. Все эти *, &, ->, >>, ! и т.д.
Доводилось читать код человека, обожавшего перегружать операторы - ужасно.
Автор определяет два вида компаний, пишущих на с++. Интересно было бы узнать пропорцию. Моя догадка, что количество "молодых модных стартапов" сильно меньше "замшелых ретроградных контор". И, возможно, причина этого именно в нежелании условной молодёжи разбираться во всех оных закорючках. А было бы наоборот, первое лобби продавило бы новые стандарты и инструменты, несмотря на сопротивление второго.П.С.
А вот бы сделали нативный компилятор питона.
commanderxo
Обсуждали уже, две недели назад. Впрочем, ваша версия перевода мне нравится больше. Спасибо.