Cancelling std::cout code lines using preprocessor
Asked Answered
W

7

5

One can remove all calls to printf() using #define printf. What if I have a lot of debug prints like std::cout << x << endl; ? How can I quickly switch off cout << statements in a single file using preprocessor?

Waters answered 7/9, 2009 at 13:54 Comment(0)
P
4

NullStream can be a good solution if you are looking for something quick that removes debug statements. However I would recommend creating your own class for debugging, that can be expanded as needed when more debug functionality is required:

class MyDebug
{
    std::ostream & stream;
  public:
    MyDebug(std::ostream & s) : stream(s) {}
#ifdef NDEBUG
    template<typename T>
    MyDebug & operator<<(T& item)
    {
      stream << item;
      return *this;
    }
#else
    template<typename T>
    MyDebug & operator<<(T&)
    {
      return *this;
    }
#endif
};

This is a simple setup that can do what you want initially, plus it has the added benefit of letting you add functionality such as debug levels etc..

Update: Now since manipulators are implemented as functions, if you want to accept manipulators as well (endl) you can add:

MyDebug & operator<<(std::ostream & (*pf)(std::ostream&))
{
  stream << pf;
  return *this;
}

And for all manipulator types (So that you don't have to overload for all manipulator types):

template<typename R, typename P>
MyDebug & operator<<(R & (*pf)(P &))
{
  stream << pf;
  return *this;
}

Be careful with this last one, because that will also accept regular functions pointers.

Proteolysis answered 7/9, 2009 at 15:9 Comment(2)
If I pass endl to this stream it gives error.. looks like T& item doesn't accept endl.. any clue how to overload it for that too ?Unsuccessful
Hello, what kind of situation does operator<<(R & (*pf)(P &)) handle? I found that everything works fine without it.Bierman
T
10

As "unwind" already said, the quick solution is a do-nothing stream. There are better implementations though:

class NullStream {
    public:
    NullStream() { }
    template<typename T> NullStream& operator<<(T const&) { return *this; }
};

You still have a slight issue with std::cout since that's a sequence of three tokens, and you really don't want to redefine std or cout individually. A simple solution is

#ifdef NDEBUG
    #define COUT std::cout
#else
    #define COUT NullStream()
#endif

COUT << "Hello, world" << std::endl;
Twicetold answered 7/9, 2009 at 14:9 Comment(0)
P
6

As a general principle logging to stdout should be avoided - far better to log to a logfile, and then you can use standard configuration tools to change log levels, or turn it off altogether.

Just my $0.02.....

Phratry answered 7/9, 2009 at 14:3 Comment(0)
L
5

Substitute your debug output statements with something like this:

IFDBG(cout << result << endl);

Then you can define macros accordingly:

#ifdef DEBUG
#  define IFDBG(x) x
#else
#  define IFDBG(x)
#endif
Langelo answered 7/9, 2009 at 13:58 Comment(2)
That doesn't look like it will work with IFDBG(cout << foo(x,y) << std::endl);. The comma is going to break the macro.Twicetold
No, that won't break the macro, since macros can escape scope whithin parenthesis. However this call will break it: IFDBG(cout << foo<int, float>() << std::endl);Proteolysis
P
5

Define this macro :

#ifdef DEBUG
    #define MY_LOG std::cout
#else
    #define MY_LOG if(false) std::cout
#endif

This macro advantage is in compiler optimization

If expressions placed inside those IFs are constant and determinable at the time of compilation, then you may be almost sure that the compiler has already removed them off the code for you... https://mcmap.net/q/375962/-c-attempt-to-optimize-code-by-replacing-tests

Peruse answered 6/8, 2015 at 21:58 Comment(1)
For me it's the best answer. Finally I solved this problem! Thank you @y30!Dynasty
P
4

NullStream can be a good solution if you are looking for something quick that removes debug statements. However I would recommend creating your own class for debugging, that can be expanded as needed when more debug functionality is required:

class MyDebug
{
    std::ostream & stream;
  public:
    MyDebug(std::ostream & s) : stream(s) {}
#ifdef NDEBUG
    template<typename T>
    MyDebug & operator<<(T& item)
    {
      stream << item;
      return *this;
    }
#else
    template<typename T>
    MyDebug & operator<<(T&)
    {
      return *this;
    }
#endif
};

This is a simple setup that can do what you want initially, plus it has the added benefit of letting you add functionality such as debug levels etc..

Update: Now since manipulators are implemented as functions, if you want to accept manipulators as well (endl) you can add:

MyDebug & operator<<(std::ostream & (*pf)(std::ostream&))
{
  stream << pf;
  return *this;
}

And for all manipulator types (So that you don't have to overload for all manipulator types):

template<typename R, typename P>
MyDebug & operator<<(R & (*pf)(P &))
{
  stream << pf;
  return *this;
}

Be careful with this last one, because that will also accept regular functions pointers.

Proteolysis answered 7/9, 2009 at 15:9 Comment(2)
If I pass endl to this stream it gives error.. looks like T& item doesn't accept endl.. any clue how to overload it for that too ?Unsuccessful
Hello, what kind of situation does operator<<(R & (*pf)(P &)) handle? I found that everything works fine without it.Bierman
C
3

You can probably do a preprocessor hack that defines a new stream-like class, with an instance named cerr, that just does nothing. If you're really lucky, the compiler will see that the function does nothing, and optimize the calls to operator<<() out.

Something like

class NullStream
{
public:
  NullStream();

  NullStream& operator<<(const std::string& text) { return *this; }
  // And operators for other types, too
}
static NullStream cerr;

This is quite the hack though, it's (far) better to go through your source and add proper support for logging.

Curve answered 7/9, 2009 at 14:1 Comment(0)
A
0

Defining a macro that replaces cout is not something you should upload to your VCS, but if you just do it temporarily during debugging, I think it serves its place. So you can just replace cout by ostream(0) like

#ifdef NDEBUG
#define cout ostream(0).flush()
#endif

This way, it works with both std::cout and plain cout, and ostream is available when including <iostream>. Writing into a ostream(0) is a no-op. The flush function call is done so that you get a non-const reference to it (so it also binds to non-member operator<< that's used for outputting std::string and others). As its type is ostream, it should behave exactly like cout.

Antiar answered 7/9, 2009 at 23:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.