[声明]:本英文资料源自于Herb Sutter 创建的“Conversation”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter、Jim Hyslop同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。
文章来源:http://www.gotw.ca
版权归属:Herb Sutter and Jim Hyslop
译 者:amature
对话#20: 新的起点,第二部分
控制权的改变来得非常突然。
吉尔伯一直在监视舞厅的大门,我们已经听到几阵撞门声。他们通过两边的大门攻了过来,当我们被俘时大部分还在睡梦中。
我被巨大的重击声和在两个相对的大门之间移动的强烈灯光所惊醒。很明显,敌人想进行突然袭击。吉尔伯的命令再清楚不过了-遇到攻击的时候,抓上我们的行李和外星文物,跳入森林大门,这也正是我们想做的。
Glib和他的几个官员就是这样做的,他们迅速地到另一个世界里并从我们的视线中消失吉尔伯和几个官员跳了进去,旋即消失于我们的视野当中。当大门卷起、全副武装的战士高喊“不许动”,从两边向我们冲过来时,我们中的大部分人正拼命地奔向金属球门。我在珍妮前面,靠近大门,正犹豫不决着....这时敌人的子弹飞了过来,我赶快跳了进去。
当我穿过那扇大门时,我没有感觉到什么不寻常,但是我衣服外面的温度计显示温度已跃升到了28°C。我向后快速地扫视了一下-珍妮仍然在舞厅里,包裹扔到地上,双手高举着。我没有等着继续看她怎么被捕俘,赶紧跟随着Glib的随行人员,匆忙地走进了新世界的丛林中。
- - - - - - - - - - - - - - - - - - - - - - - - -
管理上的变化来得很突然。
由于我们公司和我们的买主都不是上市交易的,董事会能够悄悄地谈交易,而且对员工也保密,这种状况一直持续到实际合并日的前几周。因此在新年后不久我们第一次听到了“合并”的声明(用这个词来代替“收购”或“出售”显得体面一些,因为我们公司无论规模和状况均不如我们的买主)而且在两个星期内我们发现自己已经替新的老板工作了。
彼得.威廉姆斯给人的第一印象像是个通情达理的人,尽管作为一个技术队伍的管理者他有点沉迷于体育。随着时间的过去,我有更多的机会来完善对他的看法,而且在他手下工作我也获得了经验。我非常赞同彼得的第一把火,或者说是没什么动作:他保留了我们整个队伍,而且不来干预我们的工作。大家对这个安排非常满意,除了鲍勃,他以为自已这次能获得提升。
一些紧急任务使我从低优先级的内部建库工作中脱离出来(不是说内部库不重要而是说指派给我的每一个任务都不重要,因为我刚刚开始做)。等我再次有喘息的机会时,我回到了刚写一半的库代码。我做的部分只需满足两个需求:需求247要求是“一个叫做ConvertBase的函数,它带有一个string参数,表示一个以N为基数的数字string参数,功能是将它转换为一个string,这个string 表示以M为基数的相同的数字”。Guru告诉我需求314是“一种从文本流中读和写任意基数的数字的方法”。在这样的指导下,我希望可以用后者来实现前者而避免多余的工作,我写了如下的代码:
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();
}
这似乎是用来实现需求247的,虽然它要依赖于需求314的Num,而且要等我完成了Num 这才能起作用。迄今为止,我只有一个简单的测试例,用一个接到一个Num桩模块来通过编译,并且当我考虑它时,我加了一个检查条件把基数限制在一个合理的范围内。
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;
}
接下来,我知道我必须通过为Num s真正实现<<和>>来完成314。在我准备开始编码时,我发现在已有的操作符<<和>>上有一个严重的缺陷。我的身体向屏幕靠近,正准备开始修改时,“啪”的一声,一本书合拢的声音向我提示Guru的存在。
"鲍勃在附近吗?"我低声问道。
“不在,我们尽管说吧”,她说着并在我的客椅上座了下来。她用手指着屏幕,继续说:“到底是什么使你着魔似地做那件事?”
“是的,我知道,我正在研究。我已经好几个星期没有碰它们了,而现在最明显不过的事情摆在了我面前。”
“哈,那么自个想办法。”
我指着<<和>>这两个讨厌的操作符:“我有意无意地简单实现它们,只用于基本的char以及char_traits<char>流。”
她微笑着站起来,补充道:“很好,继续,徒弟,” 这时我们都听到了鲍勃呷咖啡的声音传了过来。
“哦,那是?”我问道,“不再演说,没有道德演说,没有编程课?”
她的眉毛令人开心地向上弯了一下:“那么为什么会这样呢?你不用我帮忙就发现了你作品中的严重缺陷,而现在你需要时间来修正它。在你完成之前用不着我说什么吧?”没有等我进一步的答复,她轻轻微笑着,打开她的书本,静静地走了出去。
既然她不给我好好上课就放心让我自己去干,我猜想自己肯定有了不小进步。因此我重新回到了那段代码。首先来修正函数原型:
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 );
这更好一些。我目视检查,这次它看起来是对了。接下来,我决定让输入变成最容易的,我熟练地开始了:
template<class CharT, class Traits>
basic_istream<CharT, Traits>& operator>>
( basic_istream<CharT, Traits>& input, Num& n )
{
locale loc = input.getloc();
CharT c;
当我避免了读简单的char和忘记本地数据的类似缺陷时,我对自己的镇定自若感到庆贺。接着我想起前导空白字符可以被有选择地略过,所以我继续完善:
// 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;
到此,很好。当真正开始处理数字时,逻辑很简单,而且主要的工作就是决定一个合理的方法来对数字进行解码。不管怎样,虽然我还是新手,但我最起码会认识到我不能仅仅假设数字和(或)字母是相邻的并且和字符集中的顺序一样(虽然每个组中的元素是相邻的并在ASCII中是有序的)。暂时地,我决定用一个辅助结构,它对于用 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;
}
对输入来说,这显得非常合理了,现在只剩下输出了。输出更容易些,部分原因是我做了一个深谋远虑的决定:不提供输出像 showpos 和 uppercase 的格式化标志;这些可以留到下一个需求去做,如果它真的很需要的话-
我偷偷地从侧面扫视,以防Guru已经靠近并猛扑过来,抗议我必须支持标准流的所有标志。四周很暗(正是一年中白天最短的时候),且非常寂静,Guru并没有出现。我等了一会,还是没什么动静,便心满意足地思绪返回到代码中。
很好,我自言自语,格式化标志的问题我就跳过算了。如果导师不喜欢它,在我提交后她可以抗议,我将在代码的2.0版本中做更加完全的工作。如果温迪不喜欢它,我将尽力为我的方式辩解,我会说我们还不知道这是否是一个需求,那么为什么要没必要地将代码复杂化?如果鲍勃不喜欢它,他可以一头跳到湖里,特别是在一年中的这个时候。
因此我的在一个实际的插入操作符上的代码如下面所示:
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;
}
我开始通过一个更加广泛的测试例集合来运行这段代码,我准备在我的正常项目工作量内解决下一个任务。我们的新管理者很善于“时间管理,”这好像是由细化我们的任务列表来组成的。我想知道那是怎么达到效果的。
幸运的时,我在小组中领先。或许今天我可以早点离开。
- - - - - - - - - - - - - - - - - - - - - - - - -
幸运的是,我们现在发现自己所在的世界是非常好客的-因为这很明显,如果只是从更加巨大的地心引力,这是一个和我们从Sol的小孩子中知道的任何一个都不同的世界。是的,非常之好客:当少量的配给用完后,我们发现当地的植物亦可食用,而且我们没有立即碰到任何本地居民,或者更准确地说是他们没有碰到我们。
吉尔伯成了当地凶猛动物的牺牲品。几天后,我们安葬了他并设置了岗哨监视这些不友好的五腿食肉动物,我们称它们为“大笨蛋”,因为跟卡通角色有几分相似之处。达.罗萨成了我们这个小队的头,他制定了一个对那些文物更加严格的测试计划,看看能否用作武器。
我担心着珍妮。
[关于作者]
Herb Sutter
是个独立顾问,也是ISO/ANSI C++标准委员会的秘书。你可通过hsutter@acm.org.联系他
Jim Hyslop
Leitch Technology International Inc.资深的软件设计师,你可通过jim.hyslop@leitch.com联系他