2012-06-18 19 views
12

Imagine que tiene un archivo ahg ++, requieren la advertencia enlazador/error para la especialización múltiple plantilla

#include <iostream> 

template<typename T> struct A{ 
    int magic; 
    A():magic(1234){} 
    void f(){std::cout<<"default f"<<magic<<std::endl;} 
}; 


void f(A<int>* a); 

entonces la función f se define en "a.cpp"

#include "a.h" 
void f(A<int>* a){ 
    a->f(); 
} 

y, finalmente, " main.cpp" se especializa la plantilla, y luego utiliza f

#include "a.h" 
template<> struct A<int>{ 
}; 

int main(){ 
    A<int> a; 
    f(&a); 

} 

Claramente el compilador utiliza la versión no especializado para Ao, y el versión especializada para main.o, es decir, hay dos implementaciones diferentes de A. En ejecución, f solo puede imprimir basura/segfault porque el objeto pasado tiene una estructura diferente de la esperada.

¿Hay alguna manera de hacer que el enlazador advierta que hay dos versiones de A?

+0

Es un problema bastante difícil de resolver, por lo que no hay un diagnóstico obligatorio para esto. – Flexo

+0

HTH: elimine todas las plantillas de la pregunta, y obtendrá el mismo resultado. Deje solo la declaración de avance en a.h de struct A; y mover la versión completa a a.cpp. main.cpp - sólo eliminar la plantilla ... – PiotrNycz

Respuesta

3

La razón de Oro no advierte acerca de esto es que el oro sólo detecta símbolo desajustes (el mismo símbolo que se definen en múltiples archivos de objetos en formas incompatibles), y no hay tal falta de coincidencia en el ejemplo.

Ejecutar el ejemplo bajo Valgrind sí produce este error aunque:

valgrind --track-origins=yes ./a.out 

==11004== Memcheck, a memory error detector 
==11004== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==11004== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info 
==11004== Command: ./a.out 
==11004== 
==11004== Conditional jump or move depends on uninitialised value(s) 
==11004== at 0x40B6D24: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16) 
==11004== by 0x40B703C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16) 
==11004== by 0x40C26DE: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib64/libstdc++.so.6.0.16) 
==11004== by 0x40094F: A<int>::f() (a.h:6) 
==11004== by 0x4008CB: f(A<int>*) (a.cpp:3) 
==11004== by 0x400977: main (main.cpp:7) 
==11004== Uninitialised value was created by a stack allocation 
==11004== at 0x400964: main (main.cpp:5) 

usted debe conseguir aún mejor reporte de Address Sanitizer:

Actualización:

La cuestión es que me gustaría para detectar el error en el tiempo de enlace, no durante la ejecución.

Entiendo su punto, pero actualmente no es posible para el compilador (no tiene información sobre otras unidades de traducción) o el enlazador (no tiene información sobre los tipos involucrados) para advertirle sobre esto .

Ahora, para una compilación de depuración, el enlazador podría en teoría hacer esto, si para cada función también comparó la información de depuración para tipos de parámetros. Sugiero presentar una solicitud de función para el oro en bugzilla.

+1

Gracias. De hecho descubrí que esta situación estaba sucediendo en mi código usando valgrind, y luego obtuve este ejemplo de juguete. El punto es que me gustaría detectar el error en el tiempo de enlace, no durante la ejecución. –

1

El enlazador oro podría dar una advertencia con --detect-ODR-violaciónes

Funciona mediante la comparación del archivo y número de línea para cada definición de plantilla y de advertencia si no son todos iguales.

+0

gracias, pero ld -versión GNU (GNU Binutils oro para Ubuntu 1.11) 2.21.53.20110810 y g ++ -o main.cpp prueba a.cpp -Wl, --detect-odr-violaciones -Wall no da advertencias –

+0

Utiliza información de depuración así que intente agregar -g –

+0

Gracias. todavía g ++ -g -c main.cpp g ++ -g -c a.cpp g ++ -g -o prueba main.o ao -Wl, - Detect-ODR-violaciónes -Wall da ninguna advertencia mismo para g ++ -g -o test main.cpp a.cpp -Wl, - detect-odr-violaciones -Wall –

1

Creo que la respuesta es "no" y va a seguir así.

Los tipos solo tienen nombres que el vinculador ve cuando aparecen en parámetros de funciones o argumentos de plantilla (¿algunos otros bichos raros? Tal vez). Su ejemplo es en realidad uno de los casos más fáciles, y para detectar que el enlazador debería estar trabajando con un ABI que (en efecto) marca argumentos de plantilla suministrados por una especialización. Pero no pueden hacer eso: tienes que poder pasar un puntero a una estructura con plantilla sin saber si está apuntando a una especialización.

Incluso entonces, no puede obtener cambios ABI mucho más radicales que incluso triviales, significa al menos considerar si necesita volver a compilar y/o volver a vincular cada biblioteca y ejecutable.Si tus estructuras fueran miembros struct trojan { A<int> greeks; }, tendrías idénticos nombres de tipo de todos modos, y si nunca aparecieron como parámetros de función o argumentos de plantilla, el vinculador nunca los vería aunque fueran diferentes.

Para obtener la detección automática, comenzaría con un frontend OSS C++ accesible como clang. Necesitarás reglas (no estándar) de creación de nombres que marquen nombres de plantilla-especialización-argumento y hagan que genere listas de banda lateral de todas las declaraciones de plantilla a las que hace referencia. Luego, escriba una herramienta por separado que examine las listas de todos los objetos que están siendo vinculados y se queje si encuentra un nombre + argumentos usados ​​(no solo referenciados o declarados) en un objeto que también se usa en otro pero de una especialización diferente.

Cuestiones relacionadas