Implementing a No-Op Std::Ostream

Implementing a no-op std::ostream

To prevent the operator<<() invocations from doing formatting, you should know the streamtype at compile-time. This can be done either with macros or with templates.

My template solution follows.

class NullStream {
public:
void setFile() { /* no-op */ }
template<typename TPrintable>
NullStream& operator<<(TPrintable const&)
{ return *this; } /* no-op */
}

template<class TErrorStream> // add TInfoStream etc
class Logger {
public:
TErrorStream& errorStream() {
return m_errorStream;
}

private:
TErrorStream m_errorStream;
};

//usage
int main() {
Logger<std::ofstream> normal_logger; // does real output
normal_logger.errorStream().open("out.txt");
normal_logger.errorStream() << "My age is " << 19;

Logger<NullStream> null_logger; // does zero output with zero overhead
null_logger.errorStream().open("out.txt"); // no-op
null_logger.errorStream() << "My age is " << 19; // no-op
}

Since you have to do this at compile-time, it is of course quite inflexible.

For example, you cannot decide the logging level at runtime from a configuration file.

Standard no-op output stream

You need a custom streambuf.

class NullBuffer : public std::streambuf
{
public:
int overflow(int c) { return c; }
};

You can then use this buffer in any ostream class

NullBuffer null_buffer;
std::ostream null_stream(&null_buffer);
null_stream << "Nothing will be printed";

streambuf::overflow is the function called when the buffer has to output data to the actual destination of the stream. The NullBuffer class above does nothing when overflow is called so any stream using it will not produce any output.

Implementing a no-op std::ostringstream that can be accepted as a std::ostringstream reference

The easiest way to create a non-operational stream is to actually not create a custom stream class but rather to disable an existing stream. For example, you can disable formatting to an std::ostream by setting its stream buffer to null:

std::ostringstream out;
out.std::ostream::rdbuf(0);
// any attempt to write anything to out will fail.

If you need a stream which successfully fails to format data you can create a stream buffer which doesn't store any bytes and is always successful. However, when using this stream buffer the actually formatting will be performed:

struct nullbuf: std::streambuf {
std::streambuf::int_type overflow(std::streambuf::int_type c) {
return std::char_traits<char>::not_eof(c);
}
};
// ...
nullbuf buf;
std::ostringstream out;
out.std::ostream::rdbuf(&buf);

Note that I would also recommend not to have functions take a std::ostringstream as arguments. Instead, any function which doesn't construct the stream should travel in terms of std::ostream&. If your existing interfaces already take an std::ostringstream you can create a a null stream by deriving from std::ostringstream and setting the stream buffer appropriately:

class onullstream
: private virtual nullbuf
, public std::ostringstream {
public:
nullstring()
: std::ios(this)
, std::ostringstgream() {
this->std::ostream::rdbuf(this);
}
};

Is there a null std::ostream implementation in C++ or libraries?

If you have boost, then there's a null ostream & istream implementation available in boost/iostreams/device/null.hpp . The gist of it:

#include "boost/iostreams/stream.hpp"
#include "boost/iostreams/device/null.hpp"
...
boost::iostreams::stream< boost::iostreams::null_sink > nullOstream( ( boost::iostreams::null_sink() ) );
...

A custom ostream

A custom destination for ostream means implementing your own ostreambuf. If you want your streambuf to actually buffer (i.e. don't connect to the database after each character), the easiest way to do that is by creating a class inheriting from std::stringbuf. The only function that you'll need to override is the sync() method, which is being called whenever the stream is flushed.

class MyBuf : public std::stringbuf
{
public:
virtual int sync() {
// add this->str() to database here
// (optionally clear buffer afterwards)
}
};

You can then create a std::ostream using your buffer:

MyBuf buff;
std::ostream stream(&buf)

Most people advised against redirecting the stream to a database, but they ignored my description that the database basically has a single blob field where all text is going to.
In rare cases, I might send data to a different field. This can be facilitated with custom attributes understood by my stream. For example:

MyStream << "Some text " << process_id(1234) << "more text" << std::flush

The code above will create a record in the database with:

blob: 'Some text more text'
process_id: 1234

process_id() is a method returning a structure ProcessID. Then, in the implementation of my ostream, I have an operator<<(ProcessID const& pid), which stores the process ID until it gets written. Works great!

Printing to nowhere with ostream

I've used:

std::ostream bitBucket(0);

recently without problems, although it was flagged as having some potential problems if you looked at it from a certain angle (see the link below).

Aside: From what I understand (and I'm not entirely sure of this), that call above eventually ends up calling basic_ios::init(0) and, because that's a NULL pointer being passed in, it sets the stream state, as returned by the rdstate() function, to the badbit value.

This in turn prevents the stream from outputting any more information, instead just tossing it away.

The following program shows it in action:

#include <iostream>

int main (void) {
std::ostream bitBucket(0);
bitBucket << "Hello, there!" << std::endl;
return 0;
}

The page where I got it from also had this as a probably-cleaner solution (slightly modified to remove the duplication of my first solution above):

#include <iostream>

class null_out_buf : public std::streambuf {
public:
virtual std::streamsize xsputn (const char * s, std::streamsize n) {
return n;
}
virtual int overflow (int c) {
return 1;
}
};

class null_out_stream : public std::ostream {
public:
null_out_stream() : std::ostream (&buf) {}
private:
null_out_buf buf;
};

null_out_stream cnul; // My null stream.

int main (void) {
std::cout << std::boolalpha;

//testing nul

std::cout << "Nul stream before: " << cnul.fail() << std::endl;
cnul << "Goodbye World!" << std::endl;
std::cout << "Nul stream after: " << cnul.fail() << std::endl;
}

creating an ostream

As it is for education, as you say, i will show you how i would do such a thingy. Otherwise, stringstream is really the way to go.

Sounds like you want to create a streambuf implementation that then writes to a vector / deque. Something like this (copying from another answer of me that targeted a /dev/null stream):

template<typename Ch, typename Traits = std::char_traits<Ch>,
typename Sequence = std::vector<Ch> >
struct basic_seqbuf : std::basic_streambuf<Ch, Traits> {
typedef std::basic_streambuf<Ch, Traits> base_type;
typedef typename base_type::int_type int_type;
typedef typename base_type::traits_type traits_type;

virtual int_type overflow(int_type ch) {
if(traits_type::eq_int_type(ch, traits_type::eof()))
return traits_type::eof();
c.push_back(traits_type::to_char_type(ch));
return ch;
}

Sequence const& get_sequence() const {
return c;
}
protected:
Sequence c;
};

// convenient typedefs
typedef basic_seqbuf<char> seqbuf;
typedef basic_seqbuf<wchar_t> wseqbuf;

You can use it like this:

seqbuf s;
std::ostream os(&s);
os << "hello, i'm " << 22 << " years old" << std::endl;
std::vector<char> v = s.get_sequence();

If you want to have a deque as sequence, you can do so:

typedef basic_seqbuf< char, char_traits<char>, std::deque<char> > dseq_buf;

Or something similar... Well i haven't tested it. But maybe that's also a good thing - so if it contains still bugs, you can try fixing them.

std::ostream that invokes a callback for each line

If I understand correctly, you want to unconditionally flush at
end of line, and only at end of line. To do this, you must
implement your own streambuf; it could be based on
std::stringbuf, but if you're only concerned with output,
and not worried about seeking, it's probably just as easy to do
it yourself.

Something like the following should do the trick:

class LineBufferedOutput : public std::streambuf
{
std::vector<char> myBuffer;
protected:
int overflow( int ch ) override
{
myBuffer.push_back( ch );
if ( ch == '\n' ) {
// whatever you have to do...
}
// return traits::eof() for failure...
}
};

I'm not sure what you mean by implementing your own tokenizer;
there's no tokenization involved. You do have to look at each
character, in order to compare it to '\n', but that's all.

And you ignore any explicit requests to sync().

Own output stream (mock cout)

You could do the following horror:

ostream nullstream(0);
ostream& log = nullstream;

Passing null to the constructor of ostream will set the badbit flag and therefore discard all writes.



Related Topics



Leave a reply



Submit