>
>
>
Текстовая трансляция CppСast 300: Стаби…

CppCast
Статей: 9

Текстовая трансляция CppСast 300: Стабильность ABI

Эпизод 300 CppCast с гостем Маршаллом Клоу, записанный 18 мая 2021. В этом эпизоде Роб Ирвинг и Джейсон Тернер беседуют с Маршаллом Клоу о стабильности ABI. Также они поговорят о ASAN и нескольких полезных библиотеках для C++.

Введение

Роб: Добро пожаловать на трехсотый выпуск CppCast! Наш гость сегодня — Маршалл Клоу. Маршалл – давний участник проектов LLVM и Boost. До прошлого года он являлся владельцем кода для libc++, реализации стандартной библиотеки LLVM. Маршалл также был председателем Library Working Group в комитете по стандартизации C++. Он автор библиотеки алгоритмов Boost, а также занимается поддержкой нескольких других библиотек из Boost.

Джейсон: Учитывая, что вы долгое время были совладельцем libc++, мне интересно, когда вы начали работать над libc++?

Маршалл: Примерно в 2012. Основным автором в то время был Говард Хиннант, который работал в Apple. Говард уже давно занимался стандартной библиотекой C++. Он также был председателем комитета по стандартизации, когда вышел C++11; он же – автор move семантики в C++. Затем Говард покинул Apple и перешел в Ripple, и у него больше не было времени заниматься libc++. И тут активизировался я и руководил проектом в течение пяти-шести лет. Говард все еще время от времени появляется. Правок не вносит, но интересуется: "Почему что-то сделано таким образом?". Потом я немного выгорел к моменту выхода С++20 и решил передать часть ответственности.

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

Поиск багов с ASAN

Роб: Начнём с заметки в блоге о Visual Studio C++. Называется "Поиск багов с AddressSanitizer: паттерны из проектов с открытым исходным кодом". Мы уже разговаривали о том, что Microsoft теперь использует ASAN от Clang'а в качестве встроенного инструмента анализа в Visual C++. Они проверили им несколько библиотек с открытым исходным кодом и нашли там несколько багов. После чего показали их разработчикам тех библиотек, и, кажется, сейчас их все исправили.

Маршалл: Я правда рад, что у Microsoft теперь это есть. Крутая штука. Правда, я немного удивлен, что они говорят так, будто это появилось только в 2021. Я поискал немного и нашел свой пост в блоге, где описывал запуск набора тестов из libc++ с использованием ASAN для поиска ошибок в самом ASAN. Это было в марте 2013.

Джейсон: Мне становится немного грустно от таких статей про найденные ошибки в проектах с открытым исходным кодом. Я такой: "Погоди... То есть вы хотите сказать, что OpenSSL не запускает полный тестовый набор с включенным AddressSanitizer? Это же проблема уровня конца света! А если в OpenSSL есть проблемы с безопасностью, о которых пока никто не знает, кроме маленькой группы хакеров?"

Маршалл: Легко впечатляемый читатель?

Джейсон: Ага. Справедливости ради скажу, что баг был всего лишь в каком-то тесте, но эй... их все равно нужно ловить.

Маршалл: Да. А когда я впервые запустил ASAN, я обнаружил пару ошибок в тестовом наборе libc++. Там попался один реально крупный баг где-то в недрах iostream. Суть в том, что запускался поток (stream, не thread), с буфером нулевого размера на куче. Затем в него записывался один байт, после чего этот буфер расширялся, и никто не замечал проблем. Потому что на macOS размер выделяемой памяти округляется до 16, даже если был указан 0. Такое поведение некорректно, даже если на macOS все было в порядке. А если бы вы перенесли код на другую операционную систему с другим аллокатором, у него не было бы такого поведения. Вы могли бы получить баги или некорректное поведение. И я был весьма впечатлен результатом, потому что это было что-то вроде: "Вау, я никогда бы не нашел этот баг сам".

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

