¿Se puede hacer una definición de preprocesador #define o similar en todas las unidades de traducción?

Las implementaciones de encabezado son útiles para bibliotecas realmente pequeñas ya que todo el código puede estar contenido y distribuido con un solo encabezado con la siguiente estructura:

// library.h

void libFunc(); // forward decl

#ifdef IMPLEMENT_LIBRARY
int libState;
volatile int libVolState; // library state exposed to external processes
void libFunc(){
    // definition
}
#endif

Sin embargo, esta estructura requiere que el usuario defina IMPLEMENT_LIBRARY antes de la inclusión del encabezado en solo una de sus unidades de traducción, lo que significa que no se puede poner en los archivos de encabezado del usuario y puede ser un poco confuso para alguien que no está completamente familiarizado con las reglas de compilación de C ++.

Si hubiera una manera de definir IMPLEMENT_LIBRARY en todas las TU, esto podría hacerse automáticamente con

#ifndef IMPLEMENT_LIBRARY
#defineToAllUnits IMPLEMENT_LIBRARY
// library state
// definitions
#endif

¿Existe tal mecanismo o el sistema actual de un solo encabezado es tan bueno como sea posible?

2
Anne Quinn 2 dic. 2019 a las 16:18

2 respuestas

La mejor respuesta

Algunas unidades de compilación podrían haberse compilado antes de la que contendría un #defineToAllUnits, por lo que no es posible.

En la práctica, su problema a menudo se resuelve utilizando el sistema de compilación para pasar una opción -DIMPLEMENTAT_LIBRARY al compilador (o sintaxis equivalente). Otra posibilidad, común cuando se trata de lograr una portabilidad amplia con varias definiciones, es tener un encabezado de configuración como config.h incluido en todas partes. Ese encabezado se puede generar automáticamente en el momento de la configuración.

También puede evitar infringir el ODR utilizando funciones y variables inline.

2
AProgrammer 2 dic. 2019 a las 13:23

Para este caso de uso, probablemente no debería usar la definición de macro en absoluto. Si desea que la función se defina en la TU del usuario de la biblioteca, puede usar una función en línea. Las funciones en línea se pueden definir en más de una TU (siempre que la definición sea la misma):

// library.h
inline void libFunc(){
    // definition
}

Otro enfoque sería compilar la biblioteca por separado y hacer que el usuario de la biblioteca se vincule con ella en lugar de tener la definición dentro de su propia TU.


En cuanto a la pregunta en sí

¿Se puede hacer una definición de preprocesador #define o similar en todas las unidades de traducción?

Está bien #definir macros de preprocesador en más de una TU:

// a.cpp
#define foo a

// b.cpp
#define foo b

Si desea que las definiciones coincidan en todas las TU que la definen, puede colocar la definición de macro en un archivo de encabezado e incluir eso:

// h.hpp
#define foo h

// a.cpp
#include "h.hpp"

// b.cpp
#include "h.hpp"

No es posible "inyectar" definiciones de una TU a otras TU, por lo que no hay un equivalente posible de "#defineToAllUnits". es típicamente posible "inyectar" definiciones de macro desde la invocación del compilador: gcc a.cpp b.cpp -Dfoo=h. Sin embargo, no veo que esto sea útil para su caso de uso.

2
eerorika 2 dic. 2019 a las 13:35