How does the “lazy evaluation” of Boost Log's

2019-06-17 05:40发布

[Follows up Check boost::log filter explicitly? ]

The following example uses the trivial logger from Boost Log. It outputs 1, showing that expensive() is only called once. How does it work? Why is expensive() not called?

Live On Coliru

#include <iostream>

#include <boost/log/expressions.hpp>
#include <boost/log/trivial.hpp>

int count = 0;

int expensive()
{
    return ++count;
}

int main()
{
    boost::log::core::get()->set_filter(
        boost::log::trivial::severity >= boost::log::trivial::warning
    );

    BOOST_LOG_TRIVIAL(error) << expensive();
    BOOST_LOG_TRIVIAL(info) << expensive();

    std::cout << count << '\n';

    return 0;
}

Output:

[2018-05-21 14:33:47.327507] [0x00007eff37aa1740] [error]   1
1

1条回答
放荡不羁爱自由
2楼-- · 2019-06-17 05:54

It works by using a bit of macro/preprocessor magic. The statements look indeed like a function call to some operator<<():

BOOST_LOG_TRIVIAL(warning) << expensive();

However, simplifying a lot, the macro works as if we had written something like:

if (level == warning)
    logger << expensive();

If you wanted to simplify that code to avoid writing all the time, you could define a macro like this:

#define LOG_WARNING if (level == warning) logger

And then we could use it as:

LOG_WARNING << expensive();

The actual BOOST_LOG_TRIVIAL macro ends up expanding into:

for (
    ::boost::log::record _boost_log_record_N =
        (::boost::log::trivial::logger::get()).open_record(
            (::boost::log::keywords::severity = ::boost::log::trivial::error)
        )
    ;
    !!_boost_log_record_N;
)
    ::boost::log::aux::make_record_pump(
        (::boost::log::trivial::logger::get()),
        _boost_log_record_N
    ).stream() << expensive();

As you see, depending on the !!_boost_log_record_N loop condition (which in turn depends on the result of open_record()), the body of the loop will be run zero or more times; which is the reason why expensive() is not always run.

查看更多
登录 后发表回答