Im-Paired Programming

std::pair can make your life easier. But as with many things, moderation is the key: Excessive use can lead to unmanageable code.


March 01, 2004
URL:http://drdobbs.com/im-paired-programming/184401775

March 04: Im-Paired Programming)

"Um, you got a minute?" Kerry's quiet voice broke through my concentration. I sat back for a moment, disengaging my brain from the task at hand.

"Of course, young apprent..." I swung around. Seeing the look on Kerry's face, I changed my tone. "Aw, heck, the Guru's not around right now, there's no point in carrying on the shtick. 'Sup, Kerry?"

"Well, I'm having trouble following this function," he called up the source code he was working on:

// $Author: Bob$
void f( const vector< T > & timers )
{
  vector < pair< string,
           pair< long, 
           pair< long, 
           pair< long,
           pair< bool,
           pair< bool, bool> > > > > > >
  infoReceived;

  for (int outputCount = 0; 
       outputCount < timers.size();
       ++outputCount) 
  {
    pair< string,
      pair< long,
      pair< long,
      pair< long,
      pair< bool, 
      pair< bool, bool> > > > > >
    newInfo;

    newInfo.first = 
      timers[ outputCount ].id_;
    newInfo.second.first = 
      timers[ outputCount ].hour_;
    newInfo.second.second.first = 
      timers[ outputCount ].minute_;
    newInfo.second.second.second
      .first = 
      timers[ outputCount ].second_;
    newInfo.second.second.second
      .second.first =
      timers[ outputCount ].state_;
    newInfo.second.second.second
      .second.second.first =
      timers[ outputCount ].direction_;
    newInfo.second.second.second
      .second.second.second =
     timers[ outputCount ].showTimeSinceEvent_;

     infoReceived.push_back ( newInfo);
  }

"Whooeee, Bahb's really outdone himself this time," I muttered, loud enough to ensure that Wendy could hear me.

"Now what," she called over the cubicle wall.

"C'mon over, and I'll show you." When Wendy pulled up beside us, I pointed at the code.

"Oh, my," Wendy gasped after examining the screen, "that's gotta be a record for the worst use of a pair that I've ever seen."

"Wanna see the best part?" I didn't wait for her answer, but scrolled down to the second half of the function:

  for (int timerCount = 0; 
       timerCount < infoReceived.size();
       ++timerCount) 
  {
    TimerInfo info;
    info.id_ = 
      infoReceived[ timerCount ]
      .first;
    info.hour_ =
      infoReceived[ timerCount ]
      .second.first;
    info.minute_ =
      infoReceived[ timerCount ]
      .second.second.first;
    info.second_ =
      infoReceived[ timerCount ]
      .second.second.second.first;
    info.isRunning_ =
      infoReceived[ timerCount ]
      .second.second.second.second
      .first;
    info.isCountingUp_ = 
    infoReceived[ timerCount ]
      .second.second.second.second
      .second.first;
    info.showTimeSinceEvent_ = 
      infoReceived[ timerCount ]
      .second.second.second.second
      .second.second;
    ProcessTimer( info );
  }

} // end of f()

"Um...I don't see..." Wendy paused a moment. "Hey, wait a second — the only reason he's creating and filling in the infoReceived vector is to iterate through it and pull out everything he just put in it — then he tosses the whole darn vector away."

"You got it," I crowed. "Not only is that mess of nested pairs horrendously ugly, but it is redundant to boot."

"Why is it so ugly?" Kerry put in.

"Well, look at how much trouble you had reading it," I pointed out. "Not to mention the brittleness of it. Look at that last line — six .second statements in a row. You have to keep very careful track of how many levels down you're going in the nest — one too few and you're wasted. Boom!"

"And," Wendy interjected, "there's also the code communication that the Guru's always harping on. Look at the declaration of infoReceived:"

  vector < pair< string,
           pair< long, 
           pair< long, 
           pair< long,
           pair< bool,
           pair< bool, bool> > > > > > >
  infoReceived;

"All we know about the items stored in this vector is that it takes a string, three longs, and three bools. But what are they? What do they represent?"

"I, um, was wondering," Kerry interrupted, "about the declaration of the newInfo nested pairs, inside the for loop:"

   pair< string,
        pair< long,
        pair< long,
        pair< long,
        pair< bool, 
        pair< bool, bool> > > > > >
    newInfo;

"It looks a lot like the nested pairs that go into the infoReceived vector, so I thought maybe a typedef would help here?"

"Very good, young apprentice," the Guru's voice sounded behind us, as she closed her tome with her customary snap. The three of us jumped to varying degrees. "That would indeed be a good place to use a typedef, if indeed the monstrosity of these nested pairs were required in the first place. Let us ignore the redundancy of the second half of the function for a moment," she reached over and scrolled back to the top of the function, "and consider how to eliminate these nested pairs."

"Um..." Kerry faltered under the Guru's direct stare. "Could we maybe create a structure?"

"Excellent idea, apprentice. Please demonstrate." I slid aside, to let Kerry at the keyboard. After a few minutes, he typed:

void f( const vector< T > & timers )
{
  struct IncomingTimeInfo
  {
    string id_;
    int hour_;
    int minute_;
    int second_;
    bool state_;
    bool direction_;
    bool showTimeSinceEvent_;
  };
  vector < IncomingTimeInfo >
    infoReceived;
  for (int outputCount = 0; 
       outputCount < timers.size();
       ++outputCount) 
  {
    IncomingTimeInfo newInfo;
    newInfo.id_ = 
      timers[ outputCount ].id_;

"And so on, filling in the members as we iterate through," Kerry said as he turned back to us.

"Very good, apprentice. In general, I find that nesting pairs more than two or three levels deep becomes quickly unreadable and unmaintainable. It is wiser to create a structure to hold the values you need. I also preach that it is frequently wise to use a typedef for your containers." She reached across and modified Kerry's code slightly:

typedef vector < IncomingTimeInfo >
    IncomingTimes;
IncomingTimes infoReceived;

"The benefit is most pronounced when you need to declare an iterator for the container, as in:"

IncomingTimes::iterator timer;

"Now, as you have observed, the new IncomingTimeInfo struct is quite similar to the existing TimeInfo struct, and if we use it instead, we can eliminate the redundant, second loop and the local structure. Please revise the code accordingly, my young apprentice, and show me your writings." She turned and silently glided away.

Later that day, when I updated my code from the repository, I saw Kerry's revised function:

void f( const vector< T > & timers )
{
  for (int outputCount = 0; 
       outputCount < timers.size();
       ++outputCount) 
  {
    TimerInfo info;
    info.id_ = 
      timers[ outputCount ].id_;
    info.hour_ =
      timers[ outputCount ].hour_;
    info.minute_ =
      timers[ outputCount ].minute_;
    info.second_ =
      timers[ outputCount ].second_;
    info.isRunning_ =
      timers[ outputCount ].state_;
    info.isCountingUp_ = 
      timers[ outputCount ].direction_;
    info.showTimeSinceEvent_ = 
     timers[ outputCount ].showTimeSinceEvent_;
    ProcessTimer( info );
  }
} // end of f() 


Herb Sutter (http://www.gotw.ca/) is convener of the ISO C++ Standards committee, author of Exceptional C++ and More Exceptional C++, and Visual C++ architect for Microsoft. Jim Hyslop is a senior software designer for Leitch Technology International. He can be reached at [email protected].


Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.