0

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.

max66
  • 65,235
  • 10
  • 71
  • 111
johnco3
  • 2,401
  • 4
  • 35
  • 67

1 Answers1

2

Not sure to understand what do you exactly want but... I suppose is something as follows (caution: code not tested)

template <typename ELLT, typename ... Args>
void errorLog (ELLT & gErrorLineLog, Args const & ... as)
 {
   using unused = int[];

   (void)unused { 0, ((void)gErrorLineLog << as, 0)... };
 }

If you can use C++17, maybe simply

template <typename ELLT, typename ... Args>
void errorLog (ELLT & gErrorLineLog, Args const & ... as)
 { ( ((void)gErrorLineLog << as), ... ); }
max66
  • 65,235
  • 10
  • 71
  • 111
  • Much appreciated - I have seem the technique above but I don't understand how it works - is that something like a c++14 fold emulation? Can you explain how it works? note I will have a #ifdef in this method with gErrorLineLog used for the embedded target and boost format taking a format string and substituting ... Args for each %n% argument in the case of windows. – johnco3 Aug 13 '18 at 16:53
  • 1
    It's the power of comma operator: discard the left argument and return the right one; so you can initialize something (a C-style array in this case, a `std::initializer_list` in preceding answer) with zeros and expanding the variadic pack. – max66 Aug 13 '18 at 16:58
  • That comma operator thing threw me for a loop, totally not obvious - I found a reasonable explanation in Jason Turner's C++ weekly https://www.youtube.com/watch?v=VXi0AOQ0PF0 at about 70% of the way through the video. Also not sure if there is a way to remove the ugly using unused = int[] for c++14. Your answer above is half right for me, I now have it working for the embedded case but not the case where I have access to boost, I will edit my question to clarify shortly – johnco3 Aug 13 '18 at 21:15