Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

New Bases, Part 2


February 2002 C++ Experts Forum/Conversations


The change in control came suddenly.

Gilb had been watching the main Ballroom door, where we had heard occasional pounding. The attack, when it came, was through the two side doors and caught us while most of us were asleep.

I woke to the sound of loud banging and the glare from the intense points of light moving across the two opposing doors. It was immediately obvious that the enemy were cutting their way in, and quickly. Gilb's orders had been explicit — in case of attack, grab our packs and alien artifacts and jump through the forest gate, and that's what we tried to do.

Gilb and several of his officers made it through and quickly disappeared out of sight in the other world. Most of us were still racing for the ball-gate as the doors crumpled and armor-clad soldiers started pouring in toward us from two sides, shouting orders to stop. I was near the gate, ahead of Jeannine, and hesitated ... then shots were fired, and I jumped forward.

I felt nothing unusual as I passed through the gate, except that my suit's external temperature reading jumped to 28C. I took a quick glance backward — still in the Ballroom, Jeannine was dropping her pack and holding out her arms. I didn't wait to watch her get captured; I hurried after Gilb's party into the new world's underbrush.


The change in management came suddenly.

Because neither our company nor our acquirer were publicly traded, the boards were able to negotiate the deal quietly and keep it under wraps, even from the employees, until just a couple of weeks before the actual closing date. So it was that shortly after New Year's Day we first heard the announcement of the merger (such a polite term for "acquisition" or "buyout" given that we were much smaller and ailing than our buyer) and within two weeks found ourselves already working for the new boss.

Pete Williams seemed like a reasonable fellow at first, even if he was a bit too given to sports metaphors for a manager of a technical team. I would have more of a chance to refine that opinion as time went on, and I gained experience working under him. I did approve of Pete's first action, or perhaps inaction: he kept our team together and didn't monkey much with the way we worked, at first. That was mostly a good thing. Except for Bob, who had somehow thought he would get a promotion out of this, we were all pleased with this turn of events.

A few urgent assignments had taken me away from my lower-priority internal library-building work (not that the internal library wasn't important, but none of the requests assigned to me were, for I was only just starting out). When I next had a breather, I returned to the half-written library code. I had been filling two requests: Request 247 asked for "a ConvertBase function that takes a string representing a number in base N and converts it to a string representing the same number in base M." The Guru had pointed out that Request 314 was for "a facility to read and write a number of arbitrary base from a text stream." Thus guided, I had written the following to try to implement the former in terms of the latter and avoid redundant work:

string ConvertBase( size_t base1, size_t base2,
                    const string& src )
{
  stringstream s1( src );
  long value;

  if( !( s1 >> Num( base1, value )).eof() || 
      !( s1 >> std::ws ).eof() )
    throw logic_error( "src is not a valid number" );

  stringstream s2;
  if( !( s2 << Num( base2, value ) ) )
    throw logic_error( "unexpected error emitting "
                       "converted number" );

  return s2.str();
}

That seemed to do it for 247, though as it relied on 314's Num, it wouldn't work until I finished that. So far I had just a simple test case, with a Num stub hardwired to make the case compile, and while I was looking at it, I added a check to limit the base to a reasonable range:

class Num
{
public:
  Num( size_t base, long& value )
  : base_(base), value_(value)
  {
    if( base < 1 || base > 36 )
      throw logic_error( "base must be from "
                         "1 to 36" );
  }

  size_t Base() const { return base_;  }
  long& Value()       { return value_; }
  long  Value() const { return value_; } 

private:
  size_t base_;
  long& value_;
};

istream& operator>>( istream& i, Num& n )
{
  string s;
  i >> s;
  n.Value() = 255; // todo: really convert input
                   //       to n's base_
  return i;
}

ostream& operator<<( ostream& o, const Num& /* n */ )
{
  return o << "FF"; // todo: really output
                    //       n.value_ in n.base_
}

int main()
{
  string result = ConvertBase( 10, 16, "255" );
  cout << result << endl;
  return result == "FF" ? 0 : 1;
}

Next, I knew I would have to finish 314 by actually implementing << and >> for Nums. As I was about to start coding, I noticed a serious deficiency in the signatures of my existing operators << and >>. I leaned closer to the screen and was nearly ready to start making changes when the telltale closing of a book behind me alerted me to the Guru's presence.

"Is Bob around?" I whispered.

"No, we can just talk," she said and sat in my visitor's chair. "What on earth possessed you to do that?" she continued, pointing at the screen.

"Yeah, I know, I was just looking at that. I've been away from this code for weeks, and now the obvious thing jumps out at me."

"Ah. Well then, counsel yourself."

I pointed at the offending operator << and >> signatures. "Aw, I accidentally hardwired them for basic char and char_traits<char> streams only."

She smiled and stood. "Very good. Carry on, apprentice," she added, as we both heard the sound of Bob's sloshing latte approaching.

"Uh, that's it?" I asked. "No longer speech, no morality play, no programming lesson?"

She arched an amused eyebrow. "And why would there be? You spotted the wicked flaw in your writings without my help, and now you need time to fix it. What more can I teach until that is done?" And without waiting for a further reply, she smiled quietly, reopened her tome, and glided silently away.

I guessed that I must be improving if she was willing to let me get away without a three-page lesson. So I went back to the code. The first thing was to fix the signatures:

template<class CharT, class Traits>
basic_istream<CharT, Traits>& operator>>
  ( basic_istream<CharT, Traits>& i, Num& n );

template<class CharT, class Traits>
basic_ostream<CharT, Traits>& operator<<
  ( basic_ostream<CharT, Traits>& o, const Num& n );

That was better. I checked it by eye, and it looked right this time. Next, I decided that input would be easiest. I began adroitly:

template<class CharT, class Traits>
basic_istream<CharT, Traits>& operator>>
  ( basic_istream<CharT, Traits>& input, Num& n )
{
  locale loc = input.getloc();
  CharT c;

Here I congratulated myself on my own presence of mind, as I avoided the twin pitfalls of reading simply chars and of forgetting about locales. I then remembered that leading whitespace can be optionally skipped, and so I pressed on:

  // If we should, then skip leading
  // whitespace, if any.
  if( input.flags() & ios_base::skipws )
  {
    do
    {
      input >> c;
    }
    while( isspace( c, loc ) );
  }

  // Handle sign, if any.
  int sign = ( c == '-' ? —1 : 1 );
  if( c == '-' || c == '+' )
    input >> c;

So far, so good. When it came to actually handling the digits, the logic was simple, and it was mostly a matter of deciding on a reasonable way to decipher the digits. After all, green though I was, I was knowledgeable enough to realize that I couldn't just assume that the digit and/or letter characters would be contiguous and in order in the character set (even though each group was contiguous and in order in ASCII). For the time being, I decided on a helper structure that was suitable for searching with std::find():

  CharT digits[]
    = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  size_t numDigits 
    = sizeof( digits ) / sizeof( digits[0] );

  n.Value() = 0;
  while( input.good() )
  {
    int index = find(digits, digits + numDigits,
                     toupper (c, loc) )
                — digits;
    if( index < 0 || index > n.Base() )
    {
      break;
    }

    n.Value() = n.Value() * n.Base() + index;
    input >> c;
  }

  if ( !input.eof() )
    input.putback( c );

  n.Value() *= sign;
  return input;
}

That seemed reasonable enough for input, leaving only output. Output was easier, in part because I made a deliberate decision not to support the output formatting flags like showpos and uppercase; that could wait until a later request, if it was needed at all—

I glanced furtively over my shoulder, in case the Guru had glided up and would pounce on me, remonstrating about supporting all the standard stream flags. All was dark (it being that time of year) and quiet. There was no Guru there. I waited a few moments, but there was no sound or movement. Satisfied, I turned back to my code.

—so yes, as I was saying, I ignored the issue of formatting flags. If the Guru didn't like it, she could remonstrate after I checked it in, and I'd do a more complete job in version 2.0 of the code. If Wendy didn't like it, I would try to argue my way out of it by saying we didn't yet know if that was a requirement, so why complicate the code unnecessarily up front? If Bob didn't like it, he could go jump in the lake, particularly at this time of year.

So my first cut at a real insertion operator looked like this:

template<class CharT, class Traits>
basic_ostream<CharT, Traits>& operator<<
  ( basic_ostream<CharT, Traits>& o, const Num& n )
{
  long value = n.Value();
  basic_string<CharT, Traits> s;

  CharT digits []
    = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  basic_string<CharT, Traits>::size_type start=0;
  do
  {
    s.insert( start, 1, 
              digits[ value % n.Base() ] );
    value /= n.Base();
  }
  while( value > 0 );

  if( n.Value() < 0 )
    s.insert( start, 1, '-' );

  return o << s;
}

I started running the code through a more extensive set of test cases and got ready to tackle the next item on my normal project's workload. Our new manager was big on "time management," which seemed to consist of micromanaging our to-do lists. I wondered how that would work out.

Fortunately, I was ahead of my list. Perhaps today I could leave a little early for a change.


Fortunately, it was a mostly hospitable world we now found ourselves in — for it was obvious, if only from the slightly heavier gravity, that it was a different world from any we had known among Sol's children. Yes, mostly hospitable: as the few rations gave out, we found local flora that was edible, and we did not immediately come across any indigenous humans, or more to the point they did not come across us.

Gilb found out about the local fauna the hard way. We buried him a few days later and started posting sentries against the unfriendly five-legged carnivores we dubbed "Goofies" because of their passing resemblance to the cartoon character. That was when Da Rosa took over command of our small base and instituted a more rigorous testing of some of the artifacts we had determined were, or could be used as, weapons.

I worried about Jeannine.

Jim Hyslop is a senior software designer at Leitch Technology International Inc. He can be reached at [email protected].

Herb Sutter is an independent consultant and secretary of the ISO/ANSI C++ standards committee. He is also one of the instructors of The C++ Seminar (<www.gotw.ca/cpp_seminar>). Herb can be reached at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.