#define 预处理器指令可以包含 if 和 else 吗?
Posted
技术标签:
【中文标题】#define 预处理器指令可以包含 if 和 else 吗?【英文标题】:Can #define preprocessor directive contain if and else? 【发布时间】:2013-11-05 17:25:09 【问题描述】:我正在尝试从此链接中的记录器代码,但它给了我错误。 How to implement a good debug/logging feature in a project
#ifndef _LOGGER_HPP_
#define _LOGGER_HPP_
#include <iostream>
#include <sstream>
/* consider adding boost thread id since we'll want to know whose writting and
* won't want to repeat it for every single call */
/* consider adding policy class to allow users to redirect logging to specific
* files via the command line
*/
enum loglevel_e
logERROR, logWARNING, logINFO, logDEBUG, logDEBUG1, logDEBUG2, logDEBUG3, logDEBUG4;
class logIt
public:
logIt(loglevel_e _loglevel = logERROR)
_buffer << _loglevel << " :"
<< std::string(
_loglevel > logDEBUG
? (_loglevel - logDEBUG) * 4
: 1
, ' ');
template <typename T>
logIt & operator<<(T const & value)
_buffer << value;
return *this;
~logIt()
_buffer << std::endl;
// This is atomic according to the POSIX standard
// http://www.gnu.org/s/libc/manual/html_node/Streams-and-Threads.html
std::cerr << _buffer.str();
private:
std::ostringstream _buffer;
;
extern loglevel_e loglevel;
#define log(level) \
if (level > loglevel) ; \
else logIt(level)
#endif
更准确地说,这个#define
给出了错误:
#define log(level) \
if (level > loglevel) ; \
else logIt(level)
错误是Syntax error: if
和Syntax error: else
但后来,我注意到如果我将#include "logger.hpp"
从main.h
移动到main.cpp
,问题就消失了。虽然 'main.h' 在不同的地方被多次包含,但它确实包含 '#pragma once'。
有什么想法吗?
【问题讨论】:
那是文本替换。查看 PP 输出。logit(level)
后面缺少分号
正如@chris 所指出的,发生错误是因为 log(level) 只是被定义中的 if 语句替换,这导致编译器阻塞的无效代码。您能否在代码中添加一个示例,说明您使用 this 并获取错误的地方,并在其周围加上几行,以便我们查看上下文?
@Erbureth 用法是logIt(level) << "debug msg";
logIt(level) 是对构造函数的调用。所以我认为分号不应该在那里?
@CompuChip 它不会编译
【参考方案1】:
如果loglevel
在编译时已知,您可以执行以下操作:
template <bool>
struct LogSystem
template <class T>
LogSystem& operator << (const T &)
//ignore the input
return (*this);
;
template <>
struct LogSystem <true>
template <class T>
LogSystem& operator << (const T & v)
cout << v;
return (*this);
;
template <bool B>
LogSystem<B> getLog()
return LogSystem<B>();
#define log(level) getLog< (level <= loglevel) >()
如果loglevel
在编译时未知:
class iLogSystem
public:
virtual iLogSystem& operator << (const int &)
//empty
return (*this);
virtual iLogSystem& operator << (const custom_type &);
return (*this);
//make functions for logging all the types you want
;
class LogSystem : public iLogSystem
public:
virtual iLogSystem& operator << (const int & v)
cout << v;
return (*this);
virtual iLogSystem& operator << (const custom_type & q);
cout << q.toString();
return (*this);
//make functions for logging all the types you want
;
iLogSystem& getLog(const bool l)
static LogSystem actual_log;
static iLogSystem empty_log;
if(l)
return &actual_log;
return &empty_log;
#define log(level) getLog( level <= loglevel )
【讨论】:
使用模板代替宏的好主意! 我没有设法解决原来的问题,但这段代码有效。谢谢!【参考方案2】:任何时候你想定义一个扩展为一个语句的宏,如果定义包含任何复合语句(包括if
/else
),你应该将定义包装在@987654324中@ ... while (0)
。包含的代码仍将只执行一次,并且可以在任何需要语句的上下文中使用。
这是我所知道的在 if
/else
语句中使用宏时避免语法错误的唯一方法,因为使用了分号。
不如这样:
#define log(level) \
if ((level) > loglevel) ; \
else logIt(level)
你可以用这个:
#define log(level) \
do \
if ((level) > loglevel) ; \
else logIt(level) \
while (0)
我在对宏的level
参数的引用周围添加了括号,以避免任何可能的运算符优先级问题。还要注意末尾缺少分号;分号将由调用者提供。
另一方面,if
/else
通常可以替换为条件(三元)?:
运算符:
#define log(level) \
((level) > loglevel ? 0 : logIt(level))
允许log(level)
在任何可以使用表达式的地方使用;如果添加分号,则包括语句上下文。您可能希望将 0
替换为 logIt
返回的类型;如果 logIt
是一个 void 函数,你可能想要:
#define log(level) \
((level) > loglevel ? (void)0 : logIt(level))
这一切都假设宏是适合这项工作的工具。模板(如 this answer 所建议的那样)或内联函数可能会更好地完成这项工作,并且减少混淆的可能性。
【讨论】:
【参考方案3】:#define 预处理器指令可以包含 if 和 else 吗?
是的。
关于您的问题:预处理器很笨,只执行简单的文本替换。它不是函数,也不是语言结构,它是简单的、愚蠢的文本替换。结果是:
#define log(level) \
if (level > loglevel) ; \
else logIt(level)
...
log(logINFO) << "foo " << "bar " << "baz";
变成这样:
if (logINFO > loglevel); // << here's your problem.
else
logIt(logInfo)
<< "foo " << "bar " << "baz";
您的问题是;
。这里,分号表示c++if
语句的结束,所以编译器后面遇到else
,不知道怎么处理。
我注意到如果我将 #include "logger.hpp" 从 main.h 移动到 main.cpp,问题就消失了
C++ 具有“对数”功能。称为log。如果你的其他文件使用对数函数,事情会变得非常有趣,因为它会被你的 if/else 日志代码替换。
例如,如果在标头中的某处有内联对数代码,那么如果您首先包含 logger
标头,它将变成无稽之谈。例如log(6.0) + 1
将变成log (if (6.0 > logLevel); else logIt(6.0)) + 1
,这不是一个有效的 C++ 语句。
【讨论】:
除了log()
函数之外,您可能还提到了我在上一次对该问题的评论中所说的话:logIt::operator<<()
声明不适合应用于const logIt
.以上是关于#define 预处理器指令可以包含 if 和 else 吗?的主要内容,如果未能解决你的问题,请参考以下文章