Hay una manera de decirle al compilador g++ en mi caso) a no optimizar ciertos código de distancia, incluso si el código no es accesible? Sólo quiero que esos símbolos en el archivo objeto.

Ejemplo: Aquí es una simple función, y quiero que esta función sea compilado, incluso si nunca llama.

void foo(){
  Foo<int> v;
}

Si no hay ningún oficial de la directiva de compilador, hay un truco para hacer que el compilador de pensar que es una función importante? O al menos hacer pensar que no puede ser ignorada con seguridad? He intentado algo como esto:

extern bool bar;
void foo(){
  if(bar){
    Foo<int> v;
  }
}

pero que no parecen hacerlo.

(Si de verdad quieres saber por qué estoy en la tierra que tiene que ver con este pregunta, donde, en lugar de explícito de la plantilla de la creación de instancias con template class Foo<int> simplemente quiero ser capaz de escribir Foo<int> v, ya que en muchos casos eso es más fácil, ya que implícitamente se crea una instancia de todas las funciones necesarias, y funciona bien en modo de depuración sin optimizaciones …)

ACTUALIZACIÓN:

Aquí es lo que yo quiero hacer (como compilable mini ejemplo):

foo.h (estos archivos son para mí y no se puede cambiar)

template<class T>
struct Foo {
  T val_;
  Foo(T val) : val_(val) {
      //heavy code, long compile times
  }
};

foo-instantiation.cpp

#include "foo.h"
void neverCalled() {
  Foo<int> f(1);
}

//The standard way to instantiate it is this:
//template class Foo<int>;
//but in reality it is often hard to find out 
//exactly what types I have to declare.
//Usage like Foo<int> f(1); will instantiate all
//dependent types if necessary.

foo-decl.h (una interfaz que he extraído de foo.h)

template<class T>
struct Foo {
  T val_;
  Foo(T val); //no heavy code, can include anywhere and compile fast
};

main.cpp

#include <iostream>
#include "foo-decl.h"

int main(int argc, char** argv){
  Foo<int> foo(1);
  return 0;
}

Compilación (sin optimización)

g++ -c main.cpp
g++ -c foo-instantiation.cpp
g++ main.o foo-instantiation.oo

Compilación (optimización)

g++ -O2 -c main.cpp
g++ -O2 -c foo-instantiation.cpp
g++ main.o foo-instantiation.oo
main.o(.text+0x13): In function `main':
: undefined reference to `Foo<int>::Foo(int)'
collect2: ld returned 1 exit status
  • Traté de encabezados precompilados en su lugar, pero la ejecución de plantilla de método hace mucho más rápido de compilación.
  • Compilación de foo-instantiation.cpp sin la optimización no es lo ideal, porque entonces el código de la biblioteca (foo.h y otros) se ejecutará más lento.
  • Podría publicar algunos compilable código que ilustra lo que quieres decir?
InformationsquelleAutor Frank | 2009-03-20

7 Comentarios

  1. 7

    Que se están ejecutando en la Definición de la Regla. En un archivo que tiene una definición:

    template<class T>
    struct Foo {
      T val_;
      Foo(T val) : val_(val) {
          //heavy code, long compile times
      }
    };

    y en otro una definición diferente:

    template<class T>
    struct Foo {
      T val_;
      Foo(T val); //no heavy code, can include anywhere and compile fast
    };

    Esto está explícitamente permitido en C++ (sólo una idéntica definición permite) y si se rompe la regla de que el código podría parecer a funcionar a veces, pero lo que realmente tenemos es el temido «un comportamiento indefinido» – cualquier cosa puede ocurrir dependiendo de la fase de la luna (pero es más probable que el estado interno del compilador en ciertos momentos críticos).

    Básicamente, usted no puede escribir código como que lo siento.

    • Creo que tienes razón. Demasiado malo. 🙁
    • dehmann, todavía no he entendido de qué diablos está mejor con su forma de hacerlo que con la creación de instancias explícita. ambos tienen los mismos inconvenientes no? (y el tuyo además es un comportamiento indefinido)
    • No es un ODR problema de infracción. El compilador está indicando claramente que no hay ninguna definición de la clase, no se que hay varios. ODR violaciones de plantillas es un problema difícil, y un comentario no tiene espacio para una descripción completa, pero este no es el caso.
    • En realidad, si usted cambia de foo.h para incluir foo-decl.h y agregar la definición de la función fuera de la clase de cuerpo que funciona bien. (Pero eso no es una opción para mí, ya que no puede cambiar todos mis foo.h-como los archivos, hay demasiados de ellos, y la modificación de ellos es muy peligroso.)
  2. 4

    La compilador no puede optimizar el cuerpo de una función de distancia, si se declara extern o no, porque no sabe que la función no se llama de otra unidad de compilación. Se podría optimizar lejos si usted declaró estática, pero yo don’; t creo que ninguno de los compiladores de hacer esto.

    El compilador puede optimizar la distancia de las llamadas de función:

    while(false) {
      foo();
    }

    En el por encima de la llamada a foo() puede ser elidida.

    OTOH, el enlazador puede quitar la función de los órganos de la final excecutable si ellos no son llamados.

    Por las anteriores razones, y otras, que realmente necesita para ver algunos de los verdaderos código con el fin de diagnosticar su problema.

    • Gracias, he añadido el código, consulte Actualización. Tienes toda la razón que el compilador no puede optimizar la basura ya que no se sabe qué va a ser llamados desde el exterior. Así que no sé por qué la compilación con la optimización da un error, entonces, y w/o optimización está bien.
    • La verdad que sí, el compilador puede ver que Foo<int> v nunca se utiliza y optimizar la distancia.
    • post algunas pruebas de esta afirmación por favor
    • Al compilar sin la optimización, la nm herramienta muestra 3 símbolos en foo-ejemplificación.o: _Z11neverCalledv, _ZN3FooIiEC1Ei, __gxx_de la personalidad_v0. Pero cuando voy a compilar con -O2, no es sólo un símbolo: _Z11neverCalledv. Así que …Foo… no hay más.
    • Ver mi post sobre el ODR
    • El compilador puede saber que la variable local nunca podrá ser utilizada y optimizar la distancia, dejando un vacío cuerpo de la función, que no puede ser eliminado, pero, de nuevo, has perdido la creación de instancias.
    • El enlazador puede optimizar el símbolo de distancia (el intantiation función), pero no a optimizar las instancias de la plantilla de distancia como es en realidad (el enlazador se quejan de la no-definición de la plantilla constructor)

  3. 2

    búsqueda de la documentación en el tema de #pragma. Este es definir una especie de escotilla de escape que le permite especificar todo tipo de propiedades. gcc apoya, así que hay una buena apuesta que g++ será así. Se advierte que es probable que estos no portátil que puede o no puede ser motivo de preocupación para su proyecto.

    • Estos no son estándar. #pragma es un precompilador de palabras clave para cambiar los detalles de la implementación del compilador. Si usted depende de #pragma usted va a perder la portabilidad.
    • He mencionado que probablemente no portátil y para algunos de los proyectos que esto no es una preocupación que yo sé de esto y me dijo como tal. Me dejó a la pregunta proponente para decidir si este es un medio que es apropiado para su proyecto
  4. 2

    El compilador es la optimización de lejos una variable que nunca se utiliza, se puede optimizar una función sobre la base de que no va a ser utilizado, ya que podrían utilizarse de otra unidad de compilación. Usted podría tratar de forzar al compilador a considerar la variable como se utiliza con algo parecido a:

    void instantiation()
    {
       Foo<int> f;
       f; //mark the variable as if it is used.
    }
    
    //or:
    Foo<int>* instantiation()
    {
       Foo<int> *p = new Foo<int>();
       return p; //The compiler cannot know if p will be used, it must compile
    }

    Una mejor solución sería explícitamente crear una instancia de la plantilla si usted lo desea:

    //.h
    template <typename T>
    class Foo
    {
    public:
       Foo( T const & value );
       void set( T const & ); //whatever else
    private:
       T value_;
    };
    
    //template implementation another file, not included from .h
    //instantiation.cpp??
    template <typename T>
    Foo<T>::Foo<T>( T const & value ) : value_(value) {}
    
    template <typename T>
    void Foo<T>::set( T const & v )
    {
       value_ = value;
    }
    
    //explicit instantiation
    template class Foo<int>;
    template class Foo<double>;
    
    //test.cpp
    #include "header.h"
    int main()
    {
        Foo<int> f(5);
        f.set( 7 );
    
        Foo<char> f2; //linker error Foo<char>() not defined
    }

    Código de usuario sólo podrá ver el encabezado y saber qué métodos existen, pero no la implementación real. La aplicación se compila en una unidad de compilación, donde el explícito ejecución de plantilla se produce.

    Tenga en cuenta que si se te olvida explícitamente crear una instancia de un tipo, va a ser un error del vinculador, no un error de compilación.

    La Definición De La Regla De

    La definición de la regla en c++ los estados de que sólo puede haber una definición para cada símbolo o de la clase. Tener múltiples definiciones puede ser detectado fácilmente por símbolos regulares (si se definen dos void f() { } el vinculador detectar la duplicación de símbolo), pero es un poco más complicado que con las plantillas. Con las plantillas es más complicado, ya que suelen ser declarada y definida en los archivos de encabezado. El compilador genera los símbolos utilizados en cada unidad de compilación [1] y el enlazador generalmente se encuentra más de un símbolo equivalente ( std::vector::push_back() se compila en cada unidad de compilación que tiene un std::vector y llamadas push_back)

    El compilador banderas de la plantilla de código como ‘débil’ símbolo, lo que significa que mientras que el símbolo se define aquí, también puede ser definido de otra unidad de compilación y el enlazador es libre para descartar el símbolo sin ceder un error de vínculo. Que es un requisito si usted desea vincular las diferentes unidades de compilación que hacer uso de la misma STL instalaciones, por ejemplo, con los mismos tipos.

    Hasta gcc 4.2, gcc de linux enlazador descarta todos, pero uno de los débiles de símbolos sin más comprobaciones. Algunos conectores (gcc vinculador en linux en el futuro cercano, no como la 4.2, no sé 4.3 ni 4.4 podría ser todavia existen) revise que los diferentes ‘débil’ símbolos son en realidad la misma y proporcionar un error/advertencia para el usuario.

    Su código es romper la ODR en que se volviéndola a la plantilla en un lugar diferente. Usted debería declarar la plantilla de una vez y al implementar los métodos externamente como se publicó anteriormente. De todos modos si ambas definiciones son compatibles (como en el fragmento de código que has publicado): todos los métodos y atributos son exactamente los mismos y con la misma calificadores (virtual/const-ness…) debe ser aceptada por el compilador como hay una sola definición (por desgracia se repite) de la plantilla.

    [1] Sólo aquellos métodos que realmente son llamados en el código se compilará:

    template <typename T>
    struct Test
    {
       void f() { std::cout << "f()" << std::endl; }
       void g() { std::cout << "g()" << std::endl; }
    };
    int main()
    {
       Test<int> t;
       t.f(); //compiler generates Test<int>::f, but not Test<int>::g
    }
  5. 1

    Esto se hace generalmente por una directiva de compilador. En C será un #pragma, y en Delphi Pascal es {$O- }, {$O+} todo el código en cuestión. La sintaxis precisa y el método es de aplicación específica, por lo que es una cuestión de revisar la documentación para que sea cual sea el sistema que está utilizando.

    No la optimización de una función de distancia es bastante sencillo, pero una vez o dos veces he visto casos en los que ha sido necesario para indicarle al compilador no para optimizar código específico. Este es extremadamente rara y no es algo que me he encontrado por un tiempo muy largo, pero puede ocurrir de vez en cuando. Los casos donde se hace generalmente cuando uno está compilando en contra de algunos de los antiguos código heredado que fue construida antes de un desarrollo posterior de la cpu en la tecnología hyperthreading de ser un caso clásico en el punto.

  6. 0

    Mano, no estoy seguro. Tal vez Encabezados precompilados va a resolver este problema?

    Sólo para modificar esto un poco, obviamente esto no va a ayudar con el problema de ser capaz de utilizar los más pequeños de la plantilla de encabezado en el código, pero podría ayudar con el tiempo de compilación problema (y por lo tanto eliminar la necesidad de la plantilla).

    • El compilador no puede ‘precompilar’ plantillas sin saber a qué tipo va a ser definido por.
    • En realidad, puede precompilar» de ella. «Precompilar» es un poco de un nombre inapropiado, sin embargo, es más como, «pre-analizar y evaluar» que compila en realidad. El compilador puede todavía hacer todo el trabajo de traducir el código de la plantilla en su propia representación interna de una plantilla.
  7. 0

    Declarar la variable como volátil:

    volatile Foo<int> v;

    Por lo general, se evita que cualquier optimización. Yo he comprobado con Intel Compilador de C++ y Microsoft Visual Studio 2008.

Dejar respuesta

Please enter your comment!
Please enter your name here