Public Administration RAII

I need to change state. Then do something. Then, the reset state returns to what it was - for example:

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

How do I do this using RAII? (C ++ 11 and / or 14)

Edit: Thanks for all the answers.

There are several suggestions for creating a custom class for handling state changes (BoBTFish, mindriot, Mattias Johansson). This solution seems good and clear. However, I believe this is a flaw that increases the number of rows from 4 to 20+. If you use a lot, it inflates the code. It also seems that some locality is lost due to the presence of a separate class.

Ami Tavori suggests using std :: unique_ptr. This has no problem with bloating code and maintains locality. However, as Ami points out, this may not be the most readable solution.

sp2danny offers a generic state change class that can be reused. This avoids bloating the code, provided that it can replace several custom classes. I am going to accept this answer, but I think the right approach really depends on the context.

+4
source share
5 answers

You can create a common 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();
}

perhaps worth it if you often use this template

+1
source

RAII: R esource A cquisition I s I nitialisation.

, Release Release Is Destruction, , RRID, . (, ?)

, . , , : return s, break s, ,... ( goto!)

class ScopedActivationOrderChange {
    QMdiArea&             area_;     // the object to operate on
    QMdiArea::WindowOrder oldOrder_; // save the old state

  public:
    ScopedActivationOrderChange(QMdiArea& area, ActivationOrder newOrder)
        : area_(area)
        , oldOrder_(area_.activationOrder()) // save old state
    {
        area_.setActivationOrder(newOrder); // set new state
    }

    ~ScopedActivationOrderChange()
    {
        area_.setActivationOrder(oldOrder_); // reset to old state
    }
};

// ...

{ // <-- new scope, just to establish lifetime of the change
    ScopedActivationOrderChange orderChange{*mdiArea, QMdiArea::StackingOrder};
    mdiArea->cascadeSubWindows();
} // <-- end of scope, change is reversed

. , std::unique_ptr , , . std::vector RAII , , .

+7

, (, , ) scoped guard pattern std::unique_ptr :

#include <memory>
#include <utility>


int main()
{
    void *p, *q;                                                                                                                                                                                         
    auto reverser = [&p, &q](char *){std::swap(p, q);};
    /* This guard doesn't really release memory - 
        it just calls the lambda at exit. */
    auto guard = std::unique_ptr<char, decltype(reverser)>{nullptr, reverser};
    std::swap(p, q);
}  
+6

:

class SetActivationOrder
{
public:
  SetActivationOrder(QMdiArea *mdiArea, QMdiArea::WindowOrder order)
    : m_mdiArea(mdiArea),
      m_oldActivationOrder(mdiArea->activationOrder())
  {
    m_mdiArea->setActivationOrder(order);
  }

  ~SetActivationOrder()
  {
    m_mdiArea->setActivationOrder(m_oldActivationOrder)
  }

private:
  QMdiArea *m_mdiArea;
  QMdiArea::WindowOrder m_oldActivationOrder;
};

:

{
  // This sets the order:
  SetActivationOrder sao(mdiArea, QMdiArea::StackingOrder);
  mdiArea->cascadeSubWindows();
  // Destructor is called at end of scope and sets the old order
}
+4

RAII ( ) ( ). , , , . ++ , , , , , .

:

class ActivationOrderState
{
public:
    ActivationOrderState(QMdiArea& area)
        : m_area(area)
    {
        // Get the old value
        m_oldOrder = area.activationOrder();
    }

    ~ActivationOrderState()
    {
        // Restore the old value
        m_area.setActivationOrder( m_oldOrder );
    }

private:
    QMdiArea& m_area;
    QMdiArea::WindowOrder m_oldOrder;
};

{
    ActivationOrderState state(*mdiArea); // saves the state
    mdiArea->setActivationOrder( QMdiArea::StackingOrder ); // set the new state

    // do other things here...

} // end of scope, destructor is called and state is restored again

, , /, , :

class ActivationOrderState
{
public:
    ActivationOrderState(QMdiArea& area)
        : m_area(area)
    {
        // Get the old value
        m_oldOrder = area.activationOrder();
    }

    ~ActivationOrderState()
    {
        // Restore the old value
        m_area.setActivationOrder( m_oldOrder );
    }

    // Remove the possibility to create this object on the free store.
    template<typename... Args> void* operator new(std::size_t,Args...) = delete;

private:
    QMdiArea& m_area;
    QMdiArea::WindowOrder m_oldOrder;
};

See also Using RAII to temporarily suspend a stream.

+2
source

All Articles