Есть ли способ (в идеале простой и элегантный) регистрировать stdin
и stdout
?
Обратите внимание, что я нет намерен перенаправлять потоки. Я хочу, чтобы стандартные потоки оставались функциональными для связи с другим программным обеспечением, в то время как также записывает все межпроцессное взаимодействие в какой-либо файл.
Просто запишите ввод, который вы получаете, в свой файл, а также вывод, который вы записываете? Да, это может быть небольшое дублирование кода, но это облегчит форматирование ведения журнала по вашему желанию или когда-нибудь будет использовать хорошую существующую структуру ведения журнала.
В противном случае вы можете просмотреть список «Связанных» вопросов, перечисленных в правой колонке здесь.
«Обратите внимание, что я не собираюсь перенаправлять потоки». Я думаю, это именно то, что вы хотите сделать ... вы хотите, чтобы поток перенаправлялся на ваш регистратор, который затем снова его выдает.
@PaulR На самом деле, я изначально искал решение в C++, но я «тройник» может работать отлично!
@UKMonkey Я имел в виду, что записи только что в файл мне недостаточно. Но вы правы, в конце концов я хочу перенаправить его куда-то, где он записан в файл а также output.
@Hagadol, вы могли бы создать свой собственный поток, который обертывает cout и файловый поток ... но это будет проще, если вам это сойдет с рук
Можно перенаправить потоки C++ (std::cin
, std::cout
), манипулируя буферами потоков. Также возможно получение класса из буфера потока и его реализация для записи в два буфера. Насколько я знаю, нет способа переносимого переноса с файловыми потоками C, такими как stdin
и stdout
(можно перенаправить с помощью freopen()
, но нельзя перенаправить на то, что записывает в два выхода). Решения под C не переносимы. В Unix более простым подходом было бы (в зависимости от командной оболочки) использовать командную строку tee input.log | you_program | tee output.log
.
@PaulR, использующий «тройник», был самым разумным решением, которое я нашел, поэтому, если вы (или кто-либо другой) готовы ответить на него, я приму его!
@Hagadol: рад, что идея работает для вас - я добавил ответ для будущих читателей.
В качестве @PaulR предлагает вы можете использовать внешний процесс, такой как tee (в Linux/Mac/Unix), или написать свой собственный процесс для чтения из стандартного ввода в цикле и записи в стандартный вывод и в другой файл.
Я сделал это много лет назад с std::basic_ios::rdbuf для std::cout
. Все, что нужно сделать, это определить класс (см. std::filebuf
и std::streambuf
):
class tee_buf : public std::filebuf {
public:
// Not an owing pointer
tee_buf(std::streambuf * other_stream)
: m_other_stream(other_stream) {}
void swap( tee_buf& rhs );
// No need to override open/close since we need to manage only the file.
// The open/close calls don't touch the other_stream.
protected:
int_type overflow(int_type c = traits_type::eof()) override;
// The parent calls this->overflow(), but then still need to call
// m_other_stream->sync(). This is problematic since m_other_stream
// gets flushed twice.
int sync() override;
pos_type seekoff( off_type off,
std::ios_base::seekdir dir,
std::ios_base::openmode which) override {
return pos_type(off_type(-1)); // ???
}
pos_type seekpos( pos_type sp,
std::ios_base::openmode which) override {
return pos_type(off_type(-1)); // ???
}
....
Это более эффективно для интенсивного ввода-вывода, поскольку позволяет избежать посредников. Но в большинстве случаев тройниковое решение проще и предпочтительнее. Если производительность является проблемой (а в большинстве случаев это не так), то можно сделать так, чтобы оба буфера потока совместно использовали один буфер памяти. Также можно использовать асинхронный ввод-вывод для параллельной записи в оба потока.
Использование с утечкой памяти:
std::cout.rdbuf(new tee_buf(std::cout.rdbuf());
Использование без утечки памяти:
Напишите класс RAII, содержащий tee_buf
, чтобы сохранить оригинал и установить новый std::cout.rdbuf()
. При разрушении восстановить состояние std::cout.rdbuf()
. Создайте единственный экземпляр этого класса, который будет выполнять грязную работу по его созданию и уничтожению.
Что касается стиля C stdout
: я не верю, что есть способ переопределить его поведение. Максимум можно поиграться с буферной памятью, но этого недостаточно для получения желаемого функционала. Единственное, что можно сделать с stdout
, — это использовать tee
-подобное решение.
Спасибо за ваш ответ! Вариант 2 больше похож на то, что я изначально намеревался сделать; но теперь я верю, что первый вариант более элегантен в контексте моего приложения. Это, однако, не умаляет полезности вашего ответа для других читателей!
Должно быть довольно просто это сделать с
tee
.