22

Is there a way to define/undefine debug messages using std::cout whenever inside a program?

I am aware that there are such things such as #define, #ifndef, but I was thinking is there a cleaner way to having a variable say:

# debug ON

That prints all of my debug data (using std::cout). Consequently, we'll have code like this for debug:

#ifndef DEBUG
// do something useful
#endif

I find the above code cumbersome when you write 100s of debug code.

Thanks!

Carlo

9 Answers9

47
#ifdef DEBUG
#define DEBUG_MSG(str) do { std::cout << str << std::endl; } while( false )
#else
#define DEBUG_MSG(str) do { } while ( false )
#endif

int main()
{
    DEBUG_MSG("Hello" << ' ' << "World!" << 1 );
    return 0;
}
Gianni
  • 4,300
  • 18
  • 24
  • This isn't something you can change during runtime. If by "on the fly" you mean at compile time, then this is a very simple and good answer for your needs. If you need to change it @ runtime, log libraries are designed to do this and be configurable via some file rather than compile time flags. – NG. Jul 30 '10 at 13:39
  • @SB I understood "on the fly" to mean compile-time since it was implied by his use of `#ifndef debug` – Gianni Jul 30 '10 at 13:50
  • 13
    What's the purpose of the `do while` here? – cYrus Jan 14 '12 at 02:02
  • 13
    @cYrus To force you to put a ';' at the end of the macro call *every time*. Ensure that: if (c) MACRO(param) \ func(other); doesn't become if (c) func(other); when the macro is expanded to an 'empty' text (i.e. with no ';' – Gianni Jan 24 '12 at 11:14
  • instead of the do { } while ( false ) why don't you put a 0, for example, as an atomic operation 0;? – ben26941 Apr 01 '20 at 11:45
  • @ben26941 Style preference really. – Gianni May 13 '20 at 18:57
  • In case you don't know... To get `Hello World!1` output on the console, create a `CMakeLists.txt`, add required commands/details, and also add **`add_compile_definitions(DEBUG)`** reference: https://stackoverflow.com/a/9017635/10358768 – Milan Oct 28 '20 at 20:44
  • @ Gianni if possible, could you please elaborate on your comment in response to @cYrus 's comment? Thank you so much in advance! – Milan Oct 28 '20 at 20:45
  • @Gianni I meant how does using `do while` forces us to put a `;` ? Because, if we don't use `:` in the macro then we have to put `;` in main after using that macro, right? Otherwise, it will give a compilation error, correct? – Milan Oct 28 '20 at 22:46
  • 1
    @Milan: see https://stackoverflow.com/q/154136/158371 – Christian Severin Jan 13 '23 at 11:37
8

Some logging libraries are pretty heavy weight unless you have complex logging needs. Here's something I just knocked together. Needs a little testing but might meet your requirements:

#include <cstdio>
#include <cstdarg>

class CLog
{
public:
    enum { All=0, Debug, Info, Warning, Error, Fatal, None };
    static void Write(int nLevel, const char *szFormat, ...);
    static void SetLevel(int nLevel);

protected:
    static void CheckInit();
    static void Init();

private:
    CLog();
    static bool m_bInitialised;
    static int  m_nLevel;
};

bool CLog::m_bInitialised;
int  CLog::m_nLevel;

void CLog::Write(int nLevel, const char *szFormat, ...)
{
    CheckInit();
    if (nLevel >= m_nLevel)
    {
        va_list args;
        va_start(args, szFormat);
        vprintf(szFormat, args);
        va_end(args);
    }
}
void CLog::SetLevel(int nLevel)
{
    m_nLevel = nLevel;
    m_bInitialised = true;
}
void CLog::CheckInit()
{
    if (!m_bInitialised)
    {
        Init();
    }
}
void CLog::Init()
{
    int nDfltLevel(CLog::All);
    // Retrieve your level from an environment variable, 
    // registry entry or wherecer
    SetLevel(nDfltLevel);
}

int main()
{
    CLog::Write(CLog::Debug, "testing 1 2 3");
    return 0;
}
Michael J
  • 7,631
  • 2
  • 24
  • 30
5

Probably not. I would recommend using a logging library. I'm not sure what the best option is for C++ anymore, but I've used log4cpp in the past and found it pretty good.

EDIT: I assume on the fly means @ runtime. If you just need it to be a compile time flag, then Gianni's answer is probably easiest to implement. Logging libraries give you a lot of flexibility and allow reconfiguration @ runtime though.

Community
  • 1
  • 1
NG.
  • 22,560
  • 5
  • 55
  • 61
  • 2
    +1. log4cpp is kind of old; I'd recommend investigating log4cxx, one of the Boost.Log candidates, or Pantheios. – Josh Kelley Jul 30 '10 at 13:06
  • @Carlo - you might want to check into the ones suggested by Josh. I know log4cpp is kind of old. – NG. Jul 30 '10 at 13:16
5

Another simple solution, involves opening a std::ostream reference to cout in debug mode, and /dev/null in non-debug mode, like so:

In debug.h:

extern std::ostream &dout;

In debug.c

#ifdef DEBUG
std::ostream &dout = cout;
#else
std::ofstream dev_null("/dev/null");
std::ostream &dout = dev_null;
#endif

And then:

dout << "This is a debugging message";

