从std :: cout或std :: ofstream(文件)获取std :: ostream

mav*_*vam 48 c++ iostream exception-handling

如何根据某个程序条件将a绑定std::ostream到一个std::cout或一个std::ofstream对象?虽然这有多种原因无效,但我想实现在语义上等同于以下内容的东西:

std::ostream out = condition ? &std::cout : std::ofstream(filename);
Run Code Online (Sandbox Code Playgroud)

我见过一些不例外的示例,例如来自http://www2.roguewave.com/support/docs/sourcepro/edition9/html/stdlibug/34-2.html的示例:

int main(int argc, char *argv[])
{
  std::ostream* fp;                                           //1
  if (argc > 1)
     fp = new std::ofstream(argv[1]);                         //2
  else
     fp = &std::cout                                          //3

  *fp << "Hello world!" << std::endl;                         //4
  if (fp!=&std::cout) 
     delete fp;
}
Run Code Online (Sandbox Code Playgroud)

有谁知道一个更好的,异常安全的解决方案?

Joh*_*itb 66

std::streambuf * buf;
std::ofstream of;

if(!condition) {
    of.open("file.txt");
    buf = of.rdbuf();
} else {
    buf = std::cout.rdbuf();
}

std::ostream out(buf);
Run Code Online (Sandbox Code Playgroud)

这将cout或输出文件流的基础streambuf与out相关联.之后,您可以写入"out",它将最终出现在正确的目的地.如果你只是希望一切std::cout都进入文件,你也可以这样做

std::ofstream file("file.txt");
std::streambuf * old = std::cout.rdbuf(file.rdbuf());
// do here output to std::cout
std::cout.rdbuf(old); // restore
Run Code Online (Sandbox Code Playgroud)

第二种方法的缺点是它不是例外.您可能想要编写一个使用RAII执行此操作的类:

struct opiped {
    opiped(std::streambuf * buf, std::ostream & os)
    :os(os), old_buf(os.rdbuf(buf)) { }
    ~opiped() { os.rdbuf(old_buf); }

    std::ostream& os;
    std::streambuf * old_buf;
};

int main() {
    // or: std::filebuf of; 
    //     of.open("file.txt", std::ios_base::out);
    std::ofstream of("file.txt");
    {
        // or: opiped raii(&of, std::cout);
        opiped raii(of.rdbuf(), std::cout);
        std::cout << "going into file" << std::endl;
    }
    std::cout << "going on screen" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

现在,无论发生什么,std :: cout都处于干净状态.

  • 我不喜欢劫持std :: cout.这意味着cout和printf不再等价,这是我想象很多开发人员认为理所当然的事情. (2认同)
  • 如何在第一种情况下of.open(“ file.txt”);`关闭当前与对象关联的文件? (2认同)

Tom*_*Tom 25

这是例外安全的:

void process(std::ostream &os);

int main(int argc, char *argv[]) {
    std::ostream* fp = &cout;
    std::ofstream fout;
    if (argc > 1) {
        fout.open(argv[1]);
        fp = &fout;
    }
    process(*fp);
}
Run Code Online (Sandbox Code Playgroud)

编辑:Herb Sutter在文章" 切换流"(本周的大师)中已经解决了这个问题.

  • 你怎么样.close()? (5认同)
  • 为什么这个答案得到我的投票是第一个答案打破了我的旧规则“不要弄乱其他对象的内部结构”。仅仅因为你 ** 可以** 替换 rdbuf 并不意味着你应该。 (2认同)
  • Herb 的代码使用带有不同类型和左值/右值性质的第二个和第三个参数的“?:”运算符,该代码无效,并且无法使用 Comeau Online 或 MSVC 10.0 等现代编译器进行编译。我已经给他发邮件询问此事了。但在它修复之前,也许在答案中记下链接到 GOTW 代码是无效的。干杯, (2认同)

小智 10

std::ofstream of;
std::ostream& out = condition ? std::cout : of.open(filename);
Run Code Online (Sandbox Code Playgroud)

  • 你怎么样.close()? (5认同)
  • 怎么编译?`std :: ofstream :: open`的返回类型是`void`. (2认同)