psorek psorek - 1 month ago 12
C++ Question

How to write iostream-like interface to logging library?

I would like to write a convinient interface to my very simple logging library. Take two following pieces of code. The first one is what I do now, the second one is my idea for an intuitive interface:

std::ostringstream stream;
stream<<"Some text "<<and_variables<<" formated using standard string stream"
logger.log(stream.str()); //then passed to the logger


And

logger.convinient_log()<<"Same text "<<with_variables<<" but passed directly";


My thought-design process behind that idea is to return some kind of temporary stringstream-like object from
logger.convinient_log()
function. That object on destruction (I hope it happens at the end of the line or in a similar, convinient place) would collect string from itself and call an actual
logger.log()
. The point is I want to process it whole, not term-by-term, so that log() can add eg. prefix and sufix to whole line of text.

I'm very well avare that it might be straight impossible or impossible without some heavy magic. If that's the case, what would be an almost-as-convinient way to do that and how to implement it? I bet on passing some special variable that would force collect-call-
logger.log()
operation.

If you don't know an exact answer, resources on the topic (eg. extending stringstream) would be also welcome.

Answer

This is how Boost.Log works, for example. The basic idea is simple:

struct log
{
    log() {
        uncaught = std::uncaught_exceptions();
    }

    ~log() {
        if (uncaught >= std::uncaught_exceptions()) {
            std::cout << "prefix: " << stream.str() << " suffix\n";
        }
    }

    std::stringstream stream;
    int uncaught;
};

template <typename T>
log& operator<<(log& record, T&& t) {
    record.stream << std::forward<T>(t);
    return record;
}

template <typename T>
log& operator<<(log&& record, T&& t) {
    return record << std::forward<T>(t);
}

// Usage:
log() << "Hello world! " << 42;

std::uncaught_exceptions() is used to avoid logging an incomplete message if an exception is thrown in the middle.

Comments