#define预处理器指令可以包含if和else吗?

时间:2013-11-05 17:25:10

标签: c++ visual-studio c-preprocessor preprocessor-directive

我正在尝试此链接中的记录器代码,但它给了我错误。 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'。

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

如果在编译时知道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 )

答案 1 :(得分:2)

  

#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 ++语句。

答案 2 :(得分:2)

任何时候你想定义一个扩展为语句的宏,如果定义包含任何复合语句(包括if / else),你应该包装do中的定义... 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))

允许在{em>表达式可以使用的任何地方使用log(level);如果添加分号,则包括语句上下文。您可能希望将0替换为logIt返回的类型;如果logIt是一个void函数,您可能需要:

#define log(level) \
    ((level) > loglevel ? (void)0 : logIt(level))

这一切都假设宏是工作的正确工具。模板(由this answer建议)或内联函数可能会更好地完成工作,并且可能会产生混淆。