【问题标题】:C++ macros and std::ostringstreamsC++ 宏和 std::ostringstreams
【发布时间】:2013-04-04 16:30:02
【问题描述】:

我有一个带有公共方法的静态 C++ 类 Logger

static std::ostringstream& Log(int sev);

您可以看到该方法评估为 ostringstream,因此我只需键入即可记录我的内容:

Logger::log(MY_SEVERITY) << "hello world";

由于编译器首先评估 log(MY_SEVERITY),然后评估整个表达式,我需要第二种方法:

static FVResult_t Flush();

实际将 ostringstream 的内容写入磁盘。 Flush() 必须在任何 Log() 调用之前调用,所以每当我想记录一些东西时,我必须依次调用这两个方法:

Logger::log(MY_SEVERITY) << "hello world";
Logger::Flush();

到目前为止一切顺利。

现在我的问题是,我想编写一个 C++ 宏,它结合了两种方法,让我可以做这样的事情:

LOGMACRO(MY_SEVERITY) << "hello world";

我试过了:

#define LOGMACRO(lev) \
Logger::Log(lev) \
Logger::Flush();

但是,正如我所料,C++ 编译器抱怨缺少 ostringstream。如何仅在我的宏中将此类 ostringstream 传递给 Logger::Log(lev)?

【问题讨论】:

  • 似乎更好的方法是编写一个为您调用Logger::Flush 的iostream 操纵器。然后你可以写Logger::log(MY_SEVERITY) &lt;&lt; "hello world" &lt;&lt; flush_logger;
  • 为什么,这个里面有宏?插入运算符的要求表明宏表达的任何内容都必须返回某种ostream&amp;,而您的则不会。除非您正在记录行号和文件位置(我建议,顺便说一句),否则这里没有任何证据表明它们甚至是需要的?采用严重性并返回正确的ostream&amp; 的内联应该绰绰有余。 (我同意上面约翰的评论。)
  • 为什么不创建自己的LogStream 类,并在其中进行所有必要的刷新?
  • @John:嗯,你怎么能确保最后调用 flush_logger()? C99 和 C11 都没有规定评估顺序(例如从左到右)
  • Flush_logger 不是一个函数,它是一个操纵器(在这种情况下是一个函数指针)。它将被最后调用,您认为 std::flush 或 std::endl 还能如何工作?

标签: c++ string logging macros stringstream


【解决方案1】:

您只能使用以下宏

#define LOGMACRO(lev, msg) \
Logger::Log(lev) << msg; \
Logger::Flush();

使用喜欢

LOGMACRO(MY_SEVERITY, "hello world")

不过,我觉得那个功能,还是手动使用std::endl,或者使用自己的机械手会更好。

功能可以这样

inline void log(int severity, const std::string& msg)
{
   Logger::Log(severity) << msg;
   Logger::Flush();
}

然后

log(MY_SEVERITY, "Hello!");

【讨论】:

  • 好吧,你不能再流式传输了!形式的日志:"folder #" &lt;&lt; folderid &lt;&lt; " could not be " &lt;&lt; action 很常见,采用std::string 的函数真的不合适!
  • @MatthieuM。这是正确的。一切都取决于what 你需要记录器。
【解决方案2】:

您的问题是宏在“Hello world”流式传输到您的记录器之前正在扩展,导致以下结果:

Logger::Log(MY_SEVERITY)
Logger::flush();
<< "hello world"

这不是有效的 C++。

最好的方法是使用宏,例如:

#define LOG_ERROR( msg ) \
do { \
    std::ostringstream oss; \
    oss << __func__ << "() ERROR: " << msg; \
    Logger::Log( LEVEL_ERROR ) << oss.str(); \
    Logger::Flush(); \
} while(0)

这个宏可以如下使用:

void foo()
{
    /// stuff that detects a error
    LOG_ERROR( "Parameter X has an invalid value " << std::hex << x 
               << " should be in the range " << y " - " z" );
}

这将输出类似于

的内容
foo() : Parameter X has an invalid value F7  should be in the range 0 - F0

此机制还意味着您无需担心在调用点将流输出说明符(例如 std::hex)重置为宏 LOG_ERROR

另外,它可能值得调查boost::log

【讨论】:

  • 是的,已经尝试过 boost::log,它是一个很棒的库,但是对我来说,包含 boost::log 只是为了将一些日志行写入文本文件似乎有点矫枉过正
【解决方案3】:

有一个更简单的非宏观解决方案。让Logger::log 成为一个类,带有Logger::log::log(int) 构造函数。 Logger::log(MY_SEVERITY) 现在是一个临时的,它一直存在到 Logger::log(MY_SEVERITY) &lt;&lt; "hello world"; 语句的末尾。

现在定义Logger::log::~log 并让它调用Logger::Flush()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    • 1970-01-01
    • 2014-10-15
    • 1970-01-01
    • 1970-01-01
    • 2014-11-10
    • 2016-12-14
    相关资源
    最近更新 更多