2011-07-05 17 views
10

Al trabajar con C# o Java, solía crear clases de excepciones que incluían otras excepciones como miembros de la clase. Por ejemplo:Excepciones dentro de excepciones en C++

public class MyException : Exception { 
    private MyException ex; 
    private String message; 

    public String what() { 
     return this.message; 
    } 

    public String stack() { 
     if(ex != null) { 
      StringBuilder s; 
      s.append(this.what()); 
      s.append(this.ex.stack()); 
      return s.toString(); 
     } 

     return this.what(); 
    } 
} 

que estaba buscando ejemplos sobre el mismo tema, pero para C++ y no pude encontrar ninguna (tal vez no lo hice búsqueda de los términos adecuados, como se puede ver el título de esta pregunta no es muy elegante).

De todos modos, ¿cuál es la forma correcta de hacerlo en C++? ¿Es para almacenar la excepción interna como un puntero o una referencia? (Creo que podría necesitar un puntero para que pueda ser nulo cuando sea la primera excepción). Cuando presento la excepción, ¿debería ser como un puntero creado con new?

EDITAR: Tal vez lo que escribí fue un poco confuso o no una práctica de programación bien conocida (aceptada). Así que voy a especificar cómo tenía la intención de utilizar esta clase con un fragmento:

try { 
    // Code that throws an exception of type MyException 
} catch(MyException ex) { 
    MyException another = new MyException(); 
    another.setEx(ex); 
    another.setMessage("A message that explains where the other exception was caught and on what conditions"); 

    throw another; 
} 
+3

no puede tener múltiples excepciones en C++. Si se lanza una nueva excepción antes de capturar la excepción actual, el programa finaliza (más precisamente se llama a 'std :: terminate') –

+0

@Gene: Esta es una variación en re-lanzamiento, donde la excepción existente se rellena dentro de una nueva excepción . Probablemente no sea factible sin recolección de basura. –

+0

@Ben Voigt: Probablemente factible sin recolección de basura si no fuera tan difícil recordar exactamente cuándo se llaman a los destructores. –

Respuesta

0

No creo que necesitamos una propiedad de instancia de clase (es decir, ex) en Java/C#/C++ si la clase no está diseñado en patrón singleton. Además, here es un tutorial que es posible que desee echar un vistazo.

+0

El miembro de la clase 'MyException' se usa así: 1. Capté la excepción lanzada 2. Represento ** otra ** excepción con un mensaje más específico y establezco la propiedad' ex' en la excepción capturada en 1 3. Lanza la nueva excepción creada. Sin embargo, es un buen tutorial, el que me enviaste. – Renan

+0

Parece que quería conservarlo como referencia, ¿verdad? ¿Por qué entonces no es una instancia de 'Excepción'? Además, si solo quiere una referencia para mantener la excepción original, ¿por qué no simplemente apila los mensajes de rastreo y descarta la excepción original? Probablemente en el constructor? Me parece más razón. Mis 2 centavos – shinkou

21

No hay una forma correcta de hacerlo con las excepciones estándar en C++ 03 porque están diseñadas para ser utilizadas polimórficamente pero no pueden ser clonadas. Por lo tanto, si captura std::exception const& e, podría almacenar una copia, pero esto dará lugar a rebanar, perdiendo toda la información útil. Debe no almacenar un puntero o referencia a la excepción porque su tiempo de vida llegará a su fin tan pronto como salga de la cláusula catch (suponiendo que no vuelve a lanzar la excepción original).

Puede evitar esta limitación si conoce todos y cada uno de los tipos que se pueden arrojar y probarlos, pero no es un diseño agradable (es decir, subvierte el polimorfismo). Tiene más sentido escribir una clase de excepción base propia que pueda clonarse y captar eso. Todavía tienes el problema si tomas un std::exception que vino del código de otra persona sin embargo.

En este punto, me siento obligado a mencionar Boost.Exception. Hace que sea más fácil escribir su propia jerarquía de excepciones y proporciona varias utilidades, entre ellas boost::exception_ptr. A continuación, puede hacer:

typedef boost::error_info<struct tag_nested_exception, boost::exception_ptr> 
    nested_exception; 

// ... 
catch(...) { 
    // better: use BOOST_THROW_EXCEPTION 
    throw your_exception_type() << nested_exception(boost::current_exception()); 
} 

Esto es tan útil que boost::diagnostic_info lo apoya y mostrará la excepción anidada para usted (que es indocumentado). Incluso se ha sugerido que este nested_exceptiontypedef debería ser parte de la biblioteca; mientras tanto, es fácil escribirlo tú mismo.

No espere magia: boost::current_exception 'captura' la excepción activa (letra pequeña: o una copia de ella) bien solo si el sitio de lanzamiento utilizó boost::enable_current_exception. (Funcionalmente, este es el equivalente moral de usar una clase de excepción base que se puede clonar). De lo contrario, no fallará, pero se perderá parte de la información.


Como nota final, sabemos que el diseño de Boost.Exception se adoptó para C++ 0x.Por lo tanto el siguiente correctamente almacena la excepción activo, sin que ninguno de los boost::current_exception advertencias ya que cuenta con soporte de idiomas:

// you can still use Boost.Exception: 
typedef boost::error_info<struct tag_nested_exception, std::exception_ptr> 
    nested_exception; 

// ... 
catch(...) { 
    // e has type std::exception_ptr 
    auto e = std::current_exception(); 
    // internally store the std::exception_ptr 
    throw your_exception_type(e); 

    // or with Boost 
    BOOST_THROW_EXCEPTION(your_exception_type() << nested_exception(e)); 
} 

Hay también un tipo std::nested_exception que puede ser fácilmente utilizado de esta manera:

catch(...) { 
    // throws an unspecified type derived from your_exception_type 
    // and std::nested_exception 
    std::throw_with_nested(your_exception_type()); 
} 
+0

gracias por el heads-up, C++ 0x me mantiene sorprendiendo :) –

1

Supongo que podría lanzar un unique_ptr<some_exception>, luego hacer inner_exception otro unique_ptr que tome posesión.

+0

Y usted tiene que asegurarse de que sólo los punteros a la misma clase base se tira porque esto no se comporta polimórfica. –

+0

@Luc: Eso sin duda es un inconveniente, ¿no? –

+0

Bueno, dado que 'std :: unique_ptr' es C++ 0x, creo que hay mejores alternativas. –