Маршалл: Хорошая новость в том, что в то время запуск набора тестов libc++ занимал около 20 минут. Когда я запустил ASAN, проверка заняла полтора часа. Очевидно, что он что-то делает. И тем лучше, что Microsoft адаптировали эту технологию под свой компилятор, так как она стала доступна людям, которые предпочитают только его. А ложных срабатываний там просто не может существовать, исходя из принципов работы ASAN. Если ASAN находит баг, то это гарантированно ошибка в коде.

Обновление RmlUI

Роб: Следующая новость — обновление RmlUI. Не думаю, что мы раньше это обсуждали. Это библиотека языка C++ для создания пользовательского интерфейса из HTML+CSS.

Джейсон: До меня только спустя пару минут дошло: "Минуточку... Зачем мне делать HTML из C++? Что это?"

Роб: Это их собственный набор для разработки пользовательского интерфейса. На GitHub есть несколько небольших примеров. Похоже, это в основном нацелено на видеоигры.

Джейсон: Если вам нравятся HTML и CSS, то вы можете использовать его как свой язык описания интерфейсов. Точнее, язык разметки для интерфейсов.

Роб: Похоже, что здесь можно легко связывать данные между HTML-кодом и C++. Выглядит довольно мощно.

Джейсон: Ух ты, можно использовать Sprite sheet. С ума сойти.

Маршалл: Я никогда подобным не пользовался, поэтому не могу с чем-то сравнить. Но возможность связывать данные выглядит довольно интересно. Хотя что-то вроде Model View Controller трудно сделать правильно и точно. Просто сделать его "почти" правильно.

Not Enough Standards

Роб: Поговорим о другой библиотеке - "Not enough standards". Это набор небольших вспомогательных header-only библиотек для С++ 17 и 20. Здесь есть пара классных вещей. Например, для управления процессами и загрузки динамических библиотек.

Джейсон: Этот инструмент для запуска процессов и правда привлек мое внимание. Иметь возможность очень легко и кроссплатформенно взять и запустить что-то. И я знаю, что есть Boost::Process и QProcess, но, для проекта, над которым я сейчас работаю, я не хочу, чтобы там был boost или QT, это чересчур. Что еще смешнее, на прошлой неделе я гуглил другие библиотеки процессов, зная, что они существуют, ноне смог найти ни одну, пока не наткнулся на эту.

Маршалл: Да. Ты сказал довольно распространенную вещь: "Я не хочу тянуть Boost - он огромный". Но на самом деле Boost — это большая коллекция библиотек. Не каждая библиотека в нем большая сама по себе. Есть мелкие библиотеки, не обязательно зависящие от всего остального, кроме пары общих библиотек, вроде конфигурационной библиотеки. В принципе, можно пользоваться лишь малыми частями функционала, не тягая весь остальной Boost.

Meeting C++ 2021

Роб: И последнее на сегодня - анонс Meeting C++ 2021. Он будет проходить онлайн с 10 по 12 ноября.

Джейсон: Тут нужно уточнить, что NDC TechTown, который пройдет официально в октябре, планируется оффлайн-конференцией. От CppCon пока не было никаких новостей. Хотя на их сайте висит объявление, что CppCon будет в октябре виртуальной и оффлайн-конференцией.

Роб: Думаю, мы еще услышим подобные объявления. У меня есть ощущение, что в этом году произойдет много виртуальных и оффлайн-конференций.

Стабильность ABI

Роб: Итак, Маршалл. В последних эпизодах мы уже обсуждали C++ ABI. Но каждый раз, когда я захожу на Reddit или куда-то еще, я все еще вижу комментарии от людей, которые не знают, что такое ABI. Поэтому прежде, чем начать обсуждение, объяснить, что такое C++ ABI и почему это важно?

