Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a C++ standard class to set a variable to a value at scope exit

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?

like image 972
Didier Trosset Avatar asked Apr 15 '16 10:04

Didier Trosset


People also ask

What happens when a variable goes out of scope in C?

Nothing physical happens. A typical implementation will allocate enough space in the program stack to store all variables at the deepest level of block nesting in the current function. This space is typically allocated in the stack in one shot at the function startup and released back at the function exit.

How are variables scoped in C?

In C, variables are always statically (or lexically) scoped i.e., binding of a variable can be determined by program text and is independent of the run-time function call stack. For example, output for the below program is 0, i.e., the value returned by f() is not dependent on who is calling it.

Can you tell us something about scope rules in C++?

When you declare a program element such as a class, function, or variable, its name can only be "seen" and used in certain parts of your program. The context in which a name is visible is called its scope. For example, if you declare a variable x within a function, x is only visible within that function body.

What does it mean to go out of scope C++?

In C++ a scope is a static region of program text, and so something "out of scope", taken literally, means physically outside of a region of text. For instance, { int x; } int y; : the declaration of y is out of the scope in which x is visible.


3 Answers

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 here
  • These exception specifications are consistent the C++ proposal and GSL implementations
  • I've redacted most of the motivation for the noexcept, more substantial detail is found in the C++ proposal
  • The "usual" RAII semantics of the destructor, hence the "scope exit" function is applicable; it will not throw, 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;

  • The C++ proposal mentioned above and its revision (as of this writing)
  • Boost.ScopeExit
  • The Microsoft GSL implementation
like image 72
Niall Avatar answered Oct 18 '22 00:10

Niall


You could 'abuse' shared_ptr for this:

m_theVariableToChange = true;
std::shared_ptr<void> resetFalse(nullptr, [&](void*){ m_theVariableToChange = false; });

If there are concerns about using void as template parameter T, I found the following in the C++ standard:

20.8.2.2§2:

... The template parameter T of shared_ptr may be an incomplete type.

This indicates that T is only used as a pointer, therefore using void should be fine.

like image 43
alain Avatar answered Oct 18 '22 00:10

alain


There is no standard version of this.

The CppGoreGuidelines Support Library (GSL) has a generalized version of this called finally but that library is not production quality yet. Its definitely recommended practice.

E.19: Use a final_action object to express cleanup if no suitable resource handle is available

Reason

finally is less verbose and harder to get wrong than try/catch.

Example

void f(int n)
{
    void* p = malloc(1, n);
    auto _ = finally([p] { free(p); });
    // ...
}

Note

finally is not as messy as try/catch, but it is still ad-hoc. Prefer proper resource management objects.

like image 1
Galik Avatar answered Oct 17 '22 23:10

Galik