O mais simples e mais limpo c + + 11 ScopeGuard

Estou tentando escrever um simplesScopeGuard baseado nos conceitos de Alexandrescu mas com c + + 11 idioms.

<code>namespace RAII
{
    template< typename Lambda >
    class ScopeGuard
    {
        mutable bool committed;
        Lambda rollbackLambda; 
        public:

            ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {}

            template< typename AdquireLambda >
            ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l)
            {
                _al();
            }

            ~ScopeGuard()
            {
                if (!committed)
                    rollbackLambda();
            }
            inline void commit() const { committed = true; }
    };

    template< typename aLambda , typename rLambda>
    const ScopeGuard< rLambda >& makeScopeGuard( const aLambda& _a , const rLambda& _r)
    {
        return ScopeGuard< rLambda >( _a , _r );
    }

    template<typename rLambda>
    const ScopeGuard< rLambda >& makeScopeGuard(const rLambda& _r)
    {
        return ScopeGuard< rLambda >(_r );
    }
}
</code>

Aqui está o uso:

<code>void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptions() 
{
   std::vector<int> myVec;
   std::vector<int> someOtherVec;

   myVec.push_back(5);
   //first constructor, adquire happens elsewhere
   const auto& a = RAII::makeScopeGuard( [&]() { myVec.pop_back(); } );  

   //sintactically neater, since everything happens in a single line
   const auto& b = RAII::makeScopeGuard( [&]() { someOtherVec.push_back(42); }
                     , [&]() { someOtherVec.pop_back(); } ); 

   b.commit();
   a.commit();
}
</code>

Como minha versão é muito mais curta do que a maioria dos exemplos (como o Boost ScopeExit), estou me perguntando que especialidades estou deixando de fora. Espero que eu esteja em um cenário de 80/20 aqui (onde eu tenho 80 por cento de limpeza com 20 por cento das linhas de código), mas eu não poderia deixar de me pergunto se eu estou perdendo algo importante, ou há alguma deficiência mencionando esta versão do idioma do ScopeGuard

obrigado!

Editar Eu notei um problema muito importante com o makeScopeGuard que leva o lambda necessário no construtor. Se o comando adquire lambda for lançado, o release lambda nunca será chamado, porque o protetor de escopo nunca foi totalmente construído. Em muitos casos, este é o comportamento desejado, mas eu sinto que às vezes uma versão que invocará a reversão se um lançamento acontecer também é desejada:

<code>//WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
    return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
}

template< typename aLambda , typename rLambda>
ScopeGuard< rLambda > // return by value is the preferred C++11 way.
makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
{
    auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
    _a();
    return scope;
}
</code>

Então, para completar, eu quero colocar aqui o código completo, incluindo testes:

<code>#include <vector>

namespace RAII
{

    template< typename Lambda >
    class ScopeGuard
    {
        bool committed;
        Lambda rollbackLambda; 
        public:

            ScopeGuard( const Lambda& _l) : committed(false) , rollbackLambda(_l) {}

            ScopeGuard( const ScopeGuard& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda) 
            {
                if (_sc.committed)
                   committed = true;
                else
                   _sc.commit();
            }

            ScopeGuard( ScopeGuard&& _sc) : committed(false) , rollbackLambda(_sc.rollbackLambda)
            {
                if (_sc.committed)
                   committed = true;
                else
                   _sc.commit();
            }

            //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
            template< typename AdquireLambda >
            ScopeGuard( const AdquireLambda& _al , const Lambda& _l) : committed(false) , rollbackLambda(_l)
            {
               std::forward<AdquireLambda>(_al)();
            }

            //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
            template< typename AdquireLambda, typename L >
            ScopeGuard( AdquireLambda&& _al , L&& _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
            {
                std::forward<AdquireLambda>(_al)(); // just in case the functor has &&-qualified operator()
            }


            ~ScopeGuard()
            {
                if (!committed)
                    rollbackLambda();
            }
            inline void commit() { committed = true; }
    };


    //WARNING: only safe if adquire lambda does not throw, otherwise release lambda is never invoked, because the scope guard never finished initialistion..
    template< typename aLambda , typename rLambda>
    ScopeGuard< rLambda > // return by value is the preferred C++11 way.
    makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
    {
        return ScopeGuard< rLambda >( std::forward<aLambda>(_a) , std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
    }

    template< typename aLambda , typename rLambda>
    ScopeGuard< rLambda > // return by value is the preferred C++11 way.
    makeScopeGuardThatDoesRollbackIfAdquireThrows( aLambda&& _a , rLambda&& _r) // again perfect forwarding
    {
        auto scope = ScopeGuard< rLambda >(std::forward<rLambda>(_r )); // *** no longer UB, because we're returning by value
        _a();
        return scope;
    }

    template<typename rLambda>
    ScopeGuard< rLambda > makeScopeGuard(rLambda&& _r)
    {
        return ScopeGuard< rLambda >( std::forward<rLambda>(_r ));
    }

    namespace basic_usage
    {
        struct Test
        {

            std::vector<int> myVec;
            std::vector<int> someOtherVec;
            bool shouldThrow;
            void run()
            {
                shouldThrow = true;
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work");
                }
                shouldThrow = false;
                SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                AssertMsg( myVec.size() == 1 && someOtherVec.size() == 1 , "unexpected end state");
                shouldThrow = true;
                myVec.clear(); someOtherVec.clear();  
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 && someOtherVec.size() == 0 , "rollback did not work");
                }
            }

            void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows() //throw()
            {

                myVec.push_back(42);
                auto a = RAII::makeScopeGuard( [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty myVec"); myVec.pop_back(); } );  

                auto b = RAII::makeScopeGuardThatDoesNOTRollbackIfAdquireThrows( [&]() { someOtherVec.push_back(42); }
                                    , [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty someOtherVec"); someOtherVec.pop_back(); } );

                if (shouldThrow) throw 1; 

                b.commit();
                a.commit();
            }

            void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows() //throw()
            {
                myVec.push_back(42);
                auto a = RAII::makeScopeGuard( [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty myVec"); myVec.pop_back(); } );  

                auto b = RAII::makeScopeGuardThatDoesRollbackIfAdquireThrows( [&]() { someOtherVec.push_back(42); if (shouldThrow) throw 1; }
                                    , [&]() { HAssertMsg( myVec.size() > 0 , "attempt to call pop_back() in empty someOtherVec"); someOtherVec.pop_back(); } );

                b.commit();
                a.commit();
            }
        };
    }
}
</code>

questionAnswers(12)

yourAnswerToTheQuestion