Маршалл: Начнем с того, что ABI означает двоичный интерфейс приложения. И это способ определения того, как передаются параметры, как возвращаются значения, как распределяются данные в структурах, как обрабатываются исключения и так далее. Или системные вызовы платформы. У Windows есть ABI. У Linux есть ABI. У macOS есть ABI. У Android, iOS есть ABI. Например, для Linux и macOS есть очень хороший документ, Itanium ABI Specification. Можете погуглить. Но это не то, о чем хотят поговорить люди, когда они говорят о стандартных библиотеках и как работает ABI.

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

Я говорил об этом в марте на конференции CppCon. И я потратил больше получаса, рассказывая о One Definition Rule в C++. Для тех, кто незнаком с правилом одного определения: по сути, C++ говорит, если есть два разных, отличающихся друг от друга определения одного и того же типа, класса или структуры, и в вашей программе есть какое-то место, где вы можете видеть их оба... то это обозначается восхитительной аббревиатурой IFNDR (ill-formed, no diagnostic required). Очень плохая ситуация. IFNDR значит, что вашей цепочке инструментов разрешено сделать исполняемый файл, который может делать все, что угодно. Когда вы запускаете программу, прямо во время запуска получается неопределенное поведение.

Джейсон: И ваш инструмент не выдает предупреждения или обратной связи.

Маршалл: И этому есть причина. Рассмотрим такие примеры:

Предположим, у вас определены две структуры с одинаковыми именами. Разные layout-ы. У одного есть три поля. У другого – два. Они в одной единице трансляции, так? Компилятор это заметит и выдаст предупреждение или ошибку. И большинство компиляторов так и сделают.

Второй сценарий. Есть разные единицы трансляции, и компоновщик объединяет их. Компоновщик создает исполняемый файл из этих двух объектных. Он мог бы теоретически предупреждать о подобных проблемах, если бы у него была нужная информация в объектных файлах, но её нет.

Третий сценарий. У нас есть две разные единицы трансляции. Одна связывается с исполняемым файлом, другая связана с динамической библиотекой. Они отличаются и передаются туда-сюда. Ни ваш компоновщик, ни компилятор библиотеки никак не смогли бы сообщить вам об этом. Потому что программа, по сути, не собрана до тех пор, пока вы ее не запустите. С этим приходится разбираться загрузчику, а никакой информации о символах уже нет. Вот почему здесь IFNDR – нет ни одного места, где вы бы могли это уловить. Потому что программы как таковой не существует в наши дни динамических библиотек или, там, плагинов.

Можно привести несколько примеров нарушения ODR. Некоторые довольно очевидны. Скажем, есть две структуры с полями: у одной first и second, а у другой second и first. Они разных типов. Если передавать их туда-сюда, что произойдет? В одном месте код получит доступ к полю first, и будет считать, что оно находится в структуре по смещению 6, размером 4 байта. А потом *бам*, получается смещение 0, размером 5 байт. Постоянно получается путаница. А если они разных размеров, или есть их массивы или векторы? Пробуем получить элементы из вектора, а они оказываются не на своих местах. И множество других ситуаций при перестановке полей, и т. д. И нет возможности это отладить.

А около года назад на последней оффлайн встрече комитета по стандартизации в Праге возникли предложения поменять ABI. Какие-то более значительные, какие-то менее. Не поймите неправильно, все улучшения хороши. Некоторые могут значительно увеличить производительность некоторых типов программ.

В общем множество улучшений больших и малых, которые хотелось бы сделать, но. Не хочется при этом сломать все приложения на C++ в мире? Окей, на самом деле некоторые хотят... Я видел людей, которые заявляют: "Пользователи, которые не хотят пересобирать свои программы каждые 3 года, препятствуют развитию сообщества!". Я обычно на такое отвечаю, что у всех свои дедлайны и временные рамки. Вот моя дочь пользуется некоторыми приложениями, сделанными на C++... Если ей сказать, что нужно их пересобрать, она только скажет: "Что? Пересобрать? А я их и не собирала". То есть такие предложения могут быть жизнеспособными только для людей, которые сами все собирают из исходников. Для них стабильность ABI не имеет такой ценности.

