【问题标题】:How do I combine two boost logging macros that generate unique identifiers?如何组合两个生成唯一标识符的 boost 日志记录宏?
【发布时间】:2016-09-17 20:32:47
【问题描述】:

我正在使用 boost_logging(第 2 版),我希望避免在我的代码中乱扔这些:

BOOST_LOG_NAMED_SCOPE("SomeModuleName")
BOOST_LOG_FUNCTION()

我的想法是,我更愿意将它们组合成这样的宏:

#define LOG_NAMED_SCOPE_FUNCTION(name)\
  BOOST_LOG_NAMED_SCOPE(name)\
  BOOST_LOG_FUNCTION()

但是当我尝试这样做时出现错误。见下文。

我会非常高兴有一个解决方案允许我在模块级别执行BOOST_LOG_NAMED_SCOPE("SomeModuleName") 之类的操作(这也会出错)。更好的解决方案是执行某种 RAII/AOP 以允许我在函数的开头和结尾附加“进入”和“退出”跟踪消息,因为这是我的最终目标。

我猜是因为生成的唯一标识符是在宏定义时扩展的,而不是在宏调用时。我还查看了 DEFERRED 和 EXPAND 助手,但我不确定他们是否会在这里帮助我。

这是错误信息:

Project/SomeModuleName.cpp:9:5: error: redefinition of '_boost_log_named_scope_sentry_9'
    LOG_NAMED_SCOPE_FUNCTION("SomeModuleName")
    ^
Project/Logging.hpp:24:5: note: expanded from macro 'LOG_NAMED_SCOPE_FUNCTION'
    BOOST_LOG_FUNCTION()
    ^
/usr/local/include/boost/log/attributes/named_scope.hpp:458:36: note: expanded from macro 'BOOST_LOG_FUNCTION'
    BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, ::boost::log::attributes::named_scope_entry::function)
                                   ^
/usr/local/include/boost/log/utility/unique_identifier_name.hpp:48:5: note: expanded from macro 'BOOST_LOG_UNIQUE_IDENTIFIER_NAME'
    BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, __LINE__)
    ^
note: (skipping 2 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
#    define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
                               ^
/usr/local/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
#    define BOOST_PP_CAT_I(a, b) a ## b
                                 ^
<scratch space>:89:1: note: expanded from here
_boost_log_named_scope_sentry_9
^
Project/SomeModuleName.cpp:9:5: note: previous definition is here
Project/Logging.hpp:23:5: note: expanded from macro 'LOG_NAMED_SCOPE_FUNCTION'
    BOOST_LOG_NAMED_SCOPE(name)\
    ^
/usr/local/include/boost/log/attributes/named_scope.hpp:449:36: note: expanded from macro 'BOOST_LOG_NAMED_SCOPE'
    BOOST_LOG_NAMED_SCOPE_INTERNAL(BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_named_scope_sentry_), name, __FILE__, __LINE__, ::boost::log::attributes::named_scope_entry::general)
                                   ^
/usr/local/include/boost/log/utility/unique_identifier_name.hpp:48:5: note: expanded from macro 'BOOST_LOG_UNIQUE_IDENTIFIER_NAME'
    BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, __LINE__)
    ^
note: (skipping 2 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all)
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
#    define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b)
                               ^
/usr/local/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
#    define BOOST_PP_CAT_I(a, b) a ## b
                                 ^
<scratch space>:85:1: note: expanded from here
_boost_log_named_scope_sentry_9
^

【问题讨论】:

  • 完全不确定,但您尝试过插入分号吗? :#define LOG_NAMED_SCOPE_FUNCTION(name)\ BOOST_LOG_NAMED_SCOPE(name); \ BOOST_LOG_FUNCTION()(对不起,如果它很愚蠢,但我真的不明白你怎么会出现这个错误:它应该工作,句号)
  • @Jean-FrançoisFabre 如果我添加分号,则会出现同样的错误。我在发布之前尝试过;)谢谢!这让我明白部分问题是宏是行敏感的,如果我跳过我的宏并将两个宏调用粘贴在我试图记录的相关函数的同一行上,我会得到同样的错误信息。这个换行插入技巧对我不起作用:stackoverflow.com/a/2567304/232593
  • 好的,我的愚蠢评论让你进步了。这是另一个:您是否比较了g++ -E 的输出有和没有你的宏?

标签: c++ boost macros boost-log


【解决方案1】:

您遇到的问题是因为BOOST_LOG_FUNCTION 本质上是基于BOOST_LOG_NAMED_SCOPE - 它添加了一个名称对应于当前函数签名的范围。这两个宏都创建了一个局部变量,该变量的名称对于源文件的给定行是唯一的(它使用__LINE__ 来生成该名称)。根据 C/C++ 预处理器规则,所有宏都扩展为一行,因此您的 LOG_NAMED_SCOPE_FUNCTION 扩展为同一范围内的两个相同命名的局部变量,因此会出现编译器错误。

解决此问题的一种方法是定义宏,以便它直接为两个作用域定义两个不同的变量。变量应该具有named_scope::sentry 类型,这是一个作用域保护,在构造和销毁时自动将作用域推送和弹出到堆栈。

#define LOG_NAMED_SCOPE_FUNCTION(name)\
    boost::log::attributes::named_scope::sentry BOOST_LOG_UNIQUE_IDENTIFIER_NAME(scope_sentry1_)(name, __FILE__, __LINE__);\
    boost::log::attributes::named_scope::sentry BOOST_LOG_UNIQUE_IDENTIFIER_NAME(scope_sentry2_)(BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, boost::log::attributes::named_scope_entry::function)

这样每个LOG_NAMED_SCOPE_FUNCTION 都会向列表中添加两个作用域——命名作用域和当前函数。

但是,从您的描述看来,这实际上并不是您想要实现的目标。 Boost.Log 不允许直接使用它们起源的模块的名称来标记日志记录 - 主要是因为没有可移植的方式知道这一点,而且不可移植的方式很昂贵。但是有一些方法可以模仿这种行为。这里有一些想法。

最简单的方法是使用您自己的日志记录宏,它会自动添加当前模块名称属性。

// Define the attribute keyword for the module name
BOOST_LOG_ATTRIBUTE_KEYWORD(a_module, "Module", std::string)

#define MY_LOG(lg)\
    BOOST_LOG(lg) << boost::log::add_value(a_module, CURRENT_MODULE)

如果您在项目设置中将CURRENT_MODULE 定义为命名当前模块的字符串,MY_LOG 宏将自动将其作为属性附加到记录中。请参阅有关 attribute keywordsadd_value manipulator 的文档。

另一种方法是使用channels。如果您的 loggers 没有被不同的模块共享,您可以将当前模块名称设置为通道名称。或者,如果您已经使用通道,请将其作为新的单独属性添加到您创建的每个记录器中。您可以编写自己的 logger feature 来自动执行此操作。

如果您共享记录器,那么您还可以看看scoped attributes 是如何实现的,尤其是BOOST_LOG_SCOPED_THREAD_TAG。由于您可能在不同模块之间调用函数,BOOST_LOG_SCOPED_THREAD_TAG 不会按原样为您工作(因为如果集合中的属性已经存在,它不会替换该属性 - 在您的情况下,这意味着您只会看到名称的第一个设置属性的模块),但您可以实现适合您情况的类似的东西。这个想法是如果没有添加当前模块名称作为线程特定的属性,或者替换现有的一个。这必须在一个作用域保护中完成,该作用域保护必须用于可以从其他模块调用的每个函数。

【讨论】:

  • 这解决了我的问题。我重新考虑了“模块”的事情,因为我在添加了一堆我关心的模块是逻辑的而不是物理的日志之后意识到。根据我 Q 中的其他“功能请求”,我还简单地添加了一个带 RAII 的类来记录函数的进入和退出,并将其也粘贴在宏中。
猜你喜欢
  • 2020-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-06
  • 2012-12-31
  • 2019-08-05
相关资源
最近更新 更多