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

0

Я хочу 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. почему это?

  • 0
    В общем случае, не рекомендуется выводить из std :: stream - у него нет виртуального деструктора, и ваш код показывает, как выходные операции на самом деле не работают с производным классом, потому что вы возвращаете ссылку на базовый класс. Я постараюсь расширить до полного ответа, если кто-то не победит меня :)
  • 2
    Ваш шаблон должен возвращать parallel_cout& , а не универсальный ostream& .
Показать ещё 5 комментариев
Теги:
ostream
cout
iostream

4 ответа

7
Лучший ответ

Способ изменения поведения 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().

  • 0
    Я не знаю об этих методах (синхронизация и переполнение), вы знаете хороший учебник?
  • 1
    Библиотека IOStreams объясняется в «Стандартной библиотеке C ++» Николая Йосуттиса, и, очевидно, объяснение превосходное (хотя я могу быть предвзятым, написав, в частности, оригинальную версию разделов о создании потоковых буферов). Существует также «IOStreams and Locales» Анжелики Лангер и Клауса Крефта, но это целая книга, посвященная только I / O. В противном случае, если вы ищете в Google «streambuf» и «James Kanze» или «Dietmar Kuehl», вы должны найти множество ресурсов.
Показать ещё 3 комментария
3

Как сказал Дитмар, вы хотите, чтобы ваш собственный 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";
}
  • 0
    Хорошее усилие, хотя я предпочитаю свою версию: для одного я думаю, что это проще, а для другого он не создает конечный префикс после последней новой строки. Он также ставит префикс первой строки без ложного перевода строки. Наконец, он проходит через сброс в основной буфер потока. Я понимаю, что ваша версия буферизована, но без очистки от sync() которая причиняет некоторый вред. Моя версия не буферизована для простоты, хотя я понимаю, что отсутствие буферизации потоковых буферов представляет потенциальную проблему с производительностью.
1

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

template<typename T>
void print(T content)
{
    cout<<whatever your prefix is;
    cout<<content;
}
0

Ваш оператор вывода возвращает ссылку на базовый класс (std::ostream), что означает, что следующий вызов operator << не будет использовать вашу реализацию.

Изменение вашего оператора вывода на это должно устранить вашу проблему:

template <typename T>
parallel_cout& operator<< (const T& val)
{
    out << "prefix " << val;
    return *this;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню