Within the scope of a member function, I want to temporarly set a member variable to a certain value.
Then, when this function returns, I want to reset this member variable to a given known value.
To bo safe against exceptions and multiple returns, and I've done it with a simple RAII like class. It's defined within the scope of the member function.
void MyClass::MyMemberFunction() {
struct SetBackToFalse {
SetBackToFalse(bool* p): m_p(p) {}
~SetBackToFalse() {*m_p=false;}
private:
bool* m_p;
};
m_theVariableToChange = true;
SetBackToFalse resetFalse( &m_theVariableToChange ); // Will reset the variable to false.
// Function body that may throw.
}
It seems so obviously commonplace, that I was wondering if there was any such template class doing this in the C++ standard library?
Not yet (there have been proposals for this). But implementing a generic one is simple enough;
struct scope_exit {
std::function<void()> f_;
explicit scope_exit(std::function<void()> f) noexcept : f_(std::move(f)) {}
~scope_exit() { if (f_) f_(); }
};
// ...
m_theVariableToChange = true;
scope_exit resetFalse([&m_theVariableToChange]() { m_theVariableToChange = false; });
For simplicity above, I've redacted the copy and move constructors etc...
Marking them as = delete
will make the above a minimal solution. Further; moving could be allowed if desired, but copying should be prohibited.
A more complete scope_exit
would look like (online demo here);
template <typename F>
struct scope_exit {
F f_;
bool run_;
explicit scope_exit(F f) noexcept : f_(std::move(f)), run_(true) {}
scope_exit(scope_exit&& rhs) noexcept : f_((rhs.run_ = false, std::move(rhs.f_))), run_(true) {}
~scope_exit()
{
if (run_)
f_(); // RAII semantics apply, expected not to throw
}
// "in place" construction expected, no default ctor provided either
// also unclear what should be done with the old functor, should it
// be called since it is no longer needed, or not since *this is not
// going out of scope just yet...
scope_exit& operator=(scope_exit&& rhs) = delete;
// to be explicit...
scope_exit(scope_exit const&) = delete;
scope_exit& operator=(scope_exit const&) = delete;
};
template <typename F>
scope_exit<F> make_scope_exit(F&& f) noexcept
{
return scope_exit<F>{ std::forward<F>(f) };
}
Notes on the implementation;
std::function<void()>
can be used to erase the type of the functor. std::function<void()>
offers exception guarantees on the move constructors based on the exception specific of the held function. A sample of this implementation is found herenoexcept
, more substantial detail is found in the C++ proposalthrow
, this is also consistent with the C++11 specification on the default exception specification for a destructor. See cppreference, SO Q&A, GotW#47 and HIC++Other implementations can be found;