Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 28.  Increment Operators


28.2. Efficiency

Since postfix operators have to maintain the previous value in order to return it to the calling context, they often have an efficiency cost over their prefix analogs. I remember many years ago being told by a hardware guru[2] that Motorola chips were able to operate more efficiently with prefix rather than postfix operations. Unless the hardware supports a postfix increment, using one usually winds up consuming a temporary register, except when the compiler can deduce that the original value is not needed, in which case it would generate identical code to the prefix form. However, when dealing with class types, it is almost always the case that there is an efficiency cost for calling the postfix operator rather than the prefix operator.

[2] Well, he said he was a guru, anyway.

Sadly, all too many teaching institutions must be failing to explain the ramifications, because there's still a lot of new code being written using postfix forms. I've encountered many engineers who use the postfix form as a matter of convention, and who insist that they nevertheless use the prefix form for class types. Phooey!

This nonsensical stance fails for two reasons. First, humans are habit-forming creatures by nature. Every time I've had this debate with engineers and they've been man enough to offer up their code for inspection, I've found cases where they forgot, and their machine was executing cycles it needn't have.

The second failing of this flummery is that it neglects genericity. Many times we use template algorithms that operate on fundamental types and on user-defined types, that is, nonpointer iterator types. Failing to observe correct prefix usage may be immaterial when such algorithms are applied to fundamental types, but compilers may not be able to clean up after you with other types.

28.2.1 Detecting Unused Postfix Operators

If, like me, you sometimes tire of debating correctness, robustness, and efficiency with people who can drive you to wonder whether they're in the right industry, then you may yearn for a definitive proof of their folly. Wouldn't it be nice to be able to automatically detect inappropriate use of postfix operators? Thankfully, we can.

I'm sure you'll not exactly fall off your chair to learn that there's a template-based solution to this. It is based around the unused_return_value_monitor class.

Listing 28.1.



template< typename V


        , typename M


        , typename R = V


        >


class unused_return_value_monitor


{


public:


  typedef unused_return_value_monitor<V, M, R>  class_type;


public:


  explicit unused_return_value_monitor(R value, M monitor = M())


    : m_value(value)


    , m_monitor(monitor)


    , m_bUsed(false)


  {}


  unused_return_value_monitor(class_type const &rhs)


    : m_value(rhs.m_value)


    , m_monitorFn(rhs.m_monitorFn)


    , m_bUsed(rhs.m_bUsed)


  {


    rhs.m_bUsed = false;


  }


  ~unused_return_value_monitor()


  {


    if(!m_bUsed)


    {


      m_monitor(this, m_value);


    }


  }


public:


  operator V() const


  {


    m_bUsed = true;


    return m_value;


  }


private:


  R             m_value;


  M             m_monitor;


  mutable bool  m_bUsed;


// Not to be implemented


private:


  unused_return_value_monitor &operator =(class_type const &rhs);


};



It is easily integrated into your class's postfix operators, as can be seen in the following class:

Listing 28.2.


class X


{


private:


  struct use_monitor


  {


    void operator ()(void const *instance, X const &value) const


    {


      printf( "Unused return value %s from object instance %p\n"


               , value.get_value().c_str(), instance);


    }


  };


public:


  X &operator ++()


  {


    . . . // Increment stuff


    return *this;


  }


#ifdef ACMELIB_DEBUG


  unused_return_value_monitor<X, use_monitor> operator ++(int)


#else /* ? ACMELIB_DEBUG */


  X operator ++(int)


#endif /* ACMELIB_DEBUG */


  {


    X ret(*this);


    operator ++();


    return ret;


  }


private:


  string_t  m_value;


};



Since this is an inefficiency detector, it's only appropriate to use it in debug builds, so I'd advise you to use preprocessor discrimination to select the raw return type in release builds, as shown in the example.

As for what your monitor function should do, I suggest that it should trace to the debugger or to a log file, rather than, say, throw an exception or abort(), since calling the inappropriate form of the operators is (or rather will be, unless strange things are happening) an efficiency error rather than a semantic error. But since it's parameterizable, you can do whatever you think is appropriate.


      Previous section   Next section