我正在尝试此链接中的记录器代码,但它给了我错误。 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'。
有什么想法吗?
答案 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建议)或内联函数可能会更好地完成工作,并且可能会产生混淆。