Джейсон: Можно ли уточнить? Допустим, если бы в C++20 полностью уничтожили совместимость ABI, как бы это задело приложения вашей дочери? Все двоичные файлы и библиотеки уже установлены в её системе, и все стабильно работает. В чем проблема?

Маршалл: А что произойдет, когда придет следующее обновление от Apple с новой версией стандартной библиотеки?

Джейсон: Ну в смысле, у меня и так сейчас подобных 18 штук устанавливается вместе с Visual Studio.

Маршалл: Да. А у Apple одна...

Джейсон: То есть у них нет возможности управления версиями системной библиотеки?

Маршалл: Нет, они решили так не делать.

Приведу конкретный пример о libc++, потому что она мне более знакома. У неё внутри есть два разных типа basic_string, не совместимые по ABI. Из-за того, что некоторые люди, включая Apple, выпускали (релизили) libc++ в течение несколько лет, в Google рассчитали, что, если изменить размещение строк в памяти, можно получить преимущество в выравнивании кэша. Причем при необходимости обрабатывать большое количество строк преимущество становится существенным. При обработке JavaScript в Chrome они получали прирост 2%, и для них это большой выигрыш. Так что теперь в libc++ два типа строк, переключаемые ifdef'ами. Причем, какой тип использовать, решает поставщик библиотеки.

Могу сказать, что Apple выпускает старое размещение ради совместимости с прошлыми платформами. Но при создании новой платформы и ABI, они включают новое размещение. Первый такой раз был при переходе на x64 архитектуру. Второй – при выпуске платформы, основанной на ARM. В Chrome всегда используется новый тип строк, потому что он быстрее. А еще там можно отключить некоторые проявления Undefined Behavior: начиная с C++17 там были непонятные вызовы удаления узловых указателей, и чтобы их исправить, пришлось изменить некоторые структуры.

Джейсон: Чисто из любопытства, если я соберу libc++ и скомпилирую его прямо сейчас, то по умолчанию будет использоваться двоичная совместимость или отсутствовать неопределенное поведение?

Маршалл: По умолчанию используется двоичная совместимость. В прошлом, в задачах libc++ в плане совместимости была возможность работать с кодом, линкованным из libstdc++. В частности, была возможность создавать исключения с одной стороны и ловить их на другой. Окей. Но, пожалуй, это единственная часть, где есть совместимость. Например, вы не сможете передать строки из libc++ в libstdc++.

Джейсон: Мы, как и большинство людей, тяжело расстаемся с привычками. И я слышал однажды, что libc++ совместим с libstdc++, но я думаю, что некоторые наши слушатели линкуют их одновременно в своих приложениях, не подозревая, что у них происходят какие-то нарушения ODR.

Маршалл: Ну если только действительно не подозревают. И причина в том, что libstdc++ помещает символы в пространство std, а libc++ помещает их во встроенное пространство имен std::__1. И когда вы попытаетесь связать их вместе, у них будут разные имена. Окей. Кроме типов исключений, которые находятся в пространстве std. Можно смешать код с обоими из них и ссылаться на оба пространства. Это нормально, потому что std::basic_string будет принадлежать libstdc++, а std::__1::basic_string будет принадлежать libc++. Компилятор и компоновщик знают, что они разные.

Джейсон: Это выглядит и крякает как утка, но в нашем случае это не утка.

Маршалл: Иногда разрыв ABI очень незаметен и очень раздражает когда вскроется. У нас была пара в C++03. Она используется в упорядоченной и неупорядоченной карте. У пары есть два поля: first и second. И конструктор копирования пары был определен в C++03 как:

pair::pair(const pair& rhs)
{
  first = rhs.first;
  second = rhs.second;
}

Вот вам конструктор копирования, готово. Кому-то в голову пришла блестящая идея для C++ 11, поскольку у нас была шикарная новая функция языка =default, и мы должны были предопределить конструктор копирования пар для =default. Он сначала скопирует first, а затем скопирует second. Так короче и понятнее, что происходит: никаких фокусов здесь нет. И на самом деле, компилятор сгенерирует точно такой же код. Все это хорошо... за исключением того, что это открывает новую возможность: когда вы пишете =default в C++, некоторые специализации стандартной пары становятся тривиально копируемыми.

Джейсон: Как пара штанов.

Маршалл: Да, или шорт, или что-то в этом духе. И на каких-то платформах... кхм-кхм, Itanium, тривиально копируемый тип данных, который может поместиться в регистр, передается в качестве параметра на регистрах, а не через стек. И если бы у вас была pair<short, short>, и в ней был нетривиальный конструктор копирования, как в C++03, он был бы передан в стек. А если у вас есть тривиальный конструктор копирования, как в C++11, он передается через регистры. И излишне говорить, если у вас был какой-то код, скомпилированный в C++03, и код, скомпилированный с C++11, была бы ужасная путаница, потому что код с одной стороны пытается найти на стеке то, что находится в регистре, и наоборот.

Джейсон: Есть один вопрос не совсем по теме. А зачем вообще в C++98 для пары был определенный пользователем конструктор копирования?

Маршалл: А чем оно должно было быть?

Джейсон: Компилятор должен был бы сгенерировать конструктор копирования за нас, если не определены другие особые члены класса (правило трех).

Маршалл: Не уверен, что оно так работало в C++98, не могу просто так ответить из головы. Правила агрегирования до сих пор продолжают меняться. Ну в C++11 такой функционал точно есть, да. В libc++ есть пара механизмов (если кое-что не подкрутить во время компиляции) чтобы убедиться, что пары ни в коем случае не передаются если они не тривиально копируемы.

Разрыв ABI

Роб: Вы упомянули, что на встрече в Праге было внесено много предложений. Могли бы мы сделать разрыв ABI? Мы могли бы добиться каких-то улучшений производительности. Велось ли какое-то обсуждение о том, как мы могли бы более безопасно обрабатывать разрывы ABI? Потому что вы говорите о некоторых очень сложных ошибках, которые случаются только во время выполнения программы. Есть ли какой-то способ, которым мы могли бы лучше ловить подобные вещи, чтобы они не сломали наше приложение?

Маршалл: Я бы хотел, чтобы он был. И для меня это было бы ключом ко всему. Если бы у нас был такой способ, я думаю, никто не стал бы возражать против изменения ABI, – я продолжу использовать этот термин, хоть он и неверный, – изменить двоичную компоновку вещей в стандартной библиотеке будет действительно сложно. Программы пользователей будут сбоить. Очевидно, это невозможно исправить на уровне цепочки инструментов, потому что эти инструменты уже не задействованы в местах, где ошибки необходимо обнаружить. Было одно предложение, но насколько я знаю его особо не обсуждали с точки зрения надежности и масштабируемости: поменять манглинг имен, например с C++26.

Джейсон: Похоже на решение, которое вы предприняли для libc++ - просто скрыть все символы за другими именами.

Маршалл: Да, но тогда начнется разделение в C++ сообществе. Кто-то останется на старых бинарниках, кто-то прейдет на новые. Придется выбирать между совместимостью или улучшениями, какими бы они не были. Предстоит много обсуждений. И в конце концов, что бы не приняли в стандарт, это не имеет значения, пока это не реализуют в стандартной библиотеке.

Джейсон: Мне интересно, было ли здесь какое-то историческое изменение. В том смысле, что до 2013 года была целая эпоха, когда Visual Studio ломал ABI буквально с каждым релизом. И GCC тоже ломал ABI с каждым релизом.

