How does the "lazy evaluation" of Boost Log's trivial loggers work?
Asked Answered
L

1

11

[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
Lenticular answered 15/5, 2018 at 15:44 Comment(0)
L
9

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.

Lenticular answered 15/5, 2018 at 15:44 Comment(3)
I love that they called that macro BOOST_LOG_TRIVIAL (also love that you did what I never get around to)Bothersome
Note that conditional execution of the streaming expression is deliberate and documented, for example in Tutorial.Sexuality
@AndreySemashev: Indeed -- this question was written to clarify how it worked due to this other question. Didn't mean it wasn't intended -- actually, I expect logging libraries to support something similar (otherwise having to write explicit conditions to avoid potentially expensive operations becomes very tedious). Thanks for writing such a useful library!Lenticular

© 2022 - 2024 — McMap. All rights reserved.