Search code examples
c++macrosvariadic-templatesvariadic-macros

Make variadic macro / method which prints all variables names and values


An existing macro gets variadic number of variables on my application.
I want, using this macro, to print these variables by name=value format.

Here is a small example :

#define EXISTING_MACRO(...) < ??? >

int main()
int a = 0;
int b = 1;
int c = 2;

EXISTING_MACRO(a,b,c);

Output should be :

a=0, b=1, c=2

I've tried doing so by calling a variadic template function from within the macro, and I do succeed printing the variables values, but not the variables names. (#x prints their addresses, and even if it didn't it would probably just show the method variable name, 'f') :

#define SHOW(a) std::cout << #a << "=" << (a)

template<typename TF>
void write_err_output(std::ostream& out, TF const& f) {
    out << f << std::endl;
}

template<typename TF, typename ... TR>
void write_err_output(std::ostream& out, TF const& f, TR const& ... rest) {
    out << SHOW(f) << " ";
    write_err_output(out, rest...);
}

Solution

  • You can do this with a bit of a hack. The hack is not very robust, but it should be acceptable if you're using it for debugging purposes.

    The idea is to stringify the entire variadic macro argument, and then take the sequence of labels apart by tokenising on commas. That requires that none of the variadic arguments include a comma, which will be the case if they are all variable names, but there is no way to require that and in fact the proposed code below will happily accept expressions, as shown.

    #define SHOW(...) show(std::cout, #__VA_ARGS__, __VA_ARGS__)
    
    template<typename H1>
    std::ostream& show(std::ostream& out, const char* label, H1&& value) {
      return out << label << "=" << std::forward<H1>(value) << '\n';
    }
    
    template<typename H1, typename ...T>
    std::ostream& show(std::ostream& out, const char* label, H1&& value, T&&... rest) {
      const char* pcomma = strchr(label, ',');
      return show(out.write(label, pcomma - label) << "="
                                                   << std::forward<H1>(value)
                                                   << ',',
                  pcomma + 1,
                  std::forward<T>(rest)...);
    }
    

    (live on coliru)

    For simplicity, and to avoid std::string, I've used the standard C strchr function rather than C++ library functions. Apologies to anyone that is offended.