Маршалл: Если вы загуглите "GCC сломало ABI", то вы обнаружите, что в списке их примерно дюжина. И только один из них имел отношение к стандартной библиотеке, все остальные... Ах да, примерно в пяти или шести мы изменили манглинг имён для null указателя, потому что в прошлый раз ошиблись :). Но большинство из них были изменениями в манглинге имен очень конкретных вещей.

Но у нас есть пример из libstdc++, и он взят из C++11. И комитет по стандартизации сознательно изменил спецификацию basic_string, чтобы сделать ее такой, чтобы copy-on-write строки на самом деле не соответствовали стандарту. Они не пришли со словами: "Вы не можете реализовать copy-on-write строки". Они просто стандартизировали так, чтобы вы в принципе не смогли бы её реализовать. Вы бы не смогли реализовать copy-on-write строки в соответствии со стандартом. И для этого были хорошие причины. Это произошло примерно в то время, когда я пришел в комитет стандартизации. Там появилась многопоточность, а C++11 и copy-on-write строки плохо в ней работали. Приходилось агрессивно все копировать, чтобы исключить возможность чему-то сломаться. Например, вызов begin() у const std::string возвращал итератор, а не константный итератор. Нужна была копия, потому что через итератор можно было делать запись. И его нельзя было сделать noexcept, потому что он выделял память.

И люди, работающие с libstdc++, сели и задумались о том, как это сделать. И они реализовали вторую версию basic_string. Он был совместим с C++11. А затем они изменили свой компилятор и сделали кучу странных вещей в этой библиотеке. Не пойми меня неправильно, я не имею в виду, что это неправильно. И затем они сказали людям: вот как вы получаете все поведение в целом. Вы получаете новое поведение и позволяете людям выбрать, обновлять или нет. И все равно это была своего рода катастрофа. Я все еще вижу посты на Stack Overflow, где кто-то пишет: "Я написал эту программу используя libstdc++, и она постоянно падает". И время от времени выясняется, что программа того человека линкуется с библиотекой, реализующей copy-on-write строки. При этом сама программа использует строки без copy-on-write, и он передает их туда-сюда. А это не работает. Я знаю, что две организации отказались включать non-copy-on-write strings.

Джейсон: Они используют C++17 или до сих пор на C++98?

Маршалл: Они используют разные версии. Работают на разных системах. Что-то компилируется на C++98. Что-то на 03, что-то на 11, 14, 17. Но в системах, где используется libstdc++, они все еще используют copy-on-write строки. Они планируют переключиться.

Джейсон: Если вы не против, я бы вернулся к истории про пары. К какому выводу в итоге пришли? Означает ли это, что на некоторых платформах пара тривиальных объектов все еще не поддается тривиальному копированию?

Маршалл: Да. Как наш macOS. И причина этого кроется как раз в том, чтобы решить проблему разрыва ABI. Да. В libc++ происходит наследование из пустого базового класса, если не установить определенный макрос для разрыва ABI. С нетривиальным, но пустым конструктором копирования.

Джейсон: Понятно. Просто чтобы вернуться к тому поведению.

Маршалл: Хочу показать вам один сценарий. На мой взгляд, очень интересный мысленный эксперимент. Представим, что Apple в комитете по стандартизации собирается сделать разрыв ABI для C++23, как-нибудь. И Apple говорит, окей, мы будем его поставлять. И мы идем к художнику, который пользуется Photoshop каждый день. Он получает уведомление об каком-нибудь обновлении 11.3 для macOS и говорит: "Окей, отлично. Я обновлю, потому что тут есть внушительный список нужных мне функций". И там есть примечание: "Ой, знаете, мы внесли некоторые изменения в стандартную библиотеку C++, вам нужно обновить все ваши программы". Окей, отлично. Пользователи обновляют систему, а затем идут к Adobe и просят новую версию Photoshop. И Adobe говорит: "Окей, без проблем, у вас же есть подписка. Вот новая версия". Все отлично.

