c++ - library - Creación de biblioteca con ABI compatible con versiones anteriores que usa Boost
boost wikipedia (4)
Básicamente, solo asegúrate de que la interfaz pública de tu biblioteca no exponga Boost. Siempre puede usarlo sin importar cuánto desee internamente. En general, tener la interfaz de una biblioteca depende de otra biblioteca es malo (a menos que dependa de una biblioteca estándar como STL). Boost casi encaja en la categoría de biblioteca "estándar", pero su ABI cambia tanto que su interfaz no debería usarlo.
Para asegurarse de no exponer los símbolos de Boost, hay algunas cosas que puede hacer:
A. compilar con -fvisibility=hidden
y marcar todos los símbolos públicos con __attribute__((visibility("default")))
. podrías usar una macro para hacer esto más fácil:
#define ABI __attribute__((visibility("default")))
B. haz algo como esto:
#pragma GCC visibility push(hidden)
#include <boost/whatever.hpp>
#pragma GCC visibility pop
También debe envolver esto alrededor de todos los demás símbolos internos que no desea exportar, o declarar esto con __attribute__((visibility("hidden")))
. Nuevamente, podría usar una macro para hacer esto más fácil:
#define INTERNAL __attribute__((visibility("hidden")))
De estas opciones, me gusta A, porque me hace pensar explícitamente sobre qué símbolos se exportan, para que no exporte accidentalmente cosas que no desea.
Por cierto, puede encontrar mucha más información sobre cómo crear DSO en Cómo escribir librerías compartidas de Ulrich Drepper.
Estoy trabajando en una cierta biblioteca C ++ (o más framework). Quiero que sea compatible con versiones anteriores conservando no solo la compatibilidad API sino también ABI (como el gran trabajo que hace Qt).
Utilizo muchas funcionalidades de Boost y me parece que esto hace que la compatibilidad con versiones anteriores sea simplemente imposible, a menos que fuerce a un usuario a tener exactamente la misma versión (a veces antigua) de Boost.
¿Hay alguna forma (sin reescribir 1/2 de Boost) de hacer un "prefijo" alrededor de su espacio de nombres / cambiarle el nombre para evitar que interfiera con una versión de usuario de Boost?
Por ejemplo, mi libXYZ usa Boost 1.33 y tiene clase boost::foo
. En la versión 1.35 boost::foo
se actualizó y se agregó un nuevo miembro, por lo tanto, boost::foo
de 1.33 y 1.35 no son compatibles con ABI. Entonces, un usuario de libXYZ debe usar Boost 1.33 o recompilar libXYZ con Boost 1.35 (que ya puede haber roto alguna API de una manera que XYZ no compilaría).
Nota: Estoy hablando del sistema operativo UNIX / Linux con ELF donde el enlace dinámico es similar al enlace estático, por lo que no se puede vincular con dos versiones diferentes de bibliotecas porque los símbolos interferirían.
Una solución adecuada que puedo pensar es poner Boost en algún otro espacio de nombres privado. Entonces, libXYZ usaría ::XYZ::boost::foo
lugar de ::boost::foo
. Esto evitaría la colisión con otra versión de Boost que el usuario pueda usar.
Entonces, libXYZ continuaría trabajando con Boost 1.33 enlazado de forma estática o dinámica con él dentro de otro espacio de nombres, asumiendo que:
- No expondría la API de Boost fuera.
- Mantendría una versión privada estable de la API expuesta.
¿Hay alguna manera de hacer tales cosas con Boost?
Editar: Finalmente, decidí crear una secuencia de comandos que cambiaría el nombre de todos los símbolos de impulso en la fuente a algún símbolo personalizado.
Justificación: simplificación del proceso de compilación, independiente del soporte de visibilidad del compilador, también, la visibilidad funciona solo en las bibliotecas dinámicas, por lo que esto no funciona, por lo que necesito compilaciones y dependencias separadas para cada tipo de bibliotecas.
El script está disponible allí: http://art-blog.no-ip.info/files/rename.py
Edición 2: la última versión de Boost BCP admite el cambio de nombre del espacio de nombres.
Considere el uso abi-compliance-checker herramienta abi-compliance-checker para mantener una interfaz API / ABI estable.
Deberías poder hacer algo como esto:
namespace XYZ
{
#include <boost/my_library.hpp>
}
Y debería volcar los encabezados de impulso en el espacio de nombres XYZ. Sin embargo, tenga en cuenta que esto solo funcionará con las bibliotecas de solo encabezado.
En general, no puede confiar en ningún tipo de ABI en C ++ más allá de los enlaces C estándar. Pero dependiendo de la cantidad de suposiciones que realice, puede usar cada vez más C ++ en su interfaz.
Encontré this excelente artículo sobre los pasos para hacer que su API se convierta en un ABI estable. Por ejemplo, nunca pase los tipos de datos Biblioteca estándar de C ++ (o Boost) a través de su interfaz; podría llegar al punto de equilibrio con una pequeña corrección de errores en la biblioteca.
Algunos ejemplos de los problemas a tener en cuenta al publicar una API compatible con ABI son:
- Montón de depuración de Windows . Debe asegurarse de que todas las asignaciones y desasignaciones estén en el mismo lado de un "módulo" (es decir, ejecutable o DLL).
- Problema de interfaz binaria frágil . Incluso si ambos lados de su sistema usan sistemáticamente el mismo compilador y las mismas bibliotecas, debe tener cuidado en C ++ acerca de lo que publica en sus archivos
.h
y dónde ocurren las asignaciones.
Si sigue el artículo vinculado, encontrará soluciones para estos y otros problemas.
Editar :
También encontré un artículo interesante publicado por Microsoft que describe cómo funcionan las interfaces COM, tomando un proyecto C ++ y convirtiéndolo en COM. Creo que una de las principales razones por las que Microsoft desarrolló COM fue para resolver el problema de la Interfaz Frágil Binaria que tiene C ++, por lo que pueden enviar archivos DLL con API orientadas a objetos de publicación.