2

in the Debug.h file, I have the following:

#ifdef DEBUG_FLAG
    #define DEBUG(msg) std::cerr << #msg << std::endl
#else
    #define DEBUG(msg) for(;true==false;)
#endif

In other places, I may write something like

void process (Data data)
{
    DEBUG("Function 'process' starts");
    // Blah blah
    // More blah blah...
    DEBUG("Function 'process' returns");
}

Will the compiler optimize away the for(;true==false;); ?

Also, is this kind of practice okay? If not, what would be a better way?

Thanks!

Jimmy Lu
  • 4,810
  • 7
  • 25
  • 30
  • why not #else #define DEBUG(msg) without loop? – ForEveR Jul 14 '12 at 02:09
  • Well, it *is* the strategy that many (most?) people bring to their first debugging module in c-like language. On the other hand just about all of those people more onto other options pretty after using the first one for a short time... – dmckee --- ex-moderator kitten Jul 14 '12 at 02:12
  • @quasiverse Yeah, I just realized that... – Jimmy Lu Jul 14 '12 at 02:12
  • @ForEveR Because DEBUG(msg) does not expand to include the semicolon, if debug_flag is not defined, my program will end up with a semicolon in the middle of nowhere and thus won't compile. – Jimmy Lu Jul 14 '12 at 02:13
  • I'm pretty sure your compiler already has a debug output feature. On VC++, look up _RPTx() macros in crtdbg.h. – Seva Alekseyev Jul 14 '12 at 02:16
  • And what? Program will be compile. Example http://liveworkspace.org/code/8fc1edca625e488ff6924045713b2aee – ForEveR Jul 14 '12 at 02:16
  • @BeyondSora: That's a misunderstanding, whenever `std::cerr << msg << std::endl ;` is appropriate, `;` is also appropriate and represents an empty expression. – David Rodríguez - dribeas Jul 14 '12 at 02:30

3 Answers3

2

You don't need:

#define DEBUG(msg) for(;;)

at all. If you just have it as:

#define DEBUG(msg)

then the expression will be literally blank and won't require a semicolon at all.

EDIT: And actually, having sole semicolons will not cause crashes or compiler errors.

Yokatta24
  • 565
  • 5
  • 8
2

Here's an alternative, that uses the compiler's dead code removal:

#define DEBUG(msg) if (!DEBUG_ENABLED) {} \
                   else dbglog() << __FILE__ << ":" << __LINE__ << " " << msg
#ifdef DEBUG_FLAG
#define DEBUG_ENABLED 1
#else
#define DEBUG_ENABLED 0
#endif

The dbglog instance is a ostream wrapper that detects if the log line ended with a newline or not. If not, it adds one.

struct dbglog {
    std::ostream &os_;
    mutable bool has_endl_;
    dbglog (std::ostream &os = std::cerr) : os_(os), has_endl_(false) {}
    ~dbglog () { if (!has_endl_) os_ << std::endl; }
    template <typename T> static bool has_endl (const T &) { return false; }
    static bool has_endl (char c) { return (c == '\n'); }
    static bool has_endl (std::string s) { return has_endl(*s.rbegin()); }
    static bool has_endl (const char *s) { return has_endl(std::string(s)); }
    template <typename T>
    static bool same_manip (T & (*m)(T &), T & (*e)(T &)) { return (m == e); }
    const dbglog & operator << (std::ostream & (*m)(std::ostream &)) const {
        has_endl_ = same_manip(m, std::endl);
        os_ << m;
        return *this;
    }
    template <typename T>
    const dbglog & operator << (const T &v) const {
        has_endl_ = has_endl(v);
        os_ << v;
        return *this;
    }
};

Now, you can add a simple message like this (note, the newline is optional):

DEBUG("A simple message");
DEBUG("A simple message with newline\n");
DEBUG("A simple message with endl") << std::endl;

Or, if you want to add more debugging information:

DEBUG("Entering: ") << __func__ << ", argc=" << argc << ", argv=" << argv;
//...
DEBUG("Leaving: ") << __func__ << std::endl;
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Whoa! This looks very nice indeed. Thank you! – Jimmy Lu Jul 14 '12 at 02:44
  • Please comment on a downvote, so I know what needs improving. Thanks! – jxh Jul 14 '12 at 06:52
  • You template dblog is great!!! really useful. I learned that combination of macros and templates can be really powerful. – Jimmy Lu Jul 14 '12 at 22:45
  • @BeyondSora: Thanks. Note that `dbglog` is not a template, it's a class that has template methods. – jxh Jul 14 '12 at 23:22
  • I am not the downvoter, but I can imagine that it is because of the macro. Macros with control structures in them can cause a lot of trouble, in particular spurious and surprising warnings about dangling `else`. Here e.g if you have it in another condition `if (toto) DEBUG("bla");` could irritate the compiler. Always enclose such macros in `do { your-stuff-comes-here } while (0)`, so all ambiguities are resolved. Actually I now saw that there was a similar remark by chr below, putting that into anwser, instead of a comment :) – Jens Gustedt Aug 16 '12 at 22:34
  • @JensGustedt: Thanks for the feedback, I imagine you are right. But I don't have another solution to allow for C++ style shift operator into the stream, and still allow the debug to be disabled with no arguments being processed. – jxh Aug 16 '12 at 22:39
  • @user315052, ah right, my C++ is really too rusty. Then you should have a second class `dbglog_eater` that'd have a generic `<<` operator, just for the purpose of discarding all what is on its right. Those two classes you wrap in a `template` class `dbg` that depends on a `bool`. `dgb` should resolve basically to `dbglog` and `dbg` to `dbg_eater`. Your macro would then just be `dbg< (bool)DEBUG_ENABLED >()` to select the correct instance. – Jens Gustedt Aug 16 '12 at 22:52
  • @JensGustedt: I can create a special class that can swallow the argument, but the argument itself is still evaluated (e.g., if the argument is a function call, it will be called). – jxh Aug 16 '12 at 23:12
  • @user315052, I think that this would be a big `nop`. If everything is well exposed a decent compiler should be able to inline that completely and then to eliminate it via constant propagation. – Jens Gustedt Aug 17 '12 at 05:24
  • @JensGustedt: I think I should address the macro issue more directly in the answer, let me think about that. But, consider the code at http://ideone.com/R5s6i for why I believe not everything can be squashed to a `nop`. – jxh Aug 17 '12 at 06:06
  • For me gcc is able to compile all that out, with exception of the function call that has side effects. Ok, let's stop it here, largely off topic :) – Jens Gustedt Aug 17 '12 at 06:16
0

You also need the while (0) for your debug macro. Will be very painful to debug if/else cases without it (if you are lucky, you will get a compiler error). See this question for more details.

Community
  • 1
  • 1
Christian Garbin
  • 2,512
  • 1
  • 23
  • 31