Heim  >  Artikel  >  Backend-Entwicklung  >  Hier sind einige Titeloptionen, die jeweils einen anderen Aspekt des Artikels hervorheben: Option 1: Konzentration auf das Konzept und die C 11-Funktion: * ScopeGuard in C 11: Einfache Fehlerbehandlung, aber mit Einschränkungen

Hier sind einige Titeloptionen, die jeweils einen anderen Aspekt des Artikels hervorheben: Option 1: Konzentration auf das Konzept und die C 11-Funktion: * ScopeGuard in C 11: Einfache Fehlerbehandlung, aber mit Einschränkungen

Patricia Arquette
Patricia ArquetteOriginal
2024-10-28 04:51:02648Durchsuche

Here are a few title options, each emphasizing a different aspect of the article:

Option 1: Focusing on the concept and C  11 feature:

* ScopeGuard in C  11: Simple Error Handling, But Which Caveats? 

Option 2: Highlighting the simplicity and limitatio

Einfachster und übersichtlichster C 11 ScopeGuard

In C 11 gibt es eine einfache Redewendung für die Implementierung eines ScopeGuard, die die Fehler- und Ressourcenbehandlung vereinfacht. Hier ist eine kurze Erklärung und Implementierung:

Konzept:

Ein ScopeGuard ist eine C-Klasse, die eine Möglichkeit bietet, einen Codeblock zu definieren, der automatisch ausgeführt wird, wenn der Bereich, in dem der Wächter erstellt wurde, endet. Dies ermöglicht eine einfache Bereinigung und Fehlerbehandlung und stellt sicher, dass auch in Ausnahmefällen Ressourcen freigegeben oder Maßnahmen ergriffen werden.

Implementierung:

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

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

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

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

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

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

Nutzung :

<code class="cpp">void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptions() 
{
   std::vector<int> myVec;
   std::vector<int> someOtherVec;

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

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

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

In diesem Beispiel wird die ScopeGuard-Sprache verwendet, um sicherzustellen, dass die Bereinigungsaktionen (Entfernen von Elementen aus Vektoren) auch dann ausgeführt werden, wenn eine Ausnahme innerhalb des Funktionsumfangs ausgelöst wird.

Einfachheit und Einschränkungen:

Diese Implementierung der ScopeGuard-Sprache ist relativ einfach und prägnant, sodass sie leicht zu verwenden und zu verstehen ist. Allerdings äußerte der Autor der ursprünglichen Frage auch Bedenken hinsichtlich möglicher Mängel oder fehlender Besonderheiten. Lassen Sie uns diese angehen:

  • Perfekte Weiterleitung: Die Implementierung nutzt die perfekten Weiterleitungstechniken von C 11 nicht vollständig. Dies wird in der makeScopeGuard-Funktion deutlich, die L-Wert-Referenzen anstelle von universellen Referenzen für Lambda-Parameter verwendet. Die Verwendung universeller Referenzen würde eine größere Flexibilität und Unterstützung für R-Wert-Lambdas ermöglichen.
  • Rollback, wenn Acquire auslöst: Die ursprüngliche Implementierung behandelt nicht den Fall, in dem das „Acquire“-Lambda eine Ausnahme auslöst. Wenn das Erwerbs-Lambda fehlschlägt, wird das Rollback-Lambda nie aufgerufen, sodass der Code in einem inkonsistenten Zustand verbleibt.

Um diese Bedenken auszuräumen, finden Sie hier eine aktualisierte Version der ScopeGuard-Implementierung und -Nutzung:

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

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

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

            ScopeGuard( ScopeGuard&amp;&amp; _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&amp; _al , const Lambda&amp; _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&amp;&amp; _al , L&amp;&amp; _l) : committed(false) , rollbackLambda(std::forward<L>(_l))
            {
                std::forward<AdquireLambda>(_al)(); // just in case the functor has &amp;&amp;-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&amp;&amp; _a , rLambda&amp;&amp; _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&amp;&amp; _a , rLambda&amp;&amp; _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&amp;&amp; _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 &amp;&amp; someOtherVec.size() == 0 , "rollback did not work");
                }
                shouldThrow = false;
                SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows();
                AssertMsg( myVec.size() == 1 &amp;&amp; someOtherVec.size() == 1 , "unexpected end state");
                shouldThrow = true;
                myVec.clear(); someOtherVec.clear();  
                try
                {
                    SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesRollbackIfAdquireThrows();
                } catch (...)
                {
                    AssertMsg( myVec.size() == 0 &amp;&amp; someOtherVec.size() == 0 , "rollback did not work");
                }
            }

            void SomeFuncThatShouldBehaveAtomicallyInCaseOfExceptionsUsingScopeGuardsThatDoesNOTRollbackIfAdquireThrows() //throw()
            {

                myVec.push_back(42);</code>

Das obige ist der detaillierte Inhalt vonHier sind einige Titeloptionen, die jeweils einen anderen Aspekt des Artikels hervorheben: Option 1: Konzentration auf das Konzept und die C 11-Funktion: * ScopeGuard in C 11: Einfache Fehlerbehandlung, aber mit Einschränkungen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn