Создание библиотеки с обратно совместимым ABI, использующим Boost - PullRequest
24 голосов
/ 08 мая 2009

Я работаю над определенной библиотекой 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 ]

29 голосов
/ 08 мая 2009

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

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

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

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

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

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

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

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

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

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

5 голосов
/ 08 мая 2009

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

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

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

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

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

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

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

3 голосов
/ 08 августа 2009

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

0 голосов
/ 08 мая 2009

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

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

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

...