Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do pre-processor macros for debug log statements have a place in C++?

Recently I have been reading Effective C++ Second Edition by Scott Meyers to improve on C++ best practices. One of his listed items encourages C++ programmers to avoid pre-processor macros and 'prefer the compiler'. He went as far as saying there are almost no reasons for macro in C++ aside from #include and #ifdef/#ifndef.

I agree with his reasoning, as you can accomplish the following macro

#define min(a,b) ((a) < (b) ? (a) : (b))

with the following C++ language features

template<class T>
inline const T & min(const T & a, const T & b) {
    return a < b ? a : b;
}

where inline gives the compiler the option to remove the function call and insert inline code and template which can handle multiple data types who have an overloaded or built in > operator.

EDIT-- This template declaration will not completely match the stated macro if the data type of a and b differ. See Pete's comment for an example.

However, I am curious to know if using macros for debug logging is a valid use in C++. If the method I present below is not good practice, would someone be kind to suggest an alternative way?

I have been coding in Objective-C for the last year and one of my favorite 2D engines (cocos2d) utilized a macro to create logging statements. The macro is as follows:

/*


* if COCOS2D_DEBUG is not defined, or if it is 0 then
 *  all CCLOGXXX macros will be disabled
 *
 * if COCOS2D_DEBUG==1 then:
 *      CCLOG() will be enabled
 *      CCLOGERROR() will be enabled
 *      CCLOGINFO() will be disabled
 *
 * if COCOS2D_DEBUG==2 or higher then:
 *      CCLOG() will be enabled
 *      CCLOGERROR() will be enabled
 *      CCLOGINFO() will be enabled
 */


#define __CCLOGWITHFUNCTION(s, ...) \
NSLog(@"%s : %@",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__])

#define __CCLOG(s, ...) \
NSLog(@"%@",[NSString stringWithFormat:(s), ##__VA_ARGS__])


#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
#define CCLOG(...) do {} while (0)
#define CCLOGWARN(...) do {} while (0)
#define CCLOGINFO(...) do {} while (0)

#elif COCOS2D_DEBUG == 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) do {} while (0)

#elif COCOS2D_DEBUG > 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) __CCLOG(__VA_ARGS__)
#endif // COCOS2D_DEBUG

This macro provides for incredible utility which I will want to incorporate in my C++ programs. Writing a useful log statement is as simple as

CCLOG(@"Error in x due to y");

What is even better, is that if the COCOS2D_DEBUG is set to 0, then these statements never see the light of day. There is no overhead for checking a conditional statement to see if logging statements should be used. This is convenient when making the transition from development to production. How could one recreate this same effect in C++?

So does this type of macro belong in a C++ program? Is there a better, more C++ way of doing this?

like image 941
Paul Renton Avatar asked Aug 04 '13 21:08

Paul Renton


People also ask

How does a preprocessor work in C?

The preprocessor provides the ability for the inclusion of header files, macro expansions, conditional compilation, and line control. In many C implementations, it is a separate program invoked by the compiler as the first part of translation.

Are there macros in C?

Macro in C programming is known as the piece of code defined with the help of the #define directive. Macros in C are very useful at multiple places to replace the piece of code with a single value of the macro. Macros have multiple types and there are some predefined macros as well.

What is preprocessor directive in C?

It is a pre-process of execution of a program using c/c++ language. To initialize a process of preprocessor commands, it's mandated to define with a hash symbol (#). It can preferably be the non-blank character, and for better readability, a preprocessor directive should start in the first column.

Which preprocessor command is used for macro definition in C?

The C preprocessor is a macro processor that is used automatically by the C compiler to transform your program before actual compilation. It is called a macro processor because it allows you to define macros, which are brief abbreviations for longer constructs.


1 Answers

First, Scott's statement was made at a time when macros were considerably overused, for historical reasons. While it is generally true, there are a few cases where macros make sense. Of of these is logging, because only a macro can automatically insert __FILE__ and __LINE__. Also, only a macro can resolve to absolutely nothing (although based on my experience, this isn't a big deal).

Macros such as you show aren't very frequent in C++. There are two usual variants for logging:

#define LOG( message ) ... << message ...

which allows messages in the form " x = " << x, and can be completely suppressed by redefining the macro, and

#define LOG() logFile( __FILE__, __LINE__ )

where logFile returns a wrapper for an std::ostream, which defines operator<<, and permits such things as:

LOG() << "x = " << x;

Done this way, all of the expressions to the right of LOG() will always be evaluated, but done correctly, no formatting will be done unless the log is active.

like image 104
James Kanze Avatar answered Nov 14 '22 22:11

James Kanze