2012-03-17 12 views
6

Escribo un kernel pequeño con C++ 11 y tengo dos instancias con el mismo tipo que deben construirse antes de que se creen otros objetos estáticos.Controlar el orden del constructor de objetos estáticos

El código que he escrito es el siguiente:

// test.hpp 
class test { 
    // blahblah... 
}; 

// test.cpp 
typedef char fake_inst[sizeof(test)] __attribute__((aligned(alignof(test)))); 

fake_inst inst1; 
fake_inst inst2; 

// main.cpp 
extern test inst1; 
extern test inst2; 

int kmain() { 
    // copy data section 

    // initialize bss section 

    new (&inst1) test(); 
    new (&inst2) test(); 

    // call constructors in .init_array 

    // kernel stuffs 
} 

Se construye y funciona como se esperaba, sin ningún mensaje de aviso, pero no con LTO.

Recibo un montón de mensajes de advertencia quejándose de la coincidencia de tipos y me pregunto si hay una solución, ya que me confunde para encontrar los otros mensajes de advertencia o error 'reales'.

¿Alguna sugerencia?

+0

¿Qué estás * realmente * tratando de hacer? En los comentarios a la respuesta de Pubby a continuación, dice que desea inicializar varios objetos del mismo tipo. Para mí, parece que le gustaría inicializar estructuras relacionadas con el soporte de C++, como el montón, antes de dejar que la implementación llame a otros constructores. Pero eso no suena como lo que estás intentando. Además, ¿de qué tipo de "coincidencia de tipos" obtienes advertencias? – Potatoswatter

+0

@Potatoswatter una gran cantidad de 'advertencia: el tipo de 'xxx' no coincide con la declaración original [habilitada por defecto]' cosas. Es porque el tipo real no es prueba sino char []. Pensé que podría desactivar el constructor estático pero parece que no hay forma de hacerlo. – kukyakya

+0

Ah, ya veo. Lo que debes hacer es 'char fake_inst1 [sizeof (test)]; test * const inst1 = reinterpet_cast < test * > (fake_inst1); ' – Potatoswatter

Respuesta

0

Quizás esto?

// ... .h 
template<typename T> 
union FakeUnion { 
    FakeUnion() {} 
    ~FakeUnion() {} 

    T inst; 
}; 

extern FakeUnion<test> inst1_; 
extern FakeUnion<test> inst2_; 
static constexpr test& inst1 = inst1_.inst; 
static constexpr test& inst2 = inst2_.inst; 
// ... .h end 

// ... .cpp 
FakeUnion<test> inst1_; 
FakeUnion<test> inst2_; 
// ... .cpp end 

Dentro main, a continuación, puede decir new (&inst1) test;. Ya no debería dar más advertencias sobre las violaciones de inconsistencia de tipo, porque a diferencia de su código, este código no contiene variables que tengan diferentes tipos en diferentes archivos.

+0

Si bien esto resuelve el conflicto de nombre, no asegura que el constructor de 'inst1' y' inst2' se invoque antes que cualquier otro. –

+0

@Matt si los llama en 'main' con la ubicación nueva, lo hace así si todos los demás ctors son llamados después de' main' de forma controlada. –

+0

¿Puede aconsejarme sobre qué está pasando "mágicamente" aquí? Es decir. ¿Por qué necesitamos una unión y por qué la unión tiene ctor/dtor? – Farcaller

1

¿Podría usar el atributo init_priority de GCC?

Some_Class A __attribute__ ((init_priority (2000))); 
Some_Class B __attribute__ ((init_priority (543))); 

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

+0

He leído sobre eso, pero es difícil administrar el orden de los constructores ya que las cosas de prioridad se extienden sobre los archivos de origen. – kukyakya

+0

@kukyakya entonces, ¿el problema es inicializar los dos objetos primero u ordenar todos los constructores globales en general? – Potatoswatter

+0

@Potatoswatter En realidad, no se trata solo de dos instancias, sino de más de una instancia para el mismo tipo, por lo que no puedo usar el patrón singleton. El orden de construcción de los otros objetos estáticos no importa. – kukyakya

0
// in an .h file 

typedef char fake_inst[sizeof(test)] __attribute__((aligned(__alignof__(test)))); 

extern fake_inst fake_inst1; 
extern fake_inst fake_inst2; 

inline test& f_inst1() { return *reinterpret_cast<test*>(fake_inst1); } 
inline test& f_inst2() { return *reinterpret_cast<test*>(fake_inst2); } 

// and for readability 

static test& inst1 (f_inst1()); 
static test& inst2 (f_inst2()); 

suerte tanto inst1 y f_inst1() se optimizará a cabo.

+0

Esta es exactamente la forma en que estoy usando, pero me pregunto si hay una manera más ordenada. Quiero objetos globales como 'std :: cout' o' std :: cin' porque parece más legible que la función que devuelve la instancia. – kukyakya

+0

Ver la versión actualizada. –

+0

tal vez intente con una variable de referencia global, inicializada en el intervalo de 'inst1'? Esto también está optimizado (en msvc) – valdo

2

C++ no le ofrece formas de administrar el orden de inicialización de objetos globales en varios archivos. Si necesita administrar el orden de inicialización de estos objetos tan fuertemente, le aconsejaría encarecidamente que no los convierta en objetos globales. Conviértelos en funciones globales que contengan objetos estáticos y devuélvanles punteros.

Pero incluso entonces, eso es menos peligroso que la inicialización manual completa. Simplemente haga algunos consejos sobre aquellos objetos disponibles para quienes los necesitan (preferiblemente no globalmente), y estará bien.

+0

Consideré usar funciones globales devolviendo la referencia o puntero a objetos estáticos de alcance de función pero el compilador genera cosas de adquisición/liberación para crear la instancia y las materias de adquisición/liberación se implementan con mutex de kernel, que no se puede usar en el kernel inicialización – kukyakya

+0

@kukyakya: Entonces vas a tener que inicializar estos objetos manualmente. Esa es la única manera de estar seguro sin mutexes de kernel. –

Cuestiones relacionadas