Получите std :: ostream из std :: cout или std :: ofstream (файл)

как привязать a 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;
}

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

14.12.2008 20:41:19
5 ОТВЕТОВ
РЕШЕНИЕ
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 находится в чистом состоянии.

67
14.12.2008 21:17:40
Ох, мне нравится std :: streambuf * лучше, чем std :: ostream *
Tom 14.12.2008 20:52:22
но если что-то сработает, у вас останется висячий указатель в cout вашего streambuf * теперь уничтоженного ofstream. так что будьте осторожны, лучше напишите метод RAII.
Johannes Schaub - litb 14.12.2008 21:04:01
Мне не нравится угон std :: cout. Это означает, что cout и printf больше не эквивалентны, и я полагаю, что многие разработчики считают это само собой разумеющимся.
Tom 15.12.2008 02:12:39
Могу ли я так или иначе использовать этот подход, если он outявляется членом класса?
René Nyffenegger 14.07.2017 12:32:12
Как закрыть файл, в данный момент связанный с объектом в первом случае of.open("file.txt");?
InvincibleWolf 3.09.2017 03:41:35

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

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

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

25
14.12.2008 20:48:12
Это не кажется более безопасным, чем исходный код.
Brian 22.12.2008 20:46:20
Да, это так. Исходный код имеет утечку памяти, если во время обработки возникает исключение (* fp << "Hello World" << std :: endl).
Tom 23.12.2008 03:58:42
Почему этот ответ получил мой голос в том, что первый ответ нарушает мое старое правило «Не связывайтесь с внутренностями других объектов». То, что вы можете заменить rdbuf, не означает, что вы должны это делать.
dex black 28.02.2011 11:06:50
Код Херба, использующий ?:оператор с 2-м и 3-м аргументами различных типов и lvalue / rvalue-ness, недопустим и не компилируется с современными компиляторами, такими как Comeau Online или MSVC 10.0. Я отправил ему письмо об этом. Но пока это не исправлено, возможно, отметьте в ответе, что связанный с GOTW код недействителен. Cheers,
Cheers and hth. - Alf 20.08.2011 02:46:54
std::ofstream of;
std::ostream& out = condition ? std::cout : of.open(filename);
10
2.04.2011 05:16:51
Как это можно скомпилировать? Возвращаемый тип std::ofstream::openis void.
Haoshu 14.09.2017 22:19:34

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

std::ostream& output = (condition)?*(new std::ofstream(filename)):std::cout;
-1
29.11.2017 16:42:36
Никогда не используйте сырье new, особенно не так.
Felix Dombek 8.02.2019 14:58:29

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

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;
}
-4
24.05.2019 22:58:47