c++ c++11 raii loki scopeguard

c++ - ¿Por qué no puede Alexandrescu usar std:: uncaught_exception() para implementar SCOPE_FAIL en ScopeGuard11?



c++11 raii (1)

Esta pregunta ya tiene una respuesta aquí:

Sin duda, muchas personas están familiarizadas con la plantilla ScopeGuard del Sr. Alexandrescus (ahora parte de Loki) y la nueva versión ScopeGuard11 que se presenta aquí: http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C

con la fuente aquí: https://gist.github.com/KindDragon/4650442

En su charla en c ++ y después de 2012, mencionó que no podía encontrar una manera de detectar correctamente si se estaba saliendo del alcance debido a una excepción. Por lo tanto, no pudo implementar una macro SCOPE_FAIL que ejecutaría la lambda provista (usualmente utilizada para el código de retroceso) si y solo si el alcance se cerrara debido a una excepción. Esto haría innecesaria la función miembro de despedir () y haría que el código sea más legible.

Ya que no soy de ninguna manera tan genial o experimentado como el Sr. Alexandrescu, espero que la implementación de SCOPE_FAIL no sea tan fácil como esto:

~ScopeGuard11(){ //destructor if(std::uncaught_exception()){ //if we are exiting because of an exception f_(); //execute the functor } //otherwise do nothing }

Mi pregunta es ¿por qué no?


Con una clase de ScopeGuard11 que tiene su destructor, se puede llamar al miembro f_ , incluso si no es el ámbito actual (que se supone que está protegido por la guardia) que se está saliendo debido a una excepción. El uso de este protector no es seguro en el código que podría usarse durante la limpieza de excepciones.

Prueba este ejemplo:

#include <exception> #include <iostream> #include <string> // simplified ScopeGuard11 template <class Fun> struct ScopeGuard11 { Fun f_; ScopeGuard11(Fun f) : f_(f) {} ~ScopeGuard11(){ //destructor if(std::uncaught_exception()){ //if we are exiting because of an exception f_(); //execute the functor } //otherwise do nothing } }; void rollback() { std::cout << "Rolling back everything/n"; } void could_throw(bool doit) { if (doit) throw std::string("Too bad"); } void foo() { ScopeGuard11<void (*)()> rollback_on_exception(rollback); could_throw(false); // should never see a rollback here // as could throw won''t throw with this argument // in reality there might sometimes be exceptions // but here we care about the case where there is none } struct Bar { ~Bar() { // to cleanup is to foo // and never throw from d''tor try { foo(); } catch (...) {} } }; void baz() { Bar bar; ScopeGuard11<void (*)()> more_rollback_on_exception(rollback); could_throw(true); } int main() try { baz(); } catch (std::string & e) { std::cout << "caught: " << e << std::endl; }

Usted querría ver una rollback cuando salga de baz , pero verá dos, incluyendo una espuria para dejar foo .