对话:#09 重定向

类别:VC语言 点击:0 评论:0 推荐:
[声明]:本英文资料源自于Herb Sutter 创建的“Conversation”栏目,“C++ 翻译小组”的翻译作品供学习交流与参考用途,不得用于任何商业用途。未经Herb Sutter、Jim Hyslop同意,不得转载;对于违反以上条款,翻译小组对此不负任何责任;特此声明。

文章来源:http://www.gotw.ca
版权归属:Herb Sutter and Jim Hyslop
译    者:csdnfriend(CSDN)

对话:#09 重定向

  我开始并没太在意,但过了第三天还没见到珍妮,才感到不妙。一阵犹豫与绝望之后,我去找弗奈尔。找她是很自然的,因为我和珍妮当时都向她汇报工作。

  当我敲第二下门的时候,房门打开了。“啊,是你,”她说。“什么事?”

  “正在找珍妮,好几天没见她了。”我解释道。

  弗奈尔扫了我一眼,说:“啊,是的。她最近分配了新的任务。”

  “是吗,她在哪里呢?”

“在圆屋,那里和发掘现场都需要添加人手。她一旦去了就必须呆在那里了。去那儿是受限制的,因为门锁和地道情况不太好,进出那儿要穿特制的服装,真是痛苦。”

  “是不是很多人都重新分配了任务?这里好像以后要搬空的样子。”我所知道的就是自从几周前的爆炸事件后,主圆屋那边显然还是一直在装修。表面上我们在外边的楼层里继续自己部门的工作,实际上与内部失去了联系,圆屋内大范围的通讯设施都有故障而在维修。

  “有一些,”她说着靠在了门口边上。“为什么?如果需要你这样的,要我给你介绍进去吗?”

  “当然,”我脱口而出然而又奇怪自己反应这么快。我这么惦记珍妮吗?或者我急着再见到她?不管怎样,感觉自己有点奇怪。

  如果珍妮被转移到圆屋,我想如果有机会我也会去。忽然想起了很多年以前的一天,当时我学习处理另一种形式的转移。有意思的是,当时我也遇到了访问限制的问题。

- - - - - - - - - - - - - - - - - - - - - - - - -

  正当温迪走过我的小卧室时,我正在大叫:“啊!我恨死非法访问了!”

  挫折感太强烈了,我只想大喊大叫。然而我的声音终于烧焦般地减弱下去,最后变得和老鼠叫差不多了。

  这一切已经足够使温迪停了来看我了。“你还好吧,怎么回事,年轻人?”她的语调里带有一点爱尔兰口音。随着圣.帕特里克节的临近,她的一举一动越来越象爱尔兰人。

  “温迪,快帮我一个忙,”我叹了口气,“你用不着扮这副怪相,这里有一个怪人已经足够了。”

  “喂,你还好吧?”她平静温和地说,“伙计,什么东西把你惹你这样?” 

    “啊呀,就是这些代码,我想可能和Guru有点关系。”我承认,“一开始我就被她吓得六神无主。我肯定她是从疯人院跑出来的。后来,我又有点喜欢她把我称作她的徒弟。是有点怪异,但也没什么大不了。但当我给朋友提起此事时,他们认为这是故意贬我。我再仔细回想一下,觉得他们说得没错。”

   温迪笑了。“噢,”她说,“我明白了,事实上你没有从正确的着眼点去看待她。你应该去了解一下工作之外的她。”我试着想象她的家:架子上堆满了满是灰尘的电脑书和杂志;祭坛里烧着拜祭各位计算机科学家的香……我的神情一定出卖了我的想法—温迪大笑起来,“她摆Guru的架子,主要是为了气气鲍勃,吓唬一下新手。我喜欢她这样子,否则她就活象一具僵尸了。”

   “哦。”我并没有被说服。我不置可否,我觉得Guru的举动还是有点出格。,但我不想跟温迪争,于是叹了口气静下来回到那个问题上:“我矛盾了几个小时了。任务很简单—实际上就是重定向cout和cerr,把它们输出到文件中。”

  “好,把你做的再跟我说一下。”

  “我正在整合另一个组开发的库。这个库是以命令行(接口)的思路写出来的,所以所有的调试、诊断信息送到了cout与cerr。更糟的是我认为鲍勃在那个组,因为没有一个规则及原因说明何时信息送到cout何时送到cerr。实际上,有些信息分开送到两个流上!所以我要跟踪它们并把它们凑在一个log文件里。”

  “总之,正如我说的,我在把它们整合到我们的图形用户界面(GUI)应用程序里。不幸的是,GUI直接扔掉了cout和cerr的输出。所以,我正做的就是将输出转向到一个文件里,实际上,简单吧,简单得我不知该怎么做,我对问题进行分析过滤后,写了下面的小程序。”我拉了把椅子让温迪在电脑旁坐下。

#include <iostream>
#include <fstream>
int main()
{
    std::ofstream logFile("out.txt");
    std::cout = logFile;
    std::cerr = logFile;
    std::cout << "This goes to cout\n";
    std::cerr << "This goes to cerr\n";
}
  “程序在一个编译器上编译及运行没有问题,但换了个编译器就出了非法读取的错误。”

  温迪看了看屏幕。“嗯…我不能确定你是不是可以那样重新分配数据流。”

  “实际上,孩子,这样做显然极不自然明确地被神圣的标准所禁止。”

  我们差点跳起来。不过怎么说,我得承认Guru总是神秘地在最恰当的时间出现。“所以,我这样做不行?”我叹道。

  “不,” Guru微笑道,“,实际上很简单,徒弟,你在大学里用什么编译器?”

 “噢,是一种很老的编译器,不支持标准的很多特性。” 

  Guru点点头。“我明白了。你的编译器应该在使用一种先知Langer 和 Kreft所谓的 经典输入输出流[1]。你肯定很熟悉标准输入输出流—尤其是流缓冲类。流缓冲不再是低级的实现上的细节;它现在是一个…完整的类。输入输出流的设计者,Jerry Schwarz 早就看到了流缓冲类将会成为一个极其有用的特性。然而它的价值和功能常常被忽略。”

  “好吧。那我该怎么做呢?”

  Guru拿起一枝干擦笔飞快地在白书写板上写下了如下的代码:


#include <iostream>
#include <fstream>
int main()
{
    std::ofstream logFile("out.txt");
    std::streambuf *outbuf = std::cout.rdbuf(logFile.rdbuf());
    std::streambuf *errbuf = std::cerr.rdbuf(logFile.rdbuf());

    // do the actual work of the program;
    // GUI code and event loop would go here
    std::cout << "This would normally go to cout but goes to the log file\n";
    std::cerr << "This would normally go to cerr but goes to the log file \n";
    logFile << "This goes to the log file\n";
    // end of program body

    // restore the buffers
    std::cout.rdbuf(outbuf);
    std::cerr.rdbuf(errbuf);
}
   “rdbuf函数返回一个由基类basic_ios管理的流缓冲区的指针。重载版本允许你替换流缓冲区,返回值是原始的流缓冲区。解决方法很简单—用你的log文件的流缓冲区替换cout和cerr的流缓冲区。程序结束时,改回原来的流缓冲区。你可以看到,你仍能用logFile作为常规的输出文件。”

 “酷毙了!”终于看到了流缓冲区的威力。“嘿,如果我只想重定向一个流,比如cerr,我是不是可以简单地交换这两个缓冲区,这样就无须担心怎样恢复它们了,象下面那样 :”

int main()
{
    std::ofstream logFile("out.txt");
    std::streambuf *saveBuf=cerr.rdbuf(logFile.rdbuf());
    logFile.rdbuf(saveBuf);
    // remainder of program...
}

   “诶,并不这么简单,” Guru叹了口气 “这样的代码过不了编译关。basic_ios::rdbuf函数并不是虚拟函数。ofstream只提供了一个rdbuf函数,此函数没有参数,返回指向对象内部的文件缓冲流。也就是说
std::ofstream::rdbuf(void) 隐藏了 std::ostream::rdbuf(std::streambuf *)。

    “你可以通过一个指向std::ostream的指针操纵你的log文件,如下:”

int main()
{
    std::ofstream logFile("err.log");
    std::ostream * baseManipulator = &logFile;
    baseManipulator->rdbuf(std::cerr.rdbuf(baseManipulator->rdbuf()));
}

  这些代码能通过编译,但无法正常工作。神圣的标准并没有规定ofstream的具体实现方法。有可能某种编译器实现了ofstream的功能而忽略了ostream::rdbuf函数,直接操作文件缓冲流。标准没有要求ofstream::close调用ostream:: rdbuf来决定闭哪个缓冲。因为此函数总是返回指向ofstream对象内部的filebuf,ofstream总会关闭自己内部的filebuf对象,于是cerr就没有了有效的缓冲区。所以,你还是要恢复原来cerr的流缓冲区,main退出前加入如下:

std::cerr.rdbuf(baseManipulator->rdbuf());


   “如果你想重定向cin的输入,你必须预防类似的事情,ifstream也是这样的。”

  Guru转身准备离开了,又停住了说:“这周六我想请人到我家来。没什么特殊的,一个交际晚会。欢迎你和温迪,及你们各自的朋友前来。”

   我开口正准备优雅的谢绝。“啊!”


   “他的意思是他很乐意过去,”温迪笑得很甜。我的小腿被她踹了一下,痛得厉害,只能含泪无助地点点头。Guru微笑着飘然而去。

  我等Guru和疼痛都消失以后,对温迪大叫起来:“早晚修理你。”

- - - - - - - - - - - - - - - - - - - - - - - - -

  “好啊,”我对弗奈尔说,“别忘了通知我一声。”

    “好的”

  我就要走了,又问了一句:“诶,林格没却那儿吗,我的意思是她和珍妮是搭档。”

  “不,”弗奈尔说。“我们这里需要她。”

  “谢谢。明天见。”

    在下一个值班时,我核查了一下我允许看的职责花名册。名单很长,上面的名字各式各样。象Jupiter(木星)这样的名字归联合国管啊,所以这次任务肯定是联合国发起的。各个地方的人员都有,主要来自美利坚联合体和亚洲联盟,因为它们投入的科研经费最多。没有谁的名字被打个记号,表明已被重新分配任务。但我能看出哪些名字已不在花名册的日常值勤中了,他们肯定去别的地方了。

  我慢慢看下去,看得很安静。

  我终于发现了怪事:没有一个亚洲人被再分配到发掘现场或是圆屋。

[感谢]

        感谢Angelika Langer和Klaus Kreft在他们的著作Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference [1]中对这方面以及其它与流相关的话题作了清楚的讨论。

[注释]

[1] Angelika Langer and Klaus Kreft. Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference (Addison-Wesley, 2000). 

[关于作者]

Herb Sutter

是个独立顾问,也是ISO/ANSI C++标准委员会的秘书。你可通过[email protected].联系他

Jim Hyslop

Leitch Technology International Inc.资深的软件设计师,你可通过[email protected]联系他

本文地址:http://com.8s8s.com/it/it2450.htm