Overload handling of std::endl?

后端 未结 7 969
广开言路
广开言路 2020-11-27 04:40

I want to define a class MyStream so that:

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


        
相关标签:
7条回答
  • 2020-11-27 04:59

    Your overloaded operators of the MyStream class have to set a previous-printed-token-was-endl flag.

    Then, if the next object is printed, the [blah] can be inserted in front of it.

    std::endl is a function taking and returning a reference to std::ostream. To detect it was shifted into your stream, you have to overload the operator<< between your type and such a function:

    MyStream& operator<<( std::ostream&(*f)(std::ostream&) )
    {
        std::cout << f;
    
        if( f == std::endl )
        {
            _lastTokenWasEndl = true;
        }
    
        return *this;
    }
    
    0 讨论(0)
  • 2020-11-27 05:03

    Agreed with Neil on principle.

    You want to change the behavior of the buffer, because that is the only way to extend iostreams. endl does this:

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

    widen returns a single character, so you can't put your string in there. put calls putc which is not a virtual function and only occasionally hooks to overflow. You can intercept at flush, which calls the buffer's sync. You would need to intercept and change all newline characters as they are overflowed or manually synced and convert them to your string.

    Designing an override buffer class is troublesome because basic_streambuf expects direct access to its buffer memory. This prevents you from easily passing I/O requests to a preexisting basic_streambuf. You need to go out on a limb and suppose you know the stream buffer class, and derive from it. (cin and cout are not guaranteed to use basic_filebuf, far as I can tell.) Then, just add virtual overflow and sync. (See §27.5.2.4.5/3 and 27.5.2.4.2/7.) Performing the substitution may require additional space so be careful to allocate that ahead of time.

    - OR -

    Just declare a new endl in your own namespace, or better, a manipulator which isn't called endl at all!

    0 讨论(0)
  • 2020-11-27 05:06

    I use function pointers. It sounds terrifying to people who aren't used to C, but it's a lot more efficient in most cases. Here's an example:

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

    If you really want to you can check for the address of endl to confirm that you aren't getting some OTHER void/void function, but I don't think it's worth it in most cases. I hope that helps.

    0 讨论(0)
  • 2020-11-27 05:06

    You can't change std::endl - as it's name suggests it is a part of the C++ Standard Library and its behaviour is fixed. You need to change the behaviour of the stream itself, when it receives an end of line . Personally, I would not have thought this worth the effort, but if you want to venture into this area I strongly recommend reading the book Standard C++ IOStreams & Locales.

    0 讨论(0)
  • 2020-11-27 05:07

    Instead of attempting to modify the behavior of std::endl, you should probably create a filtering streambuf to do the job. James Kanze has an example showing how to insert a timestamp at the beginning of each output line. It should require only minor modification to change that to whatever prefix you want on each line.

    0 讨论(0)
  • 2020-11-27 05:10

    What you need to do is write your own stream buffer: When the stream buffer is flushed you output you prefix characters and the content of the stream.

    The following works because std::endl causes the following.

    1. Add '\n' to the stream.

    2. Calls flush() on the stream

    3. This calls pubsync() on the stream buffer.

      1. This calls the virtual method sync()
      2. Override this virtual method to do the work you want.
    #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
    >
    
    0 讨论(0)
提交回复
热议问题