Beneficios de las bibliotecas de solo encabezado

98

¿Cuáles son los beneficios de una biblioteca de solo encabezado y por qué la escribiría de esa manera en oposición a poner la implementación en un archivo separado?

NebulaFox
fuente
Principalmente plantillas, pero también hará que sea un poco más fácil de distribuir y usar.
BoBTFish
4
Me gustaría agregar las desventajas de una biblioteca de solo encabezado al alcance de la pregunta ...
moooeeeep
¿Qué inconvenientes hay que no se hayan mencionado ya?
NebulaFox
7
@moooeeeep: para las desventajas, es posible que desee leer el párrafo "Detener el código en línea " en la página web de proyectos de Chromium, lo que se debe y no se debe hacer en C ++ .
Mr.C64

Respuestas:

57

Hay situaciones en las que una biblioteca de solo encabezado es la única opción, por ejemplo, cuando se trata de plantillas.

Tener una biblioteca de solo encabezado también significa que no tiene que preocuparse por las diferentes plataformas donde se puede usar la biblioteca. Cuando separa la implementación, generalmente lo hace para ocultar los detalles de la implementación y distribuir la biblioteca como una combinación de encabezados y bibliotecas ( lib, dll's o .soarchivos). Por supuesto, estos deben compilarse para todos los diferentes sistemas operativos / versiones que ofrezca soporte.

También podría distribuir los archivos de implementación, pero eso significaría un paso adicional para el usuario: compilar su biblioteca antes de usarla.

Por supuesto, esto se aplica caso por caso . Por ejemplo, las bibliotecas de solo encabezado a veces aumentantamaño del código y tiempos de compilación.

Luchian Grigore
fuente
6
"Tener una biblioteca de solo encabezado también significa que no tiene que preocuparse por las diferentes plataformas donde se puede usar la biblioteca": solo si no tiene que mantener la biblioteca. De lo contrario, es una pesadilla, con informes de errores que no puede reproducir o probar en el material que tiene.
James Kanze
1
Acabo de hacer una pregunta similar sobre los beneficios de rendimiento del encabezado solamente. Como puede ver, no hay diferencia en el tamaño del código. Sin embargo, la implementación de solo encabezado de ejemplo fue un 7% más lenta. stackoverflow.com/questions/12290639/…
Homer6
@ Homer6 gracias por hacerme ping. En realidad, nunca he medido esto.
Luchian Grigore
1
@LuchianGrigore No pude encontrar a nadie más que lo hubiera hecho. Por eso tardó un poco en responder. Hay tantos comentarios especulativos sobre "aumento del tamaño del código" y "consumo de memoria". Finalmente tengo una instantánea de las diferencias, incluso si es solo un ejemplo.
Homer6
@ Homer6. ¿Por qué no aumentaría el tamaño del código? Suponiendo que crea varias bibliotecas que usan el encabezado solo lib y luego su aplicación usa todas esas bibliotecas, tendría que tener varias copias en lugar de vincularlas con una sola biblioteca compartida.
pooya13
60

Beneficios de la biblioteca de solo encabezado:

  • Simplifica el proceso de construcción. No es necesario que cree la biblioteca y no es necesario que especifique la biblioteca compilada durante el paso de enlace de la compilación. Si tiene una biblioteca compilada, probablemente desee crear varias versiones de ella: una compilada con la depuración habilitada, otra con la optimización habilitada y posiblemente otra sin símbolos. Y tal vez incluso más para un sistema multiplataforma.

Desventajas de una biblioteca de solo encabezado:

  • Archivos de objetos más grandes. Cada método en línea de la biblioteca que se utiliza en algún archivo fuente también obtendrá un símbolo débil, una definición fuera de línea en el archivo de objeto compilado para ese archivo fuente. Esto ralentiza el compilador y también ralentiza el enlazador. El compilador tiene que generar toda esa hinchazón, y luego el enlazador tiene que filtrarlo.

  • Recopilación más larga. Además del problema de hinchazón mencionado anteriormente, la compilación llevará más tiempo porque los encabezados son intrínsecamente más grandes con una biblioteca de solo encabezado que con una biblioteca compilada. Esos encabezados grandes deberán analizarse para cada archivo fuente que use la biblioteca. Otro factor es que esos archivos de encabezado en una biblioteca de solo #includeencabezado tienen que los encabezados necesarios para las definiciones en línea, así como los encabezados que se necesitarían si la biblioteca se hubiera construido como una biblioteca compilada.

  • Recopilación más enredada. Obtiene muchas más dependencias con una biblioteca solo de encabezado debido a esos #includes adicionales necesarios con una biblioteca solo de encabezado. Cambie la implementación de alguna función clave en la biblioteca y es posible que deba volver a compilar todo el proyecto. Realice ese cambio en el archivo fuente de una biblioteca compilada y todo lo que tiene que hacer es volver a compilar ese archivo fuente de biblioteca, actualizar la biblioteca compilada con ese nuevo archivo .o y volver a vincular la aplicación.

  • Más difícil de leer para el humano. Incluso con la mejor documentación, los usuarios de una biblioteca a menudo tienen que recurrir a leer los encabezados de la biblioteca. Los encabezados en una biblioteca de solo encabezado están llenos de detalles de implementación que obstaculizan la comprensión de la interfaz. Con una biblioteca compilada, todo lo que ve es la interfaz y un breve comentario sobre lo que hace la implementación, y eso suele ser todo lo que desea. Eso es realmente todo lo que debería desear. No debería tener que conocer los detalles de implementación para saber cómo usar la biblioteca.

David Hammen
fuente
21
El último punto realmente no tiene sentido. Cualquier documentación razonable incluirá la declaración de función, parámetros, valores de retorno, etc. y todos los comentarios asociados. Si tiene que consultar el archivo de encabezado, la documentación ha fallado.
Thomas
6
@Thomas - Incluso con las mejores bibliotecas profesionales, a menudo me encuentro teniendo que recurrir a leer el encabezado "fino". De hecho, si la así llamada documentación "fina" se extrae del código más el comentario, normalmente me gusta leer los encabezados. El código más los comentarios me dicen más que la documentación generada automáticamente.
David Hammen
2
El último punto no es válido. Los encabezados ya están llenos de detalles de implementación en los miembros privados, por lo que no es como si el archivo cpp ocultara todos los detalles de implementación. Además, los lenguajes como C # son esencialmente "solo encabezados" por diseño, y el IDE se encarga de ocultar los detalles ("
plegarlos
2
@Tomas: De acuerdo, el último punto es completamente falso. Puede mantener fácilmente la interfaz y la implementación igualmente separadas con las bibliotecas de solo encabezado; simplemente tiene el encabezado de la interfaz #incluye los detalles de implementación. Esta es la razón por la que las bibliotecas de Boost generalmente incluyen un subdirectorio (y un espacio de nombres) llamado detail.
Nemo
4
@Thomas: No estoy de acuerdo. El archivo de encabezado es generalmente el primer lugar al que voy para obtener documentación. Si el encabezado está bien escrito, a menudo no hay necesidad de documentación externa.
Joel Cornett
14

Sé que este es un hilo antiguo, pero nadie ha mencionado interfaces ABI o problemas específicos del compilador. Así que pensé que lo haría.

Esto se basa básicamente en el concepto de que usted escribe una biblioteca con un encabezado para distribuir a las personas o reutiliza usted mismo en lugar de tener todo en un encabezado. Si está pensando en reutilizar un encabezado y archivos de origen y volver a compilarlos en cada proyecto, entonces esto realmente no se aplica.

Básicamente, si compila su código C ++ y crea una biblioteca con un compilador, el usuario intenta usar esa biblioteca con un compilador diferente o una versión diferente del mismo compilador, entonces puede obtener errores del vinculador o un comportamiento extraño en tiempo de ejecución debido a incompatibilidad binaria.

Por ejemplo, los proveedores de compiladores a menudo cambian su implementación de STL entre versiones. Si tiene una función en una biblioteca que acepta un std :: vector, entonces espera que los bytes de esa clase estén organizados de la forma en que se organizaron cuando se compiló la biblioteca. Si, en una nueva versión del compilador, el proveedor ha realizado mejoras de eficiencia en std :: vector, entonces el código del usuario ve la nueva clase que puede tener una estructura diferente y pasa esa nueva estructura a su biblioteca. Todo va cuesta abajo a partir de ahí ... Es por eso que se recomienda no pasar objetos STL a través de los límites de la biblioteca. Lo mismo se aplica a los tipos C Run-Time (CRT).

Mientras se habla del CRT, su biblioteca y el código fuente del usuario generalmente deben estar vinculados al mismo CRT. Con Visual Studio, si crea su biblioteca usando el CRT multiproceso, pero el usuario se vincula con el CRT de depuración multiproceso, tendrá problemas de vínculo porque es posible que su biblioteca no encuentre los símbolos que necesita. No recuerdo qué función era, pero para Visual Studio 2015, Microsoft hizo una función CRT en línea. De repente, estaba en el encabezado, no en la biblioteca CRT, por lo que las bibliotecas que esperaban encontrarlo en el momento del enlace ya no podían hacerlo y esto generó errores de enlace. El resultado fue que estas bibliotecas debían volver a compilarse con Visual Studio 2015.

También puede obtener errores de enlace o comportamientos extraños si usa la API de Windows pero construye con una configuración Unicode diferente para el usuario de la biblioteca. Esto se debe a que la API de Windows tiene funciones que usan cadenas Unicode o ASCII y macros / define que automáticamente usan los tipos correctos según la configuración Unicode del proyecto. Si pasa una cadena a través del límite de la biblioteca que es del tipo incorrecto, las cosas se rompen en tiempo de ejecución. O puede encontrar que el programa no se vincula en primer lugar.

Estas cosas también son válidas para pasar objetos / tipos a través de los límites de la biblioteca de otras bibliotecas de terceros (por ejemplo, un vector Eigen o una matriz GSL). Si la biblioteca de terceros cambia su encabezado entre usted compilando su biblioteca y su usuario compilando su código, entonces las cosas se romperán.

Básicamente, para estar seguro, lo único que puede pasar a través de los límites de la biblioteca son los tipos integrados y los datos antiguos sin formato (POD). Idealmente, cualquier POD debería estar en estructuras que se definan en sus propios encabezados y no dependan de encabezados de terceros.

Si proporciona una biblioteca de solo encabezado, todo el código se compila con la misma configuración del compilador y con los mismos encabezados, por lo que muchos de estos problemas desaparecen (siempre que la versión de las bibliotecas de terceros que usted y su usuario usen sean compatibles con API).

Sin embargo, hay aspectos negativos que se han mencionado anteriormente, como el aumento del tiempo de compilación. Además, es posible que esté dirigiendo un negocio, por lo que es posible que no desee entregar todos los detalles de implementación del código fuente a todos sus usuarios en caso de que uno de ellos se lo robe.

Phil Rosenberg
fuente
8

El principal "beneficio" es que requiere que entregue el código fuente, por lo que terminará con informes de error en las máquinas y con compiladores de los que nunca ha oído hablar. Cuando la biblioteca es completamente de plantillas, no tiene muchas opciones, pero cuando tiene la opción, el encabezado solo suele ser una mala elección de ingeniería. (Por otro lado, por supuesto, el encabezado solo significa que no tiene que documentar ningún procedimiento de integración).

James Kanze
fuente
0

La integración se puede realizar mediante la optimización del tiempo de enlace (LTO)

Me gustaría resaltar esto ya que disminuye el valor de una de las dos ventajas principales de las bibliotecas de solo encabezado: "necesita definiciones en un encabezado para insertarlas".

Un ejemplo concreto mínimo de esto se muestra en: Optimización del tiempo de enlace y en línea

Así que simplemente pasa una bandera, y la inserción se puede hacer en archivos de objeto sin ningún trabajo de refactorización, ya no es necesario mantener definiciones en los encabezados para eso.

Sin embargo, LTO también puede tener sus propias desventajas: ¿Hay alguna razón por la que no se debe utilizar la optimización del tiempo de enlace (LTO)?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente