Подтвердить что ты не робот

С++: перенаправление STDOUT

В моем приложении я хочу перенаправить вывод, который обычно переходит в поток stdout к функции, которую я определяю. Я читал, что вы можете перенаправить stdio в файл, так почему бы не использовать функцию?

Например:

void MyHandler( const char* data );

//<<Magical redirection code>>

printf( "test" );
std::cout << "test" << std::endl;

//MyHandler should have been called with "test" twice, at this point
  • Как я могу достичь этого/подобного поведения?
4b9b3361

Ответ 1

@Konrad Rudolph прав, вы можете полностью сделать это, легко, по крайней мере, для cout/cerr/clog. Вам даже не нужна ваша собственная реализация streambuf, просто используйте ostringstream.

// Redirect cout.
streambuf* oldCoutStreamBuf = cout.rdbuf();
ostringstream strCout;
cout.rdbuf( strCout.rdbuf() );

// This goes to the string stream.
cout << "Hello, World!" << endl;

// Restore old cout.
cout.rdbuf( oldCoutStreamBuf );

// Will output our Hello World! from above.
cout << strCout.str();

То же самое работает для cerr и clog, но по моему опыту, который НЕ будет работать для stdout/stderr в целом, поэтому printf там не будет выводиться. cout выходит на стандартный вывод, но перенаправление cout не перенаправляет все stdout. По крайней мере, это был мой опыт.

Если количество данных должно быть небольшим, функция freopen/setbuf работает нормально. Я закончил тем, что перенаправлял трубку в дурацкое дублирование /dup 2.

Обновление: я написал сообщение в блоге, в котором показан метод dup2, который я использовал, и вы можете читать здесь. Он написан для OS X, но может работать в других вариантах Unix. Я серьезно сомневаюсь, что это будет работать в Windows. Cocoa версия того же самого здесь.

Ответ 2

Все остальные ответы неверны. Вы можете сделать это, и вам не нужно прибегать к freopen, а также к любым другим C или нестандартным функциям.

Вместо этого вам нужно создать свою собственную std::streambuf, т.е. собственный буфер потока.

После этого вы можете перенаправить поток cout, переключив буфер:

your_stream_buffer new_buffer;
streambuf* old_buffer = cout.rdbuf(&new_buffer);

cout << "Hello"; // This will be redirected to `new_buffer`.

// Restore original buffer:
cout.rdbuf(old_buffer);

Так как Ive никогда не делал этого сам, я не могу точно сказать, как должна выглядеть реализация streambuf. Мой совет - посмотреть документацию и выполнить фиктивную реализацию, которая печатает диагностику для всех реализованных функций. Это должно помочь выяснить, как работает класс.

В качестве альтернативы, посмотрите на С++-справочник по вашему выбору, который описывает класс streambuf.

Ответ 4

Объект std::cout имеет фиксированное значение и выводит его в стандартный поток. Пользователь вашей программы получает контроль над подключением стандартного подключения, а не к вам. Что вы можете сделать, так это решить, хотите ли вы писать в файл, в стандартную версию или в любой другой выходной поток. Поэтому в вашем коде вы переключаете поток, который вы пишете.

Опять же, точка записи в стандартный поток - это дать пользователю гибкость в выборе места выхода. Вы не должны перенаправлять стандартную версию; это то, что пользователь должен иметь свободу делать.

Другое дело, что вы не должны смешивать C IO и С++ IO в программе на С++. Выберите библиотеку IO, с которой вы хотите работать, и придерживайтесь ее.

Тем не менее, вы можете на С++ довольно элегантно переключать потоки для функции, которая вводит ввод, путем шаблонирования функции обработчика по параметрам шаблона std::basic_istream<>. Затем функция будет считывать свой вход из входного потока независимо от реального потока потока, с которым он работает. Вот пример:

#include<iostream>
#include<fstream>
#include<string>

template<class Ch, class Tr>
void dodge_this(std::basic_istream<Ch, Tr>& in)
{
    // in is an input stream. read from it as you read from std::cin.
}

int main(int argc, char* argv[])
{
    if( std::string(argv[1]) == "cin" ) {
        dodge_this(std::cin);
    } else if( std::string(argv[1]) == "file" ) {
        std::ifstream file("input.txt");
        dodge_this(file);
    } else {
        dodge_this(dev_null_stream);  // i just made that up. you get the idea.
    }
}

Ответ 5

Можно отключить stdin/stdout путем разыменования его указателя:

FILE fp_old = *stdout;  // preserve the original stdout
*stdout = *fopen("/dev/null","w");  // redirect stdout to null
HObject m_ObjPOS = NewLibraryObject(); // call some library which prints unwanted stdout
*stdout=fp_old;  // restore stdout

Ответ 6

Ниже приведена не лучшая реализация, но она должна работать для большей производительности...

using std::cout;
using std::endl;
using std::string;
using std::stringstream;

class Handler {
        public:
                Handler() {
                };
                virtual ~Handler() {
                };
                void operator<<(string str) {
                        this->stream << str;
                };
                string print(void) {
                        return this->stream.str();
                }
        private:
                stringstream stream;
};

int main(int argc, char ** argv) {
        Handler handle;
        handle << argv[1];
        cout << handle.print() << endl;
        return 0;
}

Я просто помещаю небольшую основную функцию под вас, чтобы вы могли попробовать и посмотреть, работает ли она так, как вы хотите.

Ответ 7

Вы можете использовать sprintf для записи в массив символов, а затем прочитать значение:

char buf[1024];
sprintf(buf, "test");
MyHandler(buf);

есть также snprintf и несколько других в зависимости от платформы