Получите std :: ostream из std :: cout или std :: ofstream (файл) - PullRequest
46 голосов
/ 14 декабря 2008

как связать std::ostream с std::cout или с std::ofstream объектом, в зависимости от определенного условия программы? Хотя это неверно по многим причинам, я хотел бы достичь чего-то, что семантически эквивалентно следующему:

std::ostream out = condition ? &std::cout : std::ofstream(filename);

Я видел несколько примеров, не являющихся безопасными для исключения, например, пример из 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;
}

Кто-нибудь знает лучшее, безопасное для исключения решение?

Ответы [ 5 ]

63 голосов
/ 14 декабря 2008
std::streambuf * buf;
std::ofstream of;

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

std::ostream out(buf);

Это связывает базовый поток буфера cout или потока выходного файла с 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

Этот второй метод имеет недостаток, заключающийся в том, что он не безопасен для исключений. Возможно, вы захотите написать класс, который делает это, используя 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;
}

Теперь, что бы ни случилось, std :: cout находится в чистом состоянии.

25 голосов
/ 14 декабря 2008

Это исключительная ситуация:

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);
}

Редактировать: Херб Саттер говорил об этом в статье Переключение потоков (Гуру недели) .

10 голосов
/ 02 апреля 2011
std::ofstream of;
std::ostream& out = condition ? std::cout : of.open(filename);
0 голосов
/ 24 мая 2019

У меня работает следующий простой код:

int main(int argc, char const *argv[]){   

    std::ofstream outF;
    if (argc > 1)
    {
        outF = std::ofstream(argv[1], std::ofstream::out); 
    }

    std::ostream& os = (argc > 1)? outF : std::cout;
}
0 голосов
/ 29 ноября 2017

Будучи новичком в C ++, я не знаю, насколько это безопасно для исключений, но вот как я обычно это делаю:

std::ostream& output = (condition)?*(new std::ofstream(filename)):std::cout;
...