Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 6.  Scoping Classes


6.1. Value

Occasionally we would like to change the value of a variable for a defined period, and then change it back. It can look like this:



int    sentinel = . . .;





++sentinel;


. . .  // Do something significant


--sentinel;



This can be used to mark a quantum of resource use, or the depth in a recursion, or even the (internal) state of an object. I've seen (and written[1]) code such as the little horror shown in Listing 6.1.

[1] My excuse was that it was in an internal development tool, so no one's ever going to see it. We all know that internal development tools never get refined and released to the world, so I'm quite safe, eh?

Listing 6.1.



class DatestampPlugIn


{


  . . .


private:


  int m_saveCount;


};





HRESULT DatestampPlugIn::OnSave(. . .)


{


  HRESULT hr;


  if(m_nSaveCount != 0)


  {


    hr = S_OK;


  }


  else


  {


    ++m_nSaveCount;


    . . . // Do something inside the file as its being saved!


    --m_nSaveCount;


  }


  return hr;


}



The OnSave() handler is used to update the contents of the file while its being saved. In order to do this within the framework of the IDE (integrated development environment) for which DatestampPlugIn operates, it has to update the corresponding document's contents and cause a save to be effected again. In order to prevent infinite looping, it uses m_nSaveCount as a sentinel to indicate whether the program flow is already within OnSave(), in which case the update step is skipped. (This outrageous strategy is caused by the constraints of the IDE and anyone using it should blush while they do so.)

Naturally, neither of these code examples is either exception-safe, or multiple return statement-safe. If the code between the changing of the sentinel variable throws an exception, or executes a return from the function, then all bets are off. Obviously this calls for RAII, plain and simple. Just as obvious is that a template (see Listing 6.2) will do nicely.

Listing 6.2.


template <typename T>


class IncrementScope


{


public:


  explicit IncrementScope(T &var)


    : m_var(var)


  {


    ++m_var;


  }


  ~IncrementScope()


  {


    --m_var;


  }


private:


  T &m_var;


};



It should seem pretty obvious that you'd want to generalize further and facilitate the functionality that a complimentary DecrementScope class would provide by means of policy classes. We'll see how this is done shortly, along with some further refinements and some valid usage scenarios.

You may be thinking that the situation seems a little artificial, and perhaps it does at first glance. However, I had need of just such a facility when implementing a high-throughput multithreaded network server a couple of years ago. I wanted to be able to monitor a number of characteristics of the server with minimal intrusion into the performance (which was at a high premium), and initially came up with the two classes shown in Listing 6.3.

Listing 6.3.


class AtomicIncrementScope


{


public:


  AtomicIncrementScope(int *var)


    : m_var(var)


  {


    atomic_inc(&m_var);


  }


  ~AtomicIncrementScope()


  {


    atomic_dec(&m_var);


  }


// Members


private:


  int  *m_var;


  . . .





class AtomicDecrementScope


{


public:


  AtomicDecrementScope(int *var)


    : m_var(var)


  {


    atomic_dec(&m_var);


  }


  ~AtomicDecrementScope()


  {


    atomic_inc(&m_var);


  }


  . . .



Using these classes allowed for modification of system counters in a thread-safe and exception-safe manner. Since debugging was impossible, using this technique meant that I could peek into the current values of important system variables from within a lowest-priority thread that woke up once a second, and printed the data into a logging console. This dramatically helped with honing the application, since it simplified the morass of threads, sockets, and shared- memory blocks into a manageable set of statistics.[2]

[2] And helped me fix some glaring oversights.

Naturally, the next time I wanted to use these classes it wasn't in a multithreaded scenario, so that was the appropriate time to derive a more general solution, as shown in Listing 6.4.

Listing 6.4.



template <typename T>


struct simple_incrementer


{


  void operator ()(T &t)


  {


    ++t;


  }


};





template <typename T>


struct simple_decrementer


{


  void operator ()(T &t)


  {


    --t;


  }


};





template< typename T


        , typename A = simple_incrementer<T>


        , typename R = simple_decrementer<T>


        >


class increment_scope


{


public:


  explicit increment_scope(T &var)


    : m_var(var)


  {


    A()(m_var);


  }


  ~ increment_scope()


  {


    R()(m_var);


  }


private:


  T &m_var;


  . . .



Sometimes it's not increment (or decrement) that you're after, it can be distinct value change(s). In this case, our previous template can't be used, however general we make the modifications. But we can easily fulfill our requirements, as shown in Listing 6.5:

Listing 6.5.


template <typename T>


class ValueScope


{


public:


  template <typename V>


  ValueScope(T &var, V const &set)


    : m_var(var)


    , m_revert(var)


  {


    m_var = set;


  }


  template <typename V1, typename V2>


  ValueScope(T &var, V1 const &set, V2 const &revert)


    : m_var(var)


    , m_revert(revert)


  {


    m_var = set;


  }


  ~ValueScope()


  {


    m_var = m_revert;


  }


private:


  V &m_var;


  V m_revert;


  . . .



There are two constructors, so that you can opt to have the managed variable revert to its original value in the destructor, or to be changed to some other value.

An example would be:



string s1 = "Original";


cout << "s1: " << s1 << endl; // Prints "s1: Original"


{


  ValueScope<string>  vs(s1, "Temporary");


  cout << "s1: " << s1 << endl; // Prints "s1: Temporary"


}


cout << "s1: " << s1 << endl; // Prints "s1: Original"




      Previous section   Next section