Я хочу ostream с префиксом в начале каждой строки, перенаправленной на cout; Я пробую это:
#include <iostream>
#include <thread>
class parallel_cout : public std::ostream
{
public:
parallel_cout(std::ostream& o):out(o){}
template <typename T>
std::ostream& operator<< (const T& val)
{
out << "prefix " << val;
return *this;
}
std::ostream& out;
};
int main()
{
parallel_cout pc(std::cout);
pc<<"a\nb"<<"c\n";
}
но у меня есть выход
prefix a
b
без c. почему это?
Способ изменения поведения std::ostream
заключается не в перегрузке любого из операторов вывода! Вместо этого вы получаете класс из std::streambuf
и переопределяете функции overflow()
virtual
функций overflow()
и sync()
. В вашем случае вы, вероятно, создадите буфер потока фильтрации, т. std::streambuf
Вы возьмете еще один std::streambuf
качестве аргумента и напишите каким-то модифицированным потоком символов в этот буфер потока.
Вот краткий пример:
#include <iostream>
class prefixbuf
: public std::streambuf
{
std::string prefix;
std::streambuf* sbuf;
bool need_prefix;
int sync() {
return this->sbuf->pubsync();
}
int overflow(int c) {
if (c != std::char_traits<char>::eof()) {
if (this->need_prefix
&& !this->prefix.empty()
&& this->prefix.size() != this->sbuf->sputn(&this->prefix[0], this->prefix.size())) {
return std::char_traits<char>::eof();
}
this->need_prefix = c == '\n';
}
return this->sbuf->sputc(c);
}
public:
prefixbuf(std::string const& prefix, std::streambuf* sbuf)
: prefix(prefix)
, sbuf(sbuf)
, need_prefix(true) {
}
};
class oprefixstream
: private virtual prefixbuf
, public std::ostream
{
public:
oprefixstream(std::string const& prefix, std::ostream& out)
: prefixbuf(prefix, out.rdbuf())
, std::ios(static_cast<std::streambuf*>(this))
, std::ostream(static_cast<std::streambuf*>(this)) {
}
};
int main()
{
oprefixstream out("prefix: ", std::cout);
out << "hello\n"
<< "world\n";
}
Потоковые буферы концептуально сохраняют внутренний буфер, который, однако, не настроен в приведенном выше примере. Каждый раз, когда в буфер вывода не std::char_traits<char>::eof()
, virtual
функция overflow()
вызывается с символом (его также можно вызвать со специальным значением std::char_traits<char>::eof()
который обычно используется для сброса буфера). Поскольку буфер не существует, для каждого символа будет вызываться overflow()
. Вся эта функция делает это, чтобы увидеть, нужно ли ему писать префикс и, если это так, записывает prefix
. В случае написания новой строки '\n'
функция запоминает, что ей нужно написать prefix
если будет написан другой символ. Затем он просто перенаправляет запись символа в базовый буфер потока.
virtual
функция sync()
используется для синхронизации потока с его внешним представлением. Для буфера потока, поддерживающего буфер, он гарантирует, что будет записан любой буфер. Поскольку prefixbuf
самом деле не хранит буфер, все, что ему нужно, это делегировать запрос sync()
в базовый буфер потока, вызвав pubsync()
.
Как сказал Дитмар, вы хотите, чтобы ваш собственный streambuf, что-то в этом общем порядке:
#include <streambuf>
#include <iostream>
class prefixer: public std::streambuf {
public:
prefixer(std::streambuf* s): sbuf(s) {}
~prefixer() { overflow('\n'); }
private:
typedef std::basic_string<char_type> string;
int_type overflow(int_type c) {
if (traits_type::eq_int_type(traits_type::eof(), c))
return traits_type::not_eof(c);
switch (c) {
case '\n':
case '\r': {
prefix = "[FIX]";
buffer += c;
if (buffer.size() > 1)
sbuf->sputn(prefix.c_str(), prefix.size());
int_type rc = sbuf->sputn(buffer.c_str(), buffer.size());
buffer.clear();
return rc;
}
default:
buffer += c;
return c;
}
}
std::string prefix;
std::streambuf* sbuf;
string buffer;
};
Чтобы использовать это, вы создаете его экземпляр из некоторого другого streambuf (который определяет, где он будет писать), а затем (обычно) создайте ostream, используя этот streambuf. Затем вы можете писать в этот ostream, и ваш префикс записывается в каждую строку вывода. Например:
int main() {
prefixer buf(std::cout.rdbuf());
std::ostream out(&buf);
out << "Test\n";
}
sync()
которая причиняет некоторый вред. Моя версия не буферизована для простоты, хотя я понимаю, что отсутствие буферизации потоковых буферов представляет потенциальную проблему с производительностью.
В качестве альтернативы вы можете написать функцию, хотя изменения синтаксиса.
template<typename T>
void print(T content)
{
cout<<whatever your prefix is;
cout<<content;
}
Ваш оператор вывода возвращает ссылку на базовый класс (std::ostream
), что означает, что следующий вызов operator <<
не будет использовать вашу реализацию.
Изменение вашего оператора вывода на это должно устранить вашу проблему:
template <typename T>
parallel_cout& operator<< (const T& val)
{
out << "prefix " << val;
return *this;
}
parallel_cout&
, а не универсальныйostream&
.