Home > Back-end >  Simpler logger function using std::stringstream
Simpler logger function using std::stringstream

Time:08-11

I'm trying to write a simple logging function which can handle different types in this format:

LOG("This is one type: " << one_type << " and here is a different type: " << diff_type);

I've been looking at the examples here:

How to use my logging class like a std C stream?

stringstream with recursive variadic function?

and this is what I came up with:

#include <iostream>
#include <sstream>

void LOG(std::stringstream& ss)
{
    std::cout << ss.str() << std::endl;
}

However, even when I just do:

LOG("Some text");

I get the error:

could not convert ‘(const char*)"Some text"’ from ‘const char*’ to ‘std::stringstream’ {aka ‘std::__cxx11::basic_stringstream<char>’}

How can I implement this?

CodePudding user response:

A common way to solve this is to make LOG a macro that just does text substitution instead. You could define a LOG macro like

#define LOG(to_log) \
do \
{ \
    std::cout << to_log << std::endl; \
} while (false)

and then

LOG("This is one type: " << one_type << " and here is a different type: " << diff_type);

would get expanded to

do
{
    std::cout << "This is one type: " << one_type << " and here is a different type: " << diff_type << std::endl;
} while (false);

CodePudding user response:

template <typename ... T>
void log(T const& ... t)
{
    ( std::cout << ... << t );
    // maybe want to add a newline?
    std::cout << std::endl; // note: '\n' just adds it,
                            // std::endl flushes the stream... 
}

would allow to log (to console!) as:

log("this is an int: ", 1210, " and this is a double: ", 10.12);

Note the commas instead of the stream operator (<<)...

If you want to log to arbitrary streams you might need to add it as yet another parameter (/*...*/ void log(std::ostream& s, T const&... t)) or you might (among other solutions) initialise some singleton that you access from within the logging function:

void initialiseLogging(std::ostream& s)
{
    Logger::instance().initialize(s);
}

template <typename ... T>
void log(T const& ... t)
{
     // assumes returning a reference:
    ( Logger::instance().stream() << ... << t );
}

Above variant assumes initialisation being required and logging without yielding undefined behaviour (needs to be documented!). If you want to be able to log without you need to change:

{
    // now assuming a pointer being returned!
    auto s = Logger::instance().stream();

    // variant 1: log to nowhere if not initialised:
    if(s)
    {
        ( *s << ... << t );
    }

    // variant 2: default logging to console
    s = s ? s : &std::cout;
    ( *s << ... << t );
}  
  • Related