最近,我一直在阅读Scott Meyers的Effective C ++ Second Edition,以改进C ++最佳实践。他列出的一个项目鼓励C ++程序员避免使用预处理器宏并且“更喜欢编译器”。他说除了#include和#ifdef / #ifndef之外,C ++中几乎没有宏的原因。
我同意他的推理,因为你可以完成以下宏
#define min(a,b) ((a) < (b) ? (a) : (b))
具有以下C ++语言功能
template<class T>
inline const T & min(const T & a, const T & b) {
return a < b ? a : b;
}
其中inline为编译器提供了删除函数调用并插入内联代码和模板的选项,该内联代码和模板可以处理多个数据类型,这些数据类型具有重载或内置的&gt;运营商。
编辑 - 如果a和b的数据类型不同,则此模板声明将不会与所述宏完全匹配。请参阅Pete的评论。
但是,我很想知道使用宏进行调试日志记录是否是C ++中的有效用法。如果我在下面提出的方法不是很好的做法,那么有人会建议另一种方法吗?
我去年在Objective-C编码,我最喜欢的2D引擎之一(cocos2d)利用宏来创建日志语句。宏如下:
/*
* 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
这个宏提供了令人难以置信的实用程序,我希望将其合并到我的C ++程序中。编写有用的日志语句就像
一样简单CCLOG(@"Error in x due to y");
更好的是,如果COCOS2D_DEBUG设置为0,那么这些陈述永远不会出现。检查条件语句以查看是否应使用日志语句没有任何开销。这在从开发过渡到生产时非常方便。如何在C ++中重新创建相同的效果?
这种类型的宏属于C ++程序吗?有更好的,更多的C ++方式吗?
答案 0 :(得分:6)
首先,斯科特的声明是在宏的时候发表的
由于历史原因,相当过度使用。虽然它是
一般来说,有一些情况下宏是有意义的。
其中是日志记录,因为只有一个宏可以自动进行
插入__FILE__
和__LINE__
。此外,只有一个宏可以
绝对没有决心(尽管根据我的经验,
这不是什么大不了的事。)
您展示的宏在C ++中并不常见。有 记录的两种常见变体:
#define LOG( message ) ... << message ...
允许使用" x = " << x
形式的消息,并且可以
通过重新定义宏完全抑制,
#define LOG() logFile( __FILE__, __LINE__ )
其中logFile
返回std::ostream
的包装器,其中
定义operator<<
,并允许以下内容:
LOG() << "x = " << x;
以这种方式完成,LOG()
右侧的所有表达式
将始终进行评估,但正确完成,没有格式化
除非日志处于活动状态,否则将完成。
答案 1 :(得分:2)
使用宏有“正确”的东西,并且宏的用途很糟糕。使用函数工作的宏是一个坏主意。在我的书中使用功能不做同样事情的宏是非常好的。
我经常使用这样的结构:
#defien my_assert(x) do { if (!x) assert_failed(x, #x, __FILE__, __LINE__); } while(0)
template<typename T>
void assert_failed(T x, const char *x_str, const char *file, int line)
{
std::cerr << "Assertion failed: " << x_str << "(" << x << ") at " << file << ":" << line << std::endl;
std::terminate();
}
使用字符串化“运算符”的另一个技巧是这样的:
enum E
{
a,
b,
c,
d
};
struct enum_string
{
E v;
const char *str;
};
#define TO_STR(x) { x, #x }
enum_string enum_to_str[] =
{
TO_STR(a),
TO_STR(b),
TO_STR(c),
TO_STR(d),
};
节省了不少重复的东西......
所以,是的,它在某些情况下很有用。