Of course, this would only work on any system where /dev/null points to a null device. Since the dout reference is global here, it would much like cout. In this way, you can point the same stream to multiple output streams, for example to a log file, depending of the value of debug flags, etc.

forumulator
  • 836
  • 12
  • 12
3

Although the question is old, and there are some good answers, i want to post also a solution to this. It is like Giannis approach but different. And also, i used std::cerr instead of std::cout, but you can change this really quick.

#include <iostream>
#ifdef DEBUG
#  define DEBUG_LOG std::cerr

#else
class log_disabled_output {};
static log_disabled_output log_disabled_output_instance;

template<typename T>
log_disabled_output& operator << (log_disabled_output& any, T const& thing) { return any; }

// std::endl simple, quick and dirty
log_disabled_output& operator << (log_disabled_output& any, std::ostream&(*)(std::ostream&)) { return any; }

#  define DEBUG_LOG log_disabled_output_instance 
#endif

int main() {
    int x=0x12345678;
    DEBUG_LOG << "my message " << x << " " << "\n more information" << std::endl;
};

Now you can use it just like a output stream.

(Note: iostream is only included if cerr is used. This will reduce the amount of inclusion if you don't have it already included. -edit: not with std::endl support).
If DEBUG is defined cerr is used to print the error. Otherwise the dummy class log_disabled_output is instantiated statically and operator<< is overloaded to any type. The vantage is; If you disable the logging, a clever compiler will notice that there is nothing to do with the stream and optimize the entire "line" away, so you don't have any overhead if DEBUG is disabled.

user1810087
  • 5,146
  • 1
  • 41
  • 76
  • I can't use `endl` with this DEBUG_LOG, isn't it? – ephemerr Jun 16 '19 at 11:30
  • @ephemerr: Not with the previous version, but with the additional overload. Please, see my edit. Note this is just an example to point into a direction, not an actuall full fledged implementation. You can extend it if you need, though. – user1810087 Jun 26 '19 at 10:51
2

I was trying to do the same thing. After some research, I developed the following, and it seems to work. Please comment if you see anything wrong.

ostream DbgMsg(NULL);
enum {
  DBGMSG_NONE,
  DBGMSG_DEFAULT,
  DBGMSG_VERBOSE
} DbgLvl = DBGMSG_DEFAULT;

ostream &DbgMsgDefault(ostream &stream) {
  return (DbgLvl>=DBGMSG_DEFAULT) ? cout : stream;
}

ostream &DbgMsgVerbose(ostream &stream) {
  return (DbgLvl>=DBGMSG_VERBOSE) ? cout : stream;
}

void main() {
   DbgMsg<<DbgMsgDefault<<"default:default"<<endl;
   DbgMsg<<DbgMsgVerbose<<"default:verbose"<<endl;
   DbgLvl = DBGMSG_NONE;
   DbgMsg<<DbgMsgDefault<<"none:default"<<endl;
}
tangbotu
  • 21
  • 1
  • Welcome to Stack Overflow! Would you consider adding some narrative to explain why this code works, and what makes it an answer to the question? This would be very helpful to the person asking the question, and anyone else who comes along. – Andrew Barber Jun 15 '13 at 09:09
1

A clean thing to do would be to use cerr.

"cerr" acts essentially as "cout", but always flushes the output (useful for debugging, by the way). If you need to remove all the messages, you can comment out all the cerr messages with a simple find-and-replace (cerr into //cerr).

There are probably even better ways to use cerr and to desactivate it cleanly (which writes into a special stream, the error stream, hence the name). I hope this helps.

1

This is what I used (worked with VC++) - here "##" is used for concatennation

#ifdef DEBUG 
#define pout cout 
#else
#define pout / ## / cout 
#endif 

For other compilers use this :

#ifdef DEBUG 
#define pout cout 
#else
#define pout 0 && cout 
#endif 

Usage :

pout << "hello world" << endl; 
Reno
  • 33,594
  • 11
  • 89
  • 102
  • While this can be a nice hack, I would rather not use it. Keep in mind that each line of a multiline cout would have to start with pout, and also, consider something like the following after preprocessing: if (condition) LF //conditional_command(); LF unconditional_command(); – risingballs Jun 06 '19 at 21:35
0

I was looking for similar example and sharing my example below:

#include <iostream>
enum debug_option
{
    DEBUG_DISABLE,
    DEBUG_ENABLE
};

class debug
{
public:
    debug_option debug_state;

    debug() : debug_state(DEBUG_ENABLE) {} // constr
    debug(debug_option state) : debug_state(state) {} // constr

    template<typename T>
    debug & operator<< (T input)
    {
    if (this->debug_state == DEBUG_ENABLE)
        std::cout << input;
    return *this;
    }
};

int main()
{
    debug log, log_lev2(DEBUG_DISABLE);
    log << "print 1..\n" << 55 << " over\n";
    log.debug_state = DEBUG_DISABLE;
    log << "print 2..\n" << 3 << "over\n";
    log_lev2 << "print 3..\n" << 4 << "over\n";
    log_lev2.debug_state = DEBUG_ENABLE;
    log_lev2 << "print 5..\n";
    std::cout << "std::cout << print..\n";
    return 0;
}

Better suggestions are always welcome.

Mithun B
  • 268
  • 1
  • 8