И он открывает свои файлы из Photoshop и начинает с ними работать. Если ему повезет, ни один из плагинов не загрузится. Если ему умеренно не повезет, Photoshop вылетит мгновенно, потому что попытается загрузить все используемые плагины. А у него старый ABI. Если ему очень не повезет, Photoshop будет работать отлично. А затем художник что-то сделает, и программа вылетит или испортит все его документы.

И пользователь поймет, что ему нужно обновить плагины. Отлично. Сколько их у меня? Около 40? Я поспрашивал у людей в Adobe и это вполне реальное количество плагинов для людей, которые используют Photoshop каждый день. 40 плагинов от 15 разных поставщиков. Окей. Мне придется связаться со всеми 15 и получить обновления для каждого из них. Кто-то из них скажет: "Окей, без проблем, держи". Кто-то скажет: "Да, у нас есть новая версия. Но за обновление придется заплатить". Кто-то скажет: "Ну да, наверное мы это сделаем когда-нибудь". Кто-то вообще ничего не скажет, потому что они не отвечают. Удивительное количество плагинов Photoshop возникает в ходе работы над магистерской диссертацией. Но это не тот результат, который хочет видеть Apple или Adobe. Не тот результат, который хочет видеть libc++ как разработчик стандартной библиотеки. Это даже не тот результат, который хочу видеть я.

Мы не хотим, чтобы люди думали, что ПО, написанное на C++, изначально нестабильно и может сломаться, как только вы что-то измените в системе.

Роб: Adobe выпускает новые версии. И если они выпустят новую версию, в которой они могут вносить изменения в API или добавлять новые API, может наступить время для обновления до последних изменений в ABI.

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

Джейсон: То есть пока у нас нет решения, есть ли вообще какой-то способ сломать ABI в стандартной библиотеке, чтобы двигаться вперед? Например, вы бы сказали: "Нет, здесь нет вариантов, пока мы не найдем хорошее решение" или "Конечно. Знаешь, в 2035 мы сможем это сделать".

Маршалл: Зависит от ситуации. Зависит от людей или вовлеченных организаций. В смысле, очевидно, когда кто-то начинает с новым ABI, у вас чистый лист, можете делать что угодно. Если у вас понимающие пользователи, которые не против, то пожалуйста дерзайте. Люди из Linux могут так сделать. Хотя они все еще сталкиваются с проблемами в вещах, сделанных для Red Hat 6 при попытке запустить их на Red Hat 8. Потому что у вас есть пре-С++11 строки. Но в Linux, если вы готовите большой релиз, вы собираете все из исходников. Для Google, например, где каждая отдельная сборка их ПО происходит с нуля, стабильный ABI не имеет никакой пользы. Они могут менять их с каждой новой версией.

Джейсон: А у Boost есть стабильный ABI?

Маршалл: Там немного больше нюансов. Boost не обещает стабильный ABI. У него и так стабильный ABI, если только нет веской причины его менять.

Джейсон: Окей. А что тогда делает Boost? Чем он отличается от стандартной библиотеки?

Маршалл: Я дам самый простой ответ: ты можешь его пересобрать. Для Boost есть исходный код. Тоже можно сделать и с libc++, но, если вы не знаете, с какими настройками он собрался в Apple, вам придется поиграть в детектива, чтобы выяснить.

Джейсон: И я слышал, что с Boost я все еще могу выбрать старую версию библиотеки, пока хочу?

Маршалл: Да, можешь. И можешь собирать старые версии библиотеки. Но если ты получил стандартную библиотеку от поставщика твоей системы, ты будешь использовать ее. Когда libc++ был в новинку, люди говорили: "О, смотрите, я могу заменить стандартную реализацию на моем Mac'е чем-то с более новыми функциями. И Говард написал об этом хорошую статью. По сути, главной мыслью было: "Да, отличный способ сделать так, чтобы ваш Mac перестал запускаться". Заменять стандартную библиотеку чем-то, что вы только что собрали – нормально, если она получилась той же самой. Но если это так, зачем вы ее заменяете? А если они различаются, то вы точно исследовали все места, где используется стандартная библиотека в macOS? Вы уверены, что замена ее не поломает? Это делать не рекомендуется. Было бы лучше, если бы стандартная библиотека могла эволюционировать лучше, чем путь, по которому развивается Java. Но просто менять новые имена – это единственный способ, который я сейчас могу придумать, чтобы избежать ситуации "давай тут все поменяем, а если все ломается –это не моя проблема".

Джейсон: Много библиотек делают так. Они решают сделать серьезный разрыв и не только меняют номер релиза, но и название полностью.

Маршалл: Это один из способов. Знаю, Apple приложило много усилий за эти годы, поставляя различные вещи под названием fat binaries, в которых содержится несколько версий объектного кода. Я предполагаю, что решение может быть таким, но это только зародыш идеи. Это не само решение. С другой стороны, есть предложение под названием C++ Epochs. И похоже, что это тоже может решить проблему, но опять же, за счет того, что сообщество C++ раскололось бы на шесть частей: 98, 03, 11, 14, 17, 20. Знаешь, когда ты создаешь что-то с C++17, оно живет в Epochs, и оно связывается с кодом, созданным на C++17. И если вам нужна, скажем JSON библиотека Неилса Лохманна, которая должна работать с кодом, созданным на C++11, 14, 17 и 20, вам нужно создать четыре копии библиотеки. Пространство на диске дешевое, но не бесплатное. Все переходят на SSD, а SSD на 16 терабайт очень дорогие. Я с пониманием отношусь к идее улучшения вещей в стандартной библиотеке. Но идея изменить их поведение или их расположение, а потом говорить: "Если это сломалось - не моя проблема"... это мне не нравится.

Идея о том, что вы можете просто перестроить свое ПО, не по нраву тем, кто не живет в мире исходного кода.

Роб: Я ценю, что вы показали эту точку зрения, и я, конечно, думаю, что есть некоторые вещи, о которых мы не поговорили в предыдущих обсуждениях ABI. Я определенно согласен с тем, что нам нужен способ развиваться. Просто я не уверен, каким образом. Но, надеюсь, члены комиссии стандартизации думают об этом и пытаются что-то придумать.

Маршалл: У меня тоже нет никаких хороших идей. Я имею в виду, что у меня есть пара предположений, которые могут перерасти в полноценные предложения. Но пока это только размышления. Может быть, нам стоит посмотреть в сторону этого направления и подумать о том, как это сделать, но это не решение. Это идея и возможно она сработает.

Роб: Похоже, нам действительно нужно какое-то предложение по стандартам, которое решит проблему. Стандарт должен определить решение.

Маршалл: Не знаю. О многих вещах стандарт вообще ничего не говорит. Стандарт не говорит о том, как компилятору реализовать виртуальные функции. Он определяет поведение виртуальных функций.

Спасибо большое, что прослушали наш разговор о C++.

Мы будем рады услышать ваши мысли о подкасте. Пожалуйста, дайте нам знать об интересных вам темах. Можете присылать их нам на почту: feedback@cppcast.com. Мы также будем рады, если вы поставите лайк или подпишетесь на CppCast в Твиттере. Можете также подписаться на меня @robwirving и Джейсона @lefticus в Твиттере. Мы бы также хотели поблагодарить всех, кто поддерживает нас на Patreon. Если и вы хотите присоединиться - переходите по patreon.com/cppcast. И конечно же, вы можете найти всю информацию на веб-сайте подкаста на cppcast.com. Музыка для этого выпуска была предоставлена podcastthemes.com.

Ресурсы

Подкаст

Новости

Ссылки

Спонсоры