#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: ifSyntax error: else

但后来,我注意到如果我将#include "logger.hpp"main.h 移动到main.cpp,问题就消失了。虽然 'main.h' 在不同的地方被多次包含,但它确实包含 '#pragma once'。

有什么想法吗?

【问题讨论】:

那是文本替换。查看 PP 输出。 logit(level) 后面缺少分号 正如@chris 所指出的,发生错误是因为 log(level) 只是被定义中的 if 语句替换,这导致编译器阻塞的无效代码。您能否在代码中添加一个示例,说明您使用 this 并获取错误的地方,并在其周围加上几行,以便我们查看上下文? @Erbureth 用法是logIt(level) &lt;&lt; "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 &gt; logLevel); else logIt(6.0)) + 1,这不是一个有效的 C++ 语句。

【讨论】:

除了log() 函数之外,您可能还提到了我在上一次对该问题的评论中所说的话:logIt::operator&lt;&lt;() 声明不适合应用于const logIt .

以上是关于#define 预处理器指令可以包含 if 和 else 吗?的主要内容,如果未能解决你的问题,请参考以下文章

C#-#define条件编译

C#-C#-#define条件编译

C#预处理器指令之#define/#undefine/#if/#elif/#else/#endif

C#预处理器指令——学习

预处理器指令

#if 预处理器指令并不总是崩溃