Pregunta

    

Esta pregunta ya tiene una respuesta aquí:

         

Me acaba de terminar el trabajo en un C ++ - programa en el que he aplicado mis propias excepciones (aunque derivado de std :: excepción). La práctica he aplicado cuando una excepción provoca una reacción en cadena, propagando el error hacia arriba y dando lugar a otras excepciones, es concatenar el mensaje de error en cada etapa adecuada, dentro de los módulos (leer clases). Es decir. la edad en sí excepción se elimina y se crea una nueva excepción, pero con un mensaje de error más tiempo.

Esto puede haber trabajado para mi pequeño programa, pero no estaba muy satisfecho con mi enfoque en el final. Por un lado, los números de línea (aunque no aplicados en el momento) y los nombres de archivo no se conservan a excepción de la última excepción; y realmente que la información es de mayor interés en la primera excepción.

Me imagino que esto podría haber llevado mejor encadenando excepciones juntos; es decir, la edad excepción se proporciona en el constructor de la nueva excepción. Pero la forma en que se pondría en práctica? Excepciones no mueren cuando salen del alcance del método, lo que impide un uso de punteros de excepción? Y cómo copiar y almacenar la excepción si la excepción puede ser de cualquier clase derivada?

En última instancia, me llevan a considerar si el encadenamiento de excepciones en C ++ es una idea tan buena después de todo. Quizás uno simplemente debe crear una excepción y luego añadir datos adicionales a que (como si hubiera estado haciendo, pero probablemente de una manera mucho mejor)?

¿Cuál es su respuesta a esto? Las excepciones deben causados ??por otro ser encadenados juntos para conservar una especie de "huella excepción" - y cómo deben ser implementadas que? - o se debe utilizar una sola excepción y datos adicionales que se le atribuye -? Y cómo debe ser hecho de que

¿Fue útil?

Solución

Es necesario copiar los datos de un objeto de excepción, en una cadena, si usted quiere que sobreviva el bloque catch que lo recibe, además de volver a lanzar por throw;. (Que incluye, por ejemplo, si que las salidas de bloque catch a través de un throw obj;.)

Esto se puede hacer poniendo los datos para ser salvo en el montón, y la implementación de swap (move en C ++ 0x) sobre sus datos privados dentro de la excepción, por ejemplo.

Por supuesto, hay que tener cuidado cuando se utiliza el montón con excepciones ... pero por otra parte, en la mayoría de los sistemas operativos modernos, en exceso la memoria impide totalmente new de tirar cada vez, para bien o para mal. Un margen de memoria buena y soltando excepciones de la cadena tras la fusión completa debe mantener su seguridad.

struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;
            throw;
        } catch ( chained_exception &prev ) {
            swap( *link, prev );
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

    virtual char const *what() const throw() { return descr.c_str(); }

    virtual ~chained_exception() throw() {
        if ( link && link->link ) delete link; // do not delete terminator
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                std::cerr << ep->what() << std::endl;
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            std::cerr << ep->what() << std::endl;
        }
    }
}

int main() try {
    throw chained_exception(); // create dummy end-of-chain
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

salida (apropiadamente gruñón):

bah
humbug!
meh!

Otros consejos

Dado que esta pregunta se ha hecho cambios notables se han hecho de la norma con C ++ 11. Estoy continuamente perdiendo esta en los debates sobre excepciones, pero el enfoque siguiente, las excepciones de anidación, el truco:

std::nested_exception y std::throw_with_nested

Se describe en StackOverflow aquí y aquí , cómo se puede conseguir una traza en sus excepciones dentro de su código sin necesidad de un depurador o engorroso de registro, simplemente escribiendo un manejador de excepción apropiado que volver a lanzar excepciones anidadas .

Ya que se puede hacer esto con cualquier clase de excepción derivada, se puede añadir una gran cantidad de información a la traza tales! También puede echar un vistazo a mi EPM en GitHub , donde un trazado inverso sería algo de esta manera:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

Es posible que desee ver en esto: http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/boost-exception.html

Es enfoque algo diferente a lo que hizo en MS C #, pero parece que para satisfacer sus necesidades.

Otra idea es agregar los datos relevantes a su objeto de excepción a continuación, utilizar una declaración throw; desnuda para volver a lanzarla. Creo que la información de la pila se mantiene en este caso, y por lo que aún sabrá la fuente original de la excepción, pero las pruebas sería una buena idea.

apuesto desde cualquier pila si o no se dispone de información en absoluto se define la aplicación, que las implementaciones pueden variar aún más ampliamente en si o no se mantiene en ninguna manera después de una declaración throw; desnudo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top