C++ cout с префиксом

Я хочу 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

без ц. почему это?

4 ответа

Решение

То, как вы изменяете поведение std::ostream не перегружая ни один из операторов вывода! Вместо этого вы извлекаете класс из std::streambuf и переопределить virtual функции overflow() а также sync(), В вашем случае вы, вероятно, создадите буфер потокового фильтра, т.е. 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";
}

Потоковые буферы концептуально сохраняют внутренний буфер, который, однако, не настроен в примере выше. Каждый раз, когда в выходном буфере нет места для символа, virtual функция overflow() вызывается с символом (также может вызываться со специальным значением std::char_traits<char>::eof() который обычно используется для очистки буфера). Поскольку нет буфера, overflow() будет вызываться для каждого персонажа. Все, что делает эта функция, это видит, нужно ли ей писать префикс и, если да, записывает prefix, В случае новой строки '\n' функция запоминает, что ей нужно написать prefix если другой символ написан. Затем он просто перенаправляет запись символа в базовый буфер потока.

virtual функция sync() используется для синхронизации потока с его внешним представлением. Для буфера потока, сохраняющего буфер, он гарантирует, что любой буфер записан. Так как prefixbuf на самом деле не сохраняет буфер, все, что ему нужно, это делегировать sync() запрос к базовому потоку буфера путем вызова pubsync(),

Как сказал Дитмар, вам нужен собственный потоковый поток, что-то в этом общем порядке:

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

Чтобы использовать это, вы создаете его экземпляр из некоторого другого потокового буфера (который определяет, куда он будет записывать), а затем (обычно) создаете ostream с помощью этого потокового буфера. Затем вы можете писать в этот ostream, и ваш префикс записывается в каждую строку вывода. Например:

int main() { 
    prefixer buf(std::cout.rdbuf());

    std::ostream out(&buf);

    out << "Test\n";
}

В качестве альтернативы вы можете написать функцию, хотя синтаксис меняется.

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;
}
Другие вопросы по тегам