2010-02-24 37 views
7

tengo una clase con un miembro estático:Cómo coger excepción lanzada al inicializar un miembro estático

class MyClass 
{ 
public: 
    static const SomeOtherClass myVariable; 
}; 

Qué inicializo en el CPP archivo de este modo:

const SomeOtherClass MyClass::myVariable(SomeFunction()); 

El problema es, SomeFunction() lee un valor del registro. Si esa clave de registro no existe, arroja una excepción. Esto hace que mi programa explote sin dar al usuario ningún resultado útil ... ¿hay alguna manera de que pueda detectar la excepción para que pueda iniciar sesión?

Respuesta

6

no me gusta mucho static miembros de datos, el problema de la inicialización de ser lo más importante.

Cada vez que tengo que hacer un procesamiento significativo, que engañar y utilizar un local de static lugar:

class MyClass 
{ 
public: 
    static const SomeOtherClass& myVariable(); 
}; 

const SomeOtherClass& MyClass::myVariable() 
{ 
    static const SomeOtherClass MyVariable(someOtherFunction()); 
    return MyVariable; 
} 

De esta manera, la excepción se tiran solamente en el primer uso, y sin embargo, el objeto se const.

Esta es una expresión bastante poderosa para retrasar la ejecución.Tenía una pequeña sobrecarga (básicamente, el compilador comprueba una bandera cada vez que entra en el método), pero es mejor preocuparse por la corrección en primer lugar;)

Si esto se llama desde varios subprocesos:

  • si sus manijas del compilador que, bien
  • si su compilador no lo hace, es posible que pueda utilizar el almacenamiento local de hilo (que de todos modos const)
  • usted podría utilizar boost::once en la biblioteca Boost.Threads
  • ya que es const, que ma Y no importa si es inicializado varias veces, a menos que someOtherFunction no admite la ejecución en paralelo (cuidado de los recursos)

Pauta: utilice únicamente static o global las variables de instancias de objetos simples (que no se puede tirar), de lo contrario usar local static variables para retrasar la ejecución hasta que pueda capturar las excepciones resultantes.

+0

simplemente porque es const no significa que no le importe que se inicialice varias veces. Podría ser acceder a un recurso para el que realmente solo quieres dar el golpe una vez. SomeOtherFunction también podría tomar mucho tiempo para ejecutarse. En ese caso, probablemente no quieras que se ejecute antes de que main se ejecute de todos modos. – Eld

+0

Es cierto, solo quería señalar que puede no ser necesario preocuparse por la sincronización: 'const' significa que nunca cambiará una vez inicializada, mientras que si no fuera así (piense en un contador, por ejemplo), restablecer después de que es utilizado podría arruinar las cosas. Por supuesto, le corresponde al Oficial de Operaciones sopesar los riesgos a mano ... Editaré la respuesta para aclarar este último punto. –

+0

Estoy bastante seguro de que mi código no tiene problemas de enhebrado con esto ... debe inicializarse antes de que se inicien los hilos adicionales. Implementé esta solución y parece resolver el problema. ¡Gracias! – rmeador

5

Quizás lo mejor que se puede hacer es agregar la clave de registro a una lista en lugar de buscarla, y tan pronto como ingrese main(), revise y busque todas las claves de la lista. No quiero ser un predicador, pero situaciones como esta son exactamente las razones por las que generalmente es una mala idea hacer un procesamiento significativo antes de ingresar a main().

+0

el problema con esto es que mi objeto es una constante ... debe _se_ inicializar en ese momento. a menos que use const_cast ... – rmeador

+0

Cierto, pasé por alto eso. Sin duda, podría hacer que los objetos globales punteros e inicializarlos dentro de la principal, pero esto podría implicar una gran cantidad de cambios de código. Es académico en este momento ... las personas han sugerido una solución práctica en otras respuestas y mi único consejo es cómo evitar el comportamiento pre-main(). Sin embargo, eliminar ese comportamiento puede no ser necesario (aunque también podría evitar otros problemas). –

+0

@MSN, lo siento, no estoy seguro de lo que quieres decir ... ¿qué tiene que ver iostream con la pregunta o respuesta? –

0

Puede ajustar la función dentro de otra función que detecta la excepción y alerta al usuario del problema (o crea la clave con un valor predeterminado seguro)

5

Claro - envolver SomeFunction() en una función como:

int static_error; 

void SomeFunctionWrapper() { 
    try { 
     SomeFunction(); 
    } 
    catch(...) { // something more specific if possible 
     static_error = 1; 
    } 
} 

a continuación, en la entrada al principal, tendrá que comprobar para static_error != 0 e imprimir un mensaje de error apropiado si es necesario (por desgracia, no se puede saber si std::cerr existe todavía en su gestor de excepciones, por lo que si desea imprimir a partir de ahí, tendrás que hacer algo como la salida basada en C FILE *).

+0

@Jerry, según el estándar de C++ (whee, me encanta decir que) 27.4 subsección 2, los constructores de objetos estáticos y los destructores pueden acceder a los objetos globales iostream. – MSN

+0

@MSN: (en realidad §27. ** 3 **/2). Por alguna razón, nunca antes había notado esa nota a pie de página. Obviamente, la intención está ahí, pero no veo ningún lenguaje normativo que realmente lo garantice. En cualquier caso, su punto es probablemente el correcto: el código que tengo arriba es demasiado paranoico. –

0

Puede crear una clase contenedora que retrase la construcción del objeto. Luego, cuando se utiliza por primera vez, se lanzará donde sea que se use por primera vez, si el constructor lanza.

Esto tiene la ventaja de no tener mucho código ejecutado antes de que se llame a main(), y si realmente nunca usa el objeto global, nunca se inicializará.

El código:

#include <boost/bind.hpp> 
#include <boost/function.hpp> 
#include <boost/scoped_ptr.hpp> 
#include <boost/thread/once.hpp> 
#include <iostream> 

const boost::once_flag DEFAULT_ONCE_FLAG = BOOST_ONCE_INIT; 
template <typename T> 
class DelayedConstruction { 
    public: 
    DelayedConstruction(boost::function<T* (void) > const & init = &DelayedConstruction::default_initializer) : 
    m_initializer(init), m_flag(DEFAULT_ONCE_FLAG) { } 

    T const & operator*() const { 
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this)) ; 
    if (! m_object) 
     throw std::runtime_error("Object could not be initialized") ; 
    return *m_object ; 
    } 
    T const * operator->() const { 
    boost::call_once(m_flag, boost::bind(&DelayedConstruction::initialize, this)) ; 
    if (! m_object) 
     throw std::runtime_error("Object could not be initialized") ; 
    return m_object.get() ; 
    } 
    static T* default_initializer() { return new T; } 
    private: 
    void initialize() const { 
    m_object.reset(m_initializer()) ; 
    } 
    boost::function<T* (void) > m_initializer ; 
    mutable boost::scoped_ptr<T> m_object ; 
    mutable boost::once_flag m_flag ; 
}; 

struct Foo { 
    Foo(int x = 0) : m_x(x) { 
    if (x == 1) throw std::runtime_error("Can't be 1") ; 
    } 
    int m_x ; 
} ; 

Foo* make_custom_foo() { 
    return new Foo(1) ; 
} 

DelayedConstruction< const Foo > g_myFoo ; 
DelayedConstruction< const Foo > g_anotherFoo(&::make_custom_foo) ; 

int main() { 

    try { 
    std::cout << "My Foo: " << g_myFoo->m_x << std::endl ; 
    std::cout << "Another Foo: " << g_anotherFoo->m_x << std::endl ; 
    } catch (std::runtime_error const & e) { 
    std::cout << "ERROR: " << e.what() << std::endl ; 
    } 

    return 0 ; 
} 

Imprime:

My Foo: 0 
ERROR: Can't be 1 
Cuestiones relacionadas