37

I want to define a class MyStream so that:

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

gives output

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

Basically, I want a "[blah]" inserted at the front, then inserted after every non terminating std::endl?

The difficulty here is NOT the logic management, but detecting and overloading the handling of std::endl. Is there an elegant way to do this?

Thanks!

EDIT: I don't need advice on logic management. I need to know how to detect/overload printing of std::endl.

OmarOthman
  • 1,718
  • 2
  • 19
  • 36
anon
  • 41,035
  • 53
  • 197
  • 293

7 Answers7

35

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
>
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 1
    -1: You copied my advice without giving credit, instead leaving criticism (huh?), and furthermore only went halfway. You didn't override `overflow`, so your code fails if `putc('\n')` results in overflow before `sync` gets called. – Potatoswatter Feb 06 '10 at 13:16
  • Ohhh, overflow never happens in a `stringbuf`. Sorry… it's sunrise here… sleepy. – Potatoswatter Feb 06 '10 at 13:24
  • 1
    Yeah… if you apply a token edit I'll reverse that vote… tomorrow – Potatoswatter Feb 06 '10 at 13:31
  • 3
    @potatoswatter: That's a bit egotistical to think I copied you. No copying involved. You came up with a description of possibilities. I came up with a solution. And we did it completely independently. Guss what: I have done this kind of thing before :-) – Martin York Feb 06 '10 at 22:27
  • I would like a flush to be triggered by a read on std::cin or end of program. Do you have a suggestion? – Macbeth's Enigma Jul 07 '13 at 21:18
  • @Macbeth'sEnigma: That is already done. If you read from std::cin the std::cout is automatically flushed first. When an application exists all buffers will be flushed. – Martin York Jul 07 '13 at 21:47
  • `myStream << "This does not print.";` whereas `std::cout << "This does print.";` I have to force it with `myStream << "This will print now" << std:flush;` I am hoping to replicate the cout behavior of triggering at end or std::cin. – Macbeth's Enigma Jul 07 '13 at 23:44
  • From Joseuutis The C++ Standard Library, this works: `std::cin.tie(&myStream);` Is there a way to incorporate that in the MyStream class? I am still trying to get end of program to trigger a flush. – Macbeth's Enigma Jul 08 '13 at 00:35
  • FWIW, I put this in the form of an SO question. http://stackoverflow.com/questions/17532460/can-a-custom-stream-buffer-automatically-flush-at-program-exit-and-when-requesti – Macbeth's Enigma Jul 08 '13 at 17:20
  • Question, does one need to call `flush` from the destructor of `MyStream`, or does `std::ostream` does it for you? It is not clear from http://en.cppreference.com/w/cpp/io/basic_ostream/%7Ebasic_ostream – alfC Apr 26 '18 at 00:53
  • @alfC: Well spotted. Added a destructor (to the buffer). – Martin York Apr 26 '18 at 01:21
  • Curious, I though you would add a `[virtual?] ~MyStream(){flush();}`. Wouldn't that be simpler? and equivalent? – alfC Apr 26 '18 at 02:37
18

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;
}
Timbo
  • 27,472
  • 11
  • 50
  • 75
  • 3
    Unfortunately, this does not detect just std::endl - it detects any manipulator that has no parameters. But I guess in the <<() operator you could compare f with std::endl, and do special processing if they are the same thing. –  Feb 06 '10 at 10:28
  • 1
    In the middle of editing the post I wasn't sure if the comparison to endl would work, but I tested it and it does :-) – Timbo Feb 06 '10 at 10:32
  • 2
    This won't work for the same reason as you can never derive from `ostream`: formatting inserters are defined to return an `ostream&`. Even if you override all the default ones, there are still user-defined `ostream &operator<<( ostream &, my_type const & )`'s floating around. Then `my_stream << my_type(5) << endl;` calls `operator<<( ostream&, manipulator )`, not `operator<<( MyStream&, manipulator )`. – Potatoswatter Feb 06 '10 at 11:37
  • The code as it stands turns off all the other manipulators - you need to call them in the "else" logic: f( *this ); –  Feb 06 '10 at 11:38
  • 2
    This should work, except that GCC complains "assuming cast to type 'std::basic_ostream >& (*)(std::basic_ostream >&)' from overloaded function" on the comparison of `f` to `std::endl`. @Neil: This doesn't "turn off" any manipulators; they are passed to `std::cout` and applied there. – Jon Purdy Feb 06 '10 at 12:40
  • 1
    @Gabriel, you should just do as GCC suggests: cast `if (f == (std::basic_ostream& (*)(std::basic_ostream&)) &std::endl) { ... }`. – j0nnyf1ve Apr 26 '15 at 02:16
2

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.

J.T. Blum
  • 324
  • 1
  • 9
2

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!

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • You are over complicating things. Use the std::stringbuf class to do the buffering. – Martin York Feb 06 '10 at 11:53
  • @Martin: He asked how to change the behavior of `ostream`. I can only assume he wants to work with files. Anyway, even if you use `stringbuf`, you're still in the same situation of deriving, overloading, and substituting: it doesn't simplify anything. Forgetting the whole streams mess and performing search-and-replace on the result of `mystringstream.str()`, on the other hand, would be very reasonable! – Potatoswatter Feb 06 '10 at 13:11
  • @Patatoswatter: Please read my solution. I think 30 lines (including comments) is relatively trivial. – Martin York Feb 06 '10 at 18:33
  • @Martin: yes, it's simple, but on the scale of complexity it's somewhere between what I thought and "use the std::stringbuf class". – Potatoswatter Feb 07 '10 at 00:24
1

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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

I had the same question, and I thought that Potatoswatter's second answer had merit: "Just declare a new endl in your own namespace, or better, a manipulator which isn't called endl at all!"

So I found out how to write a custom manipulator which is not hard at all:

#include <sstream>
#include <iostream>

class log_t : public std::ostringstream
{
    public:
};


std::ostream& custom_endl(std::ostream& out)
{
    log_t *log = dynamic_cast<log_t*>(&out);
    if (log)
    {
        std::cout << "custom endl succeeded.\n";
    }
    out << std::endl;
    return out;
}

std::ostream& custom_flush(std::ostream& out)
{
    log_t *log = dynamic_cast<log_t*>(&out);
    if (log)
    {
        std::cout << "custom flush succeeded.\n";
    }
    out << std::flush;
    return out;
}

int main(int argc, char **argv)
{
    log_t log;

    log << "custom endl test" << custom_endl;
    log << "custom flush test" << custom_flush;

    std::cout << "Contents of log:\n" << log.str() << std::endl;
}

Here's the output:

custom endl succeeded.
custom flush succeeded.
Contents of log:
custom endl test
custom flush test

Here I've created two custom manipulators, one that handles endl and one that handles flush. You can add whatever processing you want to these two functions, since you have a pointer to the log_t object.

Eric Sokolowsky
  • 134
  • 2
  • 8
0

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.