Вопрос

Я хочу определить класс MyStream так что:

MyStream myStream;
myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl;

дает результат

[blah]123
[blah]56
[blah]78

По сути, я хочу, чтобы «[бла]» вставлялось спереди, а затем вставлялось после каждого непрекращающийся std::endl?

Трудность здесь НЕ в управлении логикой, а в обнаружении и перегрузке обработки std::endl.Есть ли элегантный способ сделать это?

Спасибо!

РЕДАКТИРОВАТЬ:Мне не нужны советы по управлению логикой.Мне нужно знать, как обнаружить/перегрузить печать std::endl.

Это было полезно?

Решение

Что вам нужно сделать, это написать свой собственный буфер потока:
Когда буфер потока очищается, вы выводите символы префикса и содержимое потока.

Следующее работает, потому что std::endl вызывает следующее.

1) Добавьте ' ' в поток.
2) Вызывает флеш() в потоке
2a) Это вызывает pubsync() в буфере потока.
2б) Это вызывает виртуальный метод sync().
2c) Переопределите этот виртуальный метод, чтобы он выполнял нужную вам работу.

#include <iostream>
#include <sstream>

class MyStream: public std::ostream
{
    // Write a stream buffer that prefixes each line with Plop
    class MyStreamBuf: public std::stringbuf
    {
        std::ostream&   output;
        public:
            MyStreamBuf(std::ostream& str)
                :output(str)
            {}
            ~MyStreamBuf() {
                if (pbase() != pptr()) {
                    putOutput();
                }
            }

        // When we sync the stream with the output. 
        // 1) Output Plop then the buffer
        // 2) Reset the buffer
        // 3) flush the actual output stream we are using.
        virtual int sync() {
            putOutput();
            return 0;
        }
        void putOutput() {
            // Called by destructor.
            // destructor can not call virtual methods.
            output << "[blah]" << str();
            str("");
            output.flush();
        }
    };

    // My Stream just uses a version of my special buffer
    MyStreamBuf buffer;
    public:
        MyStream(std::ostream& str)
            :std::ostream(&buffer)
            ,buffer(str)
        {
        }
};


int main()
{
    MyStream myStream(std::cout);
    myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl;
}

> ./a.out
[blah]123 
[blah]56 
[blah]78
>

Другие советы

Ваши перегруженные операторы MyStream класс должен установить флаг предыдущего напечатанного токена-был-endl.

Затем, если будет напечатан следующий объект, [blah] можно вставить перед ним.

std::endl это функция, принимающая и возвращающая ссылку на std::ostream.Чтобы обнаружить, что оно было перенесено в ваш поток, вам необходимо перегрузить operator<< между вашим типом и такой функцией:

MyStream& operator<<( std::ostream&(*f)(std::ostream&) )
{
    std::cout << f;

    if( f == std::endl )
    {
        _lastTokenWasEndl = true;
    }

    return *this;
}

Согласен с Нилом в принципе.

Вы хотите изменить поведение буфера, потому что это единственный способ расширить iostreams. endl Означает ли это:

flush(__os.put(__os.widen('\n')));

widen возвращает один символ, поэтому вы не можете поместить туда свою строку. put звонки putc которая не является виртуальной функцией и лишь изредка подключается к overflow.Вы можете перехватить в flush, который вызывает буфер sync.Вам нужно будет перехватить и изменить все символы новой строки, как они есть. overflowредактировать или вручную synced и преобразуйте их в свою строку.

Разработка класса переопределяющего буфера затруднительна, потому что basic_streambuf ожидает прямого доступа к своей буферной памяти.Это не позволяет вам легко передавать запросы ввода-вывода уже существующему basic_streambuf.Вам нужно рискнуть и предположить, что вы знаете класс буфера потока и извлекаете его из него.(cin и cout не гарантируются использование basic_filebuf, насколько я могу судить.) Затем просто добавьте virtual overflow и sync.(См. §27.5.2.4.5/3 и 27.5.2.4.2/7.) Для выполнения замены может потребоваться дополнительное пространство, поэтому будьте осторожны, чтобы выделить его заранее.

- ИЛИ -

Просто объявите новый endl в вашем собственном пространстве имен или, лучше, манипулятор, который не называется endl совсем!

Вместо того, чтобы пытаться изменить поведение std::endl, вам, вероятно, следует создать фильтрующий поток потока, чтобы выполнить эту работу.У Джеймса Канзе есть пример показано, как вставить временную метку в начало каждой выходной строки.Чтобы изменить его на любой префикс, который вы хотите в каждой строке, потребуется лишь небольшая модификация.

Я использую указатели на функции.Это звучит устрашающе для людей, которые не привыкли к C, но в большинстве случаев это намного эффективнее.Вот пример:

#include <iostream>

class Foo
{
public:
    Foo& operator<<(const char* str) { std::cout << str; return *this; }
    // If your compiler allows it, you can omit the "fun" from *fun below.  It'll make it an anonymous parameter, though...
    Foo& operator<<(std::ostream& (*fun)(std::ostream&)) { std::cout << std::endl; }
} foo;

int main(int argc,char **argv)
{
    foo << "This is a test!" << std::endl;
    return 0;
}

Если вы действительно этого хотите, вы можете проверить адрес endl, чтобы убедиться, что вы не получаете какую-либо ДРУГУЮ функцию void/void, но я не думаю, что в большинстве случаев оно того стоит.Надеюсь, это поможет.

Вы не можете изменить std::endl - как следует из названия, это часть стандартной библиотеки C++, и ее поведение фиксировано.Вам нужно изменить поведение самого потока, когда он получает конец строки.Лично я бы не подумал, что это того стоит, но если вы хотите заняться этой областью, я настоятельно рекомендую прочитать книгу. Стандартные потоки ввода-вывода C++ и локали.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top