Pregunta

Tengo un programa en C ++ que compila con MinGW (gcc para Windows). Usando la liberación TDM de MinGW que incluye gcc 4.4.1. Los enlaces a dos ejecutables biblioteca estática (.a) archivos: Uno de ellos es una biblioteca de terceros escrito en C; la otra es una biblioteca de C ++, escrito por mí, que utiliza la biblioteca C proporciona mi propia API C ++ en la parte superior.

An (en mi opinión, excesiva) parte de la funcionalidad de la biblioteca de C se implementa en funciones en línea. No se puede evitar la inclusión de las funciones en línea cuando se utiliza la API de la biblioteca C, pero cuando intento de vincular todo junto, estoy recibiendo errores de enlace dicen que hay una definición múltiple de todas las funciones en línea - tanto los que tengo llamada en mi biblioteca de C ++ envoltorio y los que no he, básicamente cualquier cosa definida en línea en los encabezados ha conseguido una función creada por ella tanto en la biblioteca C y la biblioteca de C ++.

No causa varios errores de definición cuando los archivos de inclusión se utilizan varias veces en diferentes archivos .c o .cpp en el mismo proyecto; el problema es que genera una definición por biblioteca.

¿Cómo / por qué se genera el compilador funciones y símbolos para estas funciones en línea, tanto en las bibliotecas? ¿Cómo puedo obligarlo a dejar de generar en mi código? ¿Existe una herramienta que se puede ejecutar para despojar a las funciones duplicadas del archivo .a, o una manera de hacer que el enlazador ignorar múltiples definiciones?

(Para su información, la biblioteca de terceros sí incluye #ifdef __cplusplus y extern guardias "C" en todas sus cabeceras; de todos modos, si ese fuera el problema, no causaría una definición múltiple de símbolo, que podría causar el problema opuesto, porque el símbolo sería indefinido o al menos diferente.)

En particular, los errores de enlace no se producen si me enlace a la DLL de C biblioteca de terceros; Sin embargo después consigo fallos de ejecución extrañas que parecen tener que ver con mi código que tiene su propia versión de las funciones que debería estar llamando desde el archivo DLL. (Como si el compilador está creando versiones locales de funciones que no pidió.)

Las versiones similares de esta pregunta se ha pedido antes, sin embargo, no he encontrado la respuesta a mi situación en cualquiera de los siguientes:

La respuesta a esta pregunta es que el cartel estaba definiendo multiplican Variables , mi problema es múltiple definición de las funciones en línea: repetidos errores de definición de múltiples incluyendo misma cabecera en múltiples CPPS

Este fue un programa de MSVC, pero estoy utilizando MinGW; También, el problema del póster en esta pregunta fue la definición de un constructor de la clase C ++ fuera del cuerpo de la clase en un encabezado, mientras que mi problema es con funciones C que están en línea: estático lib múltiple definición del problema

Este tonto rebautizado todo su código C como archivos de C ++ y su código C no era C ++ - seguro: definición múltiple de un montón de std :: funciones al vincular

Éste fue sólo quieren saber por qué violar la regla de una definición no fue un error: comportamiento impredecible de funciones en línea con diferentes definiciones

¿Fue útil?

Solución

En primer lugar hay que entender el modelo de línea C99 - tal vez hay algo malo en sus cabeceras. Hay dos tipos de definiciones de funciones en línea con enlace externo (no estático)

  • definición externa
    Esta definición de una función sólo puede aparecer una vez en todo el programa, en un TU designado. Proporciona una función exportada que se puede utilizar de otras unidades de formación.

  • definición en línea
    Estos aparecen en todos los TU, donde declaró como una definición por separado. Las definiciones hacen no tienen que ser idénticos entre sí o con la definición externa. Si se usa interna en una biblioteca, que pueden hacer omitir la comprobación de argumentos de la función que de otro modo serían hechas en la definición externa.

Cada definición de la función tiene sus propias variables estáticas locales , debido a que sus declaraciones locales tienen ninguna vinculación (no se comparten como en C ++). Una definición de una función en línea no estático será una definición en línea si

  • Cada declaración de una función en un TU incluye el especificador inline, y
  • No declaración de la función en un TU incluye el especificador extern.

De lo contrario, la definición que debe aparecer en que TU (funciones en línea PORQUE se debe definir en el mismo TU donde declaró) es una definición externa. En una llamada a una función en línea es sin especificar si se utiliza la externa o la definición en línea . Sin embargo, debido a que la función definida en todos los casos sigue siendo la misma (ya que tiene enlace externo), la dirección de la que se compara la igualdad en todos los casos, sin importar cuantas definiciones aparecen en línea. Así que si usted toma la dirección de la función, lo más probable es que el compilador resuelve con la definición externa (especialmente si optimizaciones están desactivados).

Un ejemplo que demuestra un uso incorrecto de inline, ya que incluye una definición externa de una función de dos veces en dos unidades de formación, causando un error de definición múltiple

// included into two TUs
void f(void); // no inline specifier
inline void f(void) { }

El siguiente programa es peligroso, ya que el compilador es libre de utilizar la definición externa, pero el programa no proporciona un

// main.c, only TU of the program
inline void g(void) {
  printf("inline definition\n");
}

int main(void) {
  g(); // could use external definition!
}

he hecho algunos casos de prueba utilizando GCC que demuestran el mecanismo además:

main.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
  printf("inline def main.c\n");
}

// defined in TU of second inline definition
void g(void);

// defined in TU of external definition
void h(void);

int main(void) {
  // unspecified whether external definition is used!
  f();
  g();
  h();

  // will probably use external definition. But since we won't compare
  // the address taken, the compiler can still use the inline definition.
  // To prevent it, i tried and succeeded using "volatile". 
  void (*volatile fp)() = &f;
  fp();
  return 0;
}

main1.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
  printf("inline def main1.c\n");
}

void g(void) {
  f();
}

main2.c

#include <stdio.h>

// external definition!
extern inline void f(void);

inline void f(void) {
  printf("external def\n");
}


void h(void) {
  f(); // calls external def
}

Ahora, el programa da salida a lo que esperábamos!

$ gcc -std=c99 -O2 main.c main1.c main2.c
inline def main.c
inline def main1.c
external def
external def

En cuanto a la tabla de símbolos, veremos que el símbolo de una definición en línea no se exporta (de main1.o), mientras que una definición externa se exporta (de main2.o).


Ahora, si sus bibliotecas estáticas tienen cada uno una definición externa de sus funciones en línea (como debe ser), es natural que en conflicto entre sí. La solución es hacer que las funciones en línea estática o simplemente para cambiar el nombre de ellos. Estos siempre proporcionan definiciones externas (por lo que son completos definiciones de pleno derecho), pero no se exportan porque tienen enlace interno, por lo que no estén en contradicción

static inline void f(void) {
  printf("i'm unique in every TU\n");
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top