我目前正在开发一个小而简单的日志记录库,它充当Boost.Log v2 C ++库的Facade。
我的库已经完成,我已经成功封装了Boost.Log,即:
目前还有一件我无法解决的问题:懒惰参数评估
我将给出Boost.Log行为的简短用法示例:
#include <chrono>
#include <thread>
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/trivial.hpp>
std::string time_costly_function(
const std::chrono::seconds seconds = std::chrono::seconds{1}) {
std::this_thread::sleep_for(seconds);
return "DONE with time_costly_function";
}
int main() {
boost::log::core::get()->set_filter(boost::log::trivial::severity >=
boost::log::trivial::warning);
BOOST_LOG_TRIVIAL(warning) << "This is evaluated: " << time_costly_function();
BOOST_LOG_TRIVIAL(info) << "This is NOT evaluated: "
<< time_costly_function();
}
如果您运行上面的示例,则可以看到仅评估第一个BOOST_LOG_TRIVIAL
调用的参数,即time_costly_function()
仅被称为一次。
这正是我在我的库中想要的行为,但正如我所提到的,我不希望用户必须直接处理Boost.Log,而是使用我的小Facade。
以下代码说明了问题(非常简化以解决实际问题):
#include <boost/log/core.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/trivial.hpp>
#include <chrono>
#include <thread>
std::string time_costly_function(
const std::chrono::seconds seconds = std::chrono::seconds{1}) {
std::this_thread::sleep_for(seconds);
return "DONE with time_costly_function";
}
// Encapsulates Boost.Log. The API of my logging library does not know anything
// about Boost.Log (e.g. the Pimpl idiom is used).
//
// In reality this is a member function of a class in my library with the
// following signature:
//
// void log(const SeverityLevel level, const std::string& message) override;
//
void log(const std::string& message) { BOOST_LOG_TRIVIAL(warning) << message; }
// A custom logging macro.
//
// In reality this is a macro taking the object of a class with the member
// function illustrated above:
//
// FW_LOG(logger, level, message)
//
#define FW_LOG(message) log(message)
int main() {
// TODO(wolters): This does not work, since everything is evaluated outside of
// the Boost.Log macro(s). I want to call my macro as follows.
// FW_LOG(logger, level) << message << time_costly_function();
// I.e. it should work like the Boost.Log macro(s) illustrated above.
FW_LOG("foo" + time_costly_function());
}
我在代码中发现了以下问题:
log
可能不会引用std::string
引用,因为那样
始终会导致该功能之外的评估。FW_LOG
:
FW_LOG(logger, level) << message << time_costly_function();
我已经考虑过使用模板功能来使用流和/或完美转发,但是我还没有想出一个可行的解决方案。
要求:代码必须使用MSVC 12.0,Boost.Log 1.59.0进行编译。允许C + 11。
所以我的实际问题是:
如何在第二个示例中重写代码(使用宏和函数)以获得与第一个示例中相同的行为?
答案 0 :(得分:1)
像
这样的东西#define FW_LOG(logger, level, message) \
do { if (logger.check_level(level)) logger.log(message); while (false)
答案 1 :(得分:0)
在使用我的代码后,我想出了一个解决方案,感谢@ Jarod42和@Nicolás(https://stackoverflow.com/a/2196183/891439)。
bool isLevelEnabledForAtLeastOneHandler(const SeverityLevel level) const
类中添加了一个成员函数Logger
,以检查是否为添加到{的至少一个Handler类启用了日志记录宏提供的严重性级别{1}}实例。我已经修改了@Nicolás的解决方案(Logger
类扩展了我的库中的Logger
接口类):
LoggerInterface
我修改了@ Jarod42提供的宏:
class LoggerStream final : public std::ostringstream {
public:
LoggerStream(LoggerInterface& logger, const SeverityLevel level) : logger_{logger}, level_{level} {
// NOOP
}
~LoggerStream() {
logger_.log(level_, str());
}
private:
LoggerInterface& logger_;
SeverityLevel level_;
};
我更改了#define FW_LOG(logger, level) \
if (logger.isLevelEnabledForAtLeastOneHandler(level)) LoggerStream(logger, level)
宏调用:
FW_LOG
如果我使用严重性级别配置添加到FW_LOG(logger, SeverityLevel::WARNING) << "IS EVALUATED " << time_costly_function();
FW_LOG(logger, SeverityLevel::DEBUG) << "IS NOT EVALUATED " << time_costly_function();
的处理程序
Logger
,第二次调用WARNING
会导致以下行为:
FW_LOG
中的条件if (logger.isLevelEnabledForAtLeastOneHandler(level))
评估为FW_LOG
。false
的“流参数”<< "IS NOT EVALUATED << time_costly_function()
,因为没有创建FW_LOG
类的实例。因此,我当前解决方案的唯一开销是调用LoggerStream
。
不是一个完美的解决方案,但比以前好多了。我甚至不必使用此解决方案的isLevelEnabledForAtLeastOneHandler
成员函数修改LoggerInterface
接口类。
我的原始问题仍然存在:是否可以将log
宏“流参数”以某种方式转发给成员函数FW_LOG
,Boost.Log宏可以完成所有工作,即所有参数都在成员函数内部进行了lazely评估,而不是在宏内部?
直接使用Boost.Log宏不会产生与提供的解决方案相同的开销。因此感觉就像我重新发明了车轮的某些部分......