Создание библиотеки с обратно совместимым ABI, использующим Boost

Я работаю над определенной библиотекой C++ (или более фреймворком). Я хочу сделать его обратно совместимым с предыдущими версиями, сохраняя не только API-совместимость, но и ABI (как и отличная работа Qt).

Я использую множество функций Boost, и мне кажется, что это делает обратную совместимость просто невозможной, если я не заставляю пользователя иметь точно такую ​​же (иногда старую) версию Boost.

Есть ли способ (без переписывания 1/2 Boost) сделать некоторый "префикс" вокруг его пространства имен / переименовать его, чтобы он не мешал пользовательской версии Boost?

Например, мой libXYZ использует Boost 1.33 и имеет класс boost::foo, В версии 1.35 boost::foo был обновлен и добавлен новый член, так что boost::foo с 1.33 и 1.35 несовместимы с ABI. Таким образом, пользователь libXYZ должен использовать Boost 1.33 или перекомпилировать libXYZ с Boost 1.35 (что, возможно, уже сломало некоторый API из-за того, что XYZ не будет компилироваться).

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

Одно подходящее решение, о котором я могу подумать, это поместить Boost в другое частное пространство имен. Итак, libXYZ будет использовать ::XYZ::boost::foo вместо ::boost::foo, Это предотвратит конфликт с другой версией Boost, которую может использовать пользователь.

Таким образом, libXYZ будет продолжать работать с Boost 1.33, статически или динамически связанным с ним, в другом пространстве имен, предполагая, что он:

  • Не будет выставлять Boost API снаружи.
  • Сохранял бы стабильную приватную версию выставленного API.

Есть ли способ сделать такие вещи с Boost?

Изменить: Наконец я решил создать сценарий, который переименовал бы все символы повышения в источнике к некоторому пользовательскому символу.

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

Сценарий доступен здесь: http://art-blog.no-ip.info/files/rename.py

Редактировать 2: Последняя версия Boost BCP поддерживает переименование пространства имен.

4 ответа

По сути, просто убедитесь, что открытый интерфейс вашей библиотеки не предоставляет Boost. Вы всегда можете использовать его как угодно внутри. Как правило, наличие интерфейса библиотеки зависит от другой библиотеки - это плохо (если это не зависит от стандартной библиотеки, такой как STL). Boost почти вписывается в категорию "стандартной" библиотеки, но ее ABI изменяется настолько, что ваш интерфейс не должен его использовать.

Чтобы убедиться, что вы не выставляете символы Boost, есть несколько вещей, которые вы можете сделать:

А. скомпилировать с -fvisibility=hidden и пометить все публичные символы __attribute__((visibility("default"))), Вы можете использовать макрос, чтобы сделать это проще:

#define ABI __attribute__((visibility("default")))

Б. сделать что-то вроде этого:

#pragma GCC visibility push(hidden)
#include <boost/whatever.hpp>
#pragma GCC visibility pop

Вы должны также обернуть это вокруг всех других внутренних символов, которые вы не хотите экспортировать, или объявить это с __attribute__((visibility("hidden"))), Опять же, вы можете использовать макрос, чтобы сделать это проще:

#define INTERNAL __attribute__((visibility("hidden")))

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

Между прочим, вы можете найти гораздо больше информации о создании DSO в Ульрике Дреппере " Как писать общие библиотеки".

В общем, вы не можете полагаться на любой тип ABI в C++, кроме стандартных привязок C. Но в зависимости от того, сколько предположений вы делаете, вы можете использовать все больше и больше C++ в своем интерфейсе.

Я нашел эту замечательную статью о шагах, чтобы превратить ваш API в стабильный ABI. Например, никогда не передавайте типы данных стандартной библиотеки C++ (или Boost) через интерфейс; это может привести к поломке даже при небольшом исправлении ошибки в библиотеке.

Вот некоторые примеры проблем, на которые следует обратить внимание при публикации ABI-совместимого API:

  • Кучи отладки Windows. Вы должны быть уверены, что все выделения и освобождения находятся на одной стороне "модуля" (то есть исполняемого файла или DLL).
  • Хрупкая проблема двоичного интерфейса. Даже если обе стороны вашей системы постоянно используют один и тот же компилятор и библиотеки, вы должны быть осторожны в C++ с тем, что вы публикуете в своем .h файлы, и где происходят выделения.

Если вы будете следовать связанной статье, вы найдете решения для этих и других проблем.

Редактировать:

Я также нашел интересную статью, опубликованную Microsoft, которая описывает, как работают интерфейсы COM, взяв проект C++ и превратив его в COM. Я считаю, что одной из основных причин, по которой Microsoft разработала COM, было решение проблемы хрупкого двоичного интерфейса, которая есть в C++, чтобы они могли поставлять библиотеки DLL с опубликованными объектно-ориентированными API.

Рассмотрите возможность использования инструмента проверки соответствия abi для поддержания стабильного интерфейса API/ABI.

Вы должны быть в состоянии сделать что-то вроде этого:

namespace XYZ
{
#include <boost/my_library.hpp>
}

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

Другие вопросы по тегам