Search code examples
c++c++11c++14raii

RAII state management


I need to change a state. Then do stuff. Then reset the state back to what it was - e.g:

auto oldActivationOrder = mdiArea->activationOrder();
mdiArea->setActivationOrder( QMdiArea::StackingOrder );
mdiArea->cascadeSubWindows();
mdiArea->setActivationOrder( oldActivationOrder );

How do I do this in a RAII way? (c++ 11 and/or 14)

Edit: Thanks for all the answers.

There are several suggestions to create a custom class for handling the state change (BoBTFish, mindriot, Mattias Johansson). This solution seems good and clear. However I think it is a drawback that it increases the line count from 4 to 20+. If used a lot this would bloat the code. Also it seems that some locality is lost by having a separate class.

Ami Tavory suggests using std::unique_ptr. This does not have the code bloat issue and maintains locality. However, as Ami also indicates, it may not be the most readable solution.

sp2danny suggests a generalized state-change class that can be reused. This avoids code bloat provided that it can replace several custom classes. I'm going to accept this answer - but I guess the right approach really depends on the context.


Solution

  • You can do a generic template:

    template< typename Obj, typename Getter, typename Setter , typename StateType >
    class ScopedStateChangeType
    {
    public:
        ScopedStateChangeType( Obj& o, Getter g, Setter s, const StateType& state )
            : o(o), s(s)
        {
            oldstate = (o.*g)();
            (o.*s)(state);
        }
        Obj* operator -> () { return &o; }
        ~ScopedStateChangeType()
        {
            (o.*s)(oldstate);
        }
    
    private:
        Obj& o;
        Setter s;
        StateType oldstate;
    };
    
    template< typename Obj, typename Getter, typename Setter , typename StateType >
    auto MakeScopedStateChanger( Obj& o, Getter g, Setter s, StateType state )
         -> ScopedStateChangeType<Obj,Getter,Setter,StateType>
    {
        return { o, g, s, state };
    }
    

    use it like:

    QMdiArea mdiArea;
    
    {
        auto ref = MakeScopedStateChanger(
            mdiArea, &QMdiArea::activationOrder, &QMdiArea::setActivationOrder,
            QMdiArea::StackingOrder );
        ref->cascadeSubWindows();
    }
    

    maybe it's worth it if you use this pattern often