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 jim.hyslop@leitch.com.
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 hsutter@acm.org.