tags:

views:

384

answers:

4

I came across a class instance function that needed to temporarily change a class instance variable, and then restore it when the function completed. The function had return statements all over the place, and before each return there was a restoring statement. That seemed messy to me, not to mention scary when a exception is thrown.

As an improvement I came up with this generalization using a inner class definition. Here is a sample driver program (class restorer).

class Unwind {
private:
  bool b_active_; ///< the thing I want to be restored
  template<typename T>
  class restorer {
    T* ref_;
    T save_;
  public:
    restorer(T* perm) : ref_(perm), save_(*ref_) {};
    ~restorer() { *ref_ = save_; }
  };
public:
  Unwind() : b_active_(false) {};
  void a() { out("a in"); b(); out("a end"); }
  void b() {
    out("b in");
    {
      restorer<bool> trust_in_the_stack(&b_active_); // "restorer" created on the stack
      b_active_ = true; // change b_active_ only while "within" b()
      c();
      out("b inner end");
    }
    out("b end");
  }
  void c() { out("c in"); d(); out("c end"); }
  void d() { out("d in"); cout << "deepest" << endl; out("d end"); }
  void out(const std::string& msg) {
    std::cout << msg << ": " << b_active_ << std::endl;
  }
};

int main() { Unwind u; u.a(); return 0; }

The output using g++ 4.2.3 (-Wall) was:

a in: 0
b in: 0
c in: 1
d in: 1
deepest
d end: 1
c end: 1
b inner end: 1
b end: 0
a end: 0

Which is what I expect at "b end".

I felt that defining the class restorer inside the class Unwind helps to discourage misuse.

My question is, is there a general and safer way to do this? I am worried about lifetime issues.

Edit: Please assume that there are no threads, but "downstream" methods on the stack that change behavior based on this b_active_ flag.

A: 

This is how I would do it as well. This way if the function throws, or returns early for some reason, your Restorer object will be destroyed and the variable reset to the original value. The question really is, why do you need to have a variable that is reverted when the function returns? Is the object used from more than one thread?

QuantumPete

QuantumPete
+1  A: 

I like the restorer template but I would probably put the template outside the Unwind class or even in a separate header file so it can be reused by other classes in the future. That would also make it a little more readable.

Adam Pierce
+3  A: 

I agree with Adam Pierce and also think that you should prefer references over pointers:

template<typename T>
class restorer {
   T& ref_;
   T save_;
public:
   restorer(T& perm) : ref_(perm), save_(ref_) {};
   ~restorer() { ref_ = save_; }
};
dalle
A: 

I revised the sample a bit more based on the comments, and placed as an Community Wiki answer instead of editing the question. Please vote up if useful.

/// c++ code sample
#ifndef UTIL_RESTORER_HPP
#define UTIL_RESTORER_HPP

namespace Utility {

/// A Restorer instance ("inst") uses the stack to restore a saved
/// value to the named variable when the instance "inst" goes out of
/// scope.
/// 
/// Restorer is designed to be an auto variable, not allocated on any
/// other memory resource like a heap or in-place.
template<typename T>
class restorer {
  T& ref_;
  T  save_;
public:
  restorer(T& perm) : ref_(perm), save_(perm) {}
  ~restorer() { ref_ = save_; }
};

}//NAMESPACE
#endif//UTIL_RESTORER_HPP
piyo