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
.
std::endl Inserts a newline character into the output sequence os and flushes it as if by calling os.
There is rarely a need for std::endl , and getting in the habit of using it will lead to mysteriously slow code. Just use '\n' unless you know you need to flush the buffer.
We can overload the '>>' and '<<' operators to take input in a linked list and print the element in the linked list in C++. It has the ability to provide the operators with a special meaning for a data type, this ability is known as Operator Overloading.
std::endl. Inserts a new-line character and flushes the stream.
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.
Add '\n'
to the stream.
Calls flush()
on the stream
This calls pubsync()
on the stream buffer.
sync()
#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
>
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;
}
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.
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 overflow
ed or manually sync
ed 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.
Just declare a new endl
in your own namespace, or better, a manipulator which isn't called endl
at all!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With