I need help to add a variadic member function to a c++14 class to support logging. My class is currently running on both windows and another embedded operating system that does not have access to the standard library. My code is sprinkled with hundreds of preprocessor #ifdefs
like the one shown below, and my goal is to consolidate these blocks (many of which have multiple parameters) into a common variadic member function that I can log to boost::log
via boost::format
(_WIN32) or alternatively (Embedded OS) to a special gErrorLineLog
object that supports a stream insertion like operator for some very basic types (including const char*
). Think of this latter object as something similar to std::stringstream
.
#if defined (_WIN32)
LOG_ERROR(gLogger, gChannel) << boost::format("%1%")
% "'Transmit429Request' invalid message length";
#else
gErrorLineLog << "error: TX429 [bad message length]";
#endif
The following stack overflow answer boost::format with variadic template arguments goes a long way towards answering how to put the windows/boost::format
side of this into a variadic member function, but I have no idea how to loop around the expansion pack for the embedded case (where I have to do something along the following lines in my variadic template function:
for (auto next : parameterpack) {
gErrorLineLog << next:
}
EDIT
So far, I have a partial solution for the embedded case (where I do not have STL and therefore std::initializer_list<int>
) - thanks max66 for the alternative trick that works well when I pass in a logger with its associated variadic arguments.
After looking at Jason Turner's C++ weekly video episode 4 about 70% of the way through he explains some of the mysterious magic behind parameter pack expansions that is c++14 compatible. I would have preferred to use a folding expression (thanks again max66 for the alternative more compact fold expression syntax), but unfortunately gcc 5.3.0 does not c++17 and therefore fold expressions).
My latest version after the feedback is as follows:
template <typename LOGGER, typename ... Args>
void addLogEntry(LOGGER& VideoLogger, Args const& ... args)
{
#if defined (_WIN32)
LOG_INFO(gLogger, gChannel) << boost::format(
"DOUTRequest: query, eotw[0x%1%]")
% boost::io::group(std::setfill('0'),
std::hex, std::setw(4), *endOfFrame);
#else
// https://www.youtube.com/watch?v=VXi0AOQ0PF0
// parameter pack expansion using comma operator
VideoLogger.clear();
using unused = int[];
(void)unused{0, (VideoLogger << args, 0)...};
#endif
}
and I call this in my application code:
#if defined (_WIN32)
// I'm not sure how to get this right
LOG_INFO(gLogger, gChannel) << boost::format("received: TX429 with [%1%] labels") % numArincWords %
#else // THIS WORKS
addLogEntry(gReceiveLineLog,
"received: TX429 with [", dec,
numArincWords, "] labels");
#endif
As you can see I haven't quite figured out how to do the win32 boost thing in the logger or in the call to the logger.