50

Is there a way to create an ostream instance which basically doesn't do anything ?

For example :

std::ostream dummyStream(...);
dummyStream << "Nothing will be printed";

I could just create an ostringstream, but data will be buffered (and I really don't want to make anything with them, so it adds a useless overhead).

Any idea ?

[edit] Found this related question which suits my needs. However, I think it could be useful to have a answer saying how to create a valid (no badbit) output stream with standard c++.

Community
  • 1
  • 1
Maël Nison
  • 6,785
  • 6
  • 41
  • 72

6 Answers6

74

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.

john
  • 7,747
  • 26
  • 26
  • 21
    One could create a convenience class `class NullStream : public std::ostream { public: NullStream() : std::ostream(&m_sb) {} private: NullBuffer m_sb; };`, which simplifies the usage to `NullStream null_stream; null_stream << ...` – Sjoerd Aug 06 '12 at 10:54
  • 1
    This is great and I suggest adding @Sjoerd's suggestion. I implemented something effectively identical to his, not seeing his comment until just now when I came back to upvote. – sage Feb 23 '17 at 18:47
  • 3
    Just a nit: the function may yield failure turning the stream into failure state (most people won't care, though). To avoid that, you'd want to return the result of `not_eof(). Also, buffering characters is way more effective than calling a `virtual` function on the assumed to be unlikely path, i.e., I'd also recommend adding setting up a buffer which is just ignored. The overwrite would become `int overflow(int c) { return this->setp(std::begin(d_buffer), std::end(this->d_buffer); std::char_traits::not_eof(c); }`. Similarly, it may be reasonable to overwrite `xsputn()` to do nothing. – Dietmar Kühl Feb 22 '18 at 23:29
  • 3
    @DietmarKühl: Would you mind editing that into the answer, or writing your own? – einpoklum Apr 29 '18 at 17:05
36

If this is to disable logging output, your dummyStream would still cause arguments to be evaluated. If you want to minimize impact when logging is disabled, you can rely on a conditional, such as:

#define debugStream \
    if (debug_disabled) {} \
    else std::cerr

So if you have code like:

debugStream << "debugging output: " << foo() << std::endl;

No arguments will be evaluated if debug_disabled is true.

jxh
  • 66,730
  • 7
  • 105
  • 179
  • I'm sorry for necroing this question, but I really need to know this: isn't this answer better than the selected answer performance wise? If debug_disabled is a constant (or even more appropriate, a macro) the compiler might (will?) optimze the else clause away, while using a nullbuffer still causes the stream input to be processed, only to be put into a null device. Is that true? Or not? It'd be awesome if someone could shed some light on this for me. – bobismijnnaam Oct 25 '14 at 17:41
  • 1
    @bobismijnnaam: In fact, someone ripped off this answer in a question asked later in the day I posted it :-). [Link.](http://stackoverflow.com/q/11832960/315052) – jxh Oct 25 '14 at 19:10
  • Hmm, well I went with your answer anyway. The whole NullStream thing seems like too much work. – bobismijnnaam Oct 26 '14 at 11:23
  • That's a great solution, but is it possible to do something similar without having to include `iostream` or to define a throwaway global variable? – Paul Jul 01 '15 at 07:38
  • @Paul: The question was about using an `ostream`, I simply picked one that was already available. To disable logging, the log line has to fall into the `else` side. So, if the goal is to always disable, just use `true` instead of a variable. – jxh Jul 01 '15 at 13:28
  • What I mean is, let's say the line becomes: `if (true) {} else ??? << "debugging output"`. If I have no `ostream`, what can replace `???`? – Paul Jul 01 '15 at 13:31
  • @Paul: That would be a different question, so this answer would not apply. But anything that behaved like a stream would work. – jxh Jul 01 '15 at 13:43
  • @Paul: But I don't understand why you would not use the stream type that you used when debug logging is enabled. That is one of the appealing aspects of this solution. – jxh Jul 01 '15 at 13:52
  • @jxh because it's not defined for a release build. – Paul Jul 01 '15 at 13:55
  • @Paul: Assuming the release version also uses a stream for regular logging, you could just swap that into the macro instead for release builds. However, if the debug stream object is always used in the context of the macro, there are no significant reasons to not leave it enabled in release builds. – jxh Jul 01 '15 at 15:47
4

The basic method voor new stream classes is:

  1. Derive a class from std::streambuf;
  2. Override the virtual functions in that class. This is where the real work is done. In your case, empty implementations should be good enough.
  3. Derive a class from std::ostream with one member, your streambuf class.
  4. The constructor of your streamclass should forward the pointer to that member to the base constructor of std::ostream.

I'm afraid you won't get rid of the formatting step, though.

Hopefully this gives you some pointers; I don't have the time to expand this into a full answer, sorry.

Update: See john's answer for details.

Community
  • 1
  • 1
Sjoerd
  • 6,728
  • 29
  • 44
1

For runtime-controllable redirection of log messages, a self-contained solution combining the ideas of john and Sjoerd:

class DebugStream {
private:
    class NullStream : public std::ostream {
    private:
        class NullBuffer : public std::streambuf {
        public:
            int overflow(int c) override { return c; }
        } buffer_;
    public:
        NullStream() : std::ostream(&buffer_) {}
    } null_;

    std::ostream &output_;
    bool enabled_;

public:
    DebugStream(std::ostream &output = std::cout) : output_(output), enabled_(false) {}
    void enable(const bool enable) { enabled_ = enable; }

    template <typename T> std::ostream& operator<<(const T &arg) {
        if (enabled_) return output_ << arg;
        else return null_ << arg;
    }
};

extern DebugStream debug_stream;
#define TRACE_ENABLE(x) debug_stream.enable(x)
#define TRACELN(x) debug_stream << x << std::endl
#define TRACE(x) debug_stream << x

Then you can do stuff like:

TRACELN("The value of x is " << x " and the value of y is " << y);

It would also be easy to just remove the trace statements from a release version completely with #define the trace macros to empty statements.

You still need to define debug_stream somewhere global, though.

neuviemeporte
  • 6,073
  • 10
  • 48
  • 76
0

If you are concerned about the overhead of your debugger then you can write a very simple code to void out your debug messages on compilation. This is what I use for my c++ programs.

#include <iostream>
#define DEBUGGING // Define this in your config.h or not.
#ifdef DEBUGGING
/*
 * replace std::cout with your stream , you don't need to
 * worry about the context since macros are simply search
 * and replace on compilation.
 */
#define LOG_START std::cout <<
#define LOG_REDIR <<
#define LOG_END   << std::endl;
#else
#define LOG_START if(0){(void)
#define LOG_REDIR ;(void)
#define LOG_END   ;}
#endif // DEBUGGING

int main(){
LOG_START "This is a log message " LOG_REDIR "Still a log message." LOG_END;
return 0;
}

Now when making your project , check if the user wants to disable the logging , if so , just undefine the DEBUGGING macro or whatever macro you choose to check for.

Now your code will be optimized by the compiler , Because when anything is voided , it will not be included in the resulting binary(most of the time) , making the binary production ready.

antonyjr
  • 61
  • 2
  • 5
  • Compiler will not optimize out function calls. You need to define LOG_START as `if(0){(void)`, and LOG_END as `;}`. This will be optimized out even with optimization disabled - at least gcc is able to so when compiling with -O0. – Daniel Frużyński Nov 20 '19 at 13:05
  • @DanielFrużyński Thanks for the hint. I've made the changes. – antonyjr May 25 '21 at 18:10
-1

I needed a null stream that was of type ostream so I did something like this:

struct NullStream: public stringstream {
   NullStream(): stringstream() {}
};

template<typename T>
void operator<<(const NullStream&, const T&) {}

Application code:

NullStream ns;
ostream &os = ns;
os << "foo";

The real issue is all the public methods that I inherited but don't care about so I just didn't bother overriding them.

solstice333
  • 3,085
  • 25
  • 27