Создание библиотеки с обратно совместимым 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 поддерживает переименование пространства имен.


person Artyom    schedule 07.05.2009    source источник
comment
Что вы имеете в виду под динамической компоновкой, похожей на статическую? SO в ELF очень похожи на библиотеки DLL в Windows. Фактически, библиотеки DLL, вероятно, больше похожи на статическое пристрастие, чем SO ELF (библиотеки DLL не зависят от позиции, как это обычно бывает с SO в ELF).   -  person Zifre    schedule 08.05.2009


Ответы (4)


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

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

A. скомпилировать с -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 в Как писать общие библиотеки < / а>.

person Zifre    schedule 07.05.2009
comment
Этого недостаточно, даже если я не раскрываю Boost API, потому что пользователь может использовать другую версию Boost, которая будет мешать внутренним символам, используемым в этой библиотеке. - person Artyom; 08.05.2009
comment
Вы можете исправить это с помощью компоновщика. - person Zifre; 08.05.2009
comment
Как я могу это сделать? Как мне отфильтровать символы, которые я хочу показывать, но не думаю? Напомним, что опций типа __dllexport не существует. - person Artyom; 08.05.2009
comment
Последний вопрос: есть ли способ скрыть динамически выровненную библиотеку от других? Имеется в виду что-то вроде g ++ -shared ... -o libfoo.so -Wl, hide-these -lboost_regex -lboost_XYZ -Wl, stop-hiding? Я просто не могу найти. Или мне нужно перекомпилировать boost_regex и boost_xyz с -fvisibility = hidden? - person Artyom; 09.05.2009
comment
@Artyom: Не рекомендуется динамически связывать DSO с другим DSO. Вы не можете просто перекомпилировать библиотеки с помощью -fvisibility = hidden, потому что тогда ваша библиотека не сможет получить к ним доступ. Вероятно, вам следует связать другие библиотеки статически с помощью -fvisibility = hidden. - person Zifre; 09.05.2009
comment
Большое спасибо! Вы действительно мне помогли! - person Artyom; 10.05.2009
comment
Все хорошие советы, хотя лично я бы опасался раскрывать даже STL в интерфейсах (приходилось поставлять библиотеки клиентам, использующим необычную реализацию STL). И не позволяйте мне начинать заниматься проблемами совместимости в мире Windows с отладочной версией STL от MS итератора ... - person timday; 19.05.2009
comment
@timday И не позволяйте мне начинать заниматься проблемами совместимости в мире Windows с отладочной версией STL от MS итератора. К счастью, я не занимаюсь разработкой для компиляторов Windows и Windows. - person Artyom; 20.05.2009
comment
В конце концов остановился на скрипте переименования Boost ... Слишком много хлопот и исправления ошибок сборки. - person Artyom; 24.05.2009

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

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

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

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

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

Изменить:

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

person Jared Oberhaus    schedule 08.05.2009

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

person Community    schedule 07.08.2009

У вас должно получиться что-то вроде этого:

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

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

person rlbond    schedule 07.05.2009
comment
Это не сработает, потому что, если my_library использует, например, std :: string и включает ‹string›, он будет помещен в пространство имен XYZ, что неверно. - person Artyom; 08.05.2009
comment
пространство имен просто окружает включение boost - person Gaetano Mendola; 12.05.2009