3

I would like to define a macro with a variable number of parameters which prints the name and value of each given parameter.

For instance :

MACRO(x) would print x = 123 MACRO(x,y) would print x,y = 123,666

A better macro would be more readable

BETTER_MACRO(x,y) would print x = 123, y = 666

For one variable, I can manage with :

#define MACRO(...) cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << endl;

For more, it does not work.

By acting this way, some auxiliary questions come to my mind.

1) How to get the number of variables given to the macro? 2) How to access each argument?

Guess naïvely, we can answer these two questions.

We then hope to define the macro in the following way.

#define BETTER_MACRO(...) {for (int i=0;i<=nb_variables;i++) {cout << #var[i] << var[i];}}

a3f
  • 8,517
  • 1
  • 41
  • 46
captain_flammy
  • 117
  • 3
  • 9
  • http://en.wikipedia.org/wiki/Variadic_macro http://msdn.microsoft.com/en-us/library/ms177415.aspx – Alex F Apr 09 '14 at 13:56
  • Related to: [iterating-variadic-macro-arguments](http://stackoverflow.com/questions/6194031/iterating-variadic-macro-arguments) – Jarod42 Apr 09 '14 at 14:22

5 Answers5

6
#define MACRO(...) function(#__VA_ARGS__, __VA_ARGS__)

// base case for template recursion when one argument remains
template <typename Arg1>
void function(const char* name, Arg1&& arg1)
{
    std::cout << name << " = " << arg1 << std::endl;
}

// recursive variadic template for multiple arguments
template <typename Arg1, typename... Args>
void function(const char* names, Arg1&& arg1, Args&&... args)
{
    const char* comma = strchr(names + 1, ',');
    std::cout.write(names, comma - names) << " = " << arg1;
    function(comma, args...);
}

The above has the somewhat whimsical property that whatever spacing (or not) that you include in the invocation of MACRO will be mirrored in the output.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • I tried your solution which is the closest from what I expect. I noticed a problem when I apply the macro to more than 2 arguments. `MACRO(a,b,c)` prints out `a=1 =2,b,c=3` instead of `a=1,b=2,c=3`. I don't know yet how to fix it. Anyway, thanks to people who helps me. This community is great. :-) – captain_flammy Apr 09 '14 at 15:32
  • Penultimate line. Replace `comma` per `comma + 1`. – captain_flammy Apr 09 '14 at 16:21
  • @captain_flammy: sorry about that, I fixed it in a way I found better: by adding 1 to `names` in the call to `strchr`. I think that makes it work always. – John Zwinck Apr 10 '14 at 00:14
  • Good, except for your awful function naming. Also throw some universal references in there, would ya? – Lightness Races in Orbit Apr 10 '14 at 14:37
  • @LightnessRacesinOrbit: I named the function function because the OP named the macro MACRO. I always try to match local conventions! ;-) As for the universal references, I've added them (hopefully correctly). – John Zwinck Apr 11 '14 at 00:59
  • This is perfect, just what I needed. This is a very useful pattern! – Bogatyr Oct 05 '18 at 16:34
  • Thank you! This is exactly what I needed. @captain_flammy I modified it to print multiple values in an alternate format: a,b,c: val_a, val_b, val_c – Sujay Phadke Sep 15 '20 at 10:50
3

1) How to get the number of variables given to the macro?

You may use something like this to count element: (with hard-coded limit):

#define COUNT_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...)    N
#define COUNT(...)   COUNT_N(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
// Warning: COUNT() return 1 (as COUNT(A)) :-/

2) How to access each argument?

/* args */
#define ARG(N, ...) ARG_##N(__VA_ARGS__)

#define ARG_1(a, ...) a
#define ARG_2(a, b,...) b
#define ARG_3(a, b, c, ...) c
#define ARG_4(a, b, c, d, ...) d
#define ARG_5(a, b, c, d, e, ...) e
#define ARG_6(a, b, c, d, e, f, ...) f
#define ARG_7(a, b, c, d, e, f, g, ...) g
#define ARG_8(a, b, c, d, e, f, g, h, ...) h
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • By the way, Boost has already done this for up to...256? with `BOOST_PP_VARIADIC_SIZE`. – chris Apr 09 '14 at 14:18
1

Not an ideal solution, but:

The macro itself can take a fixed number of parameters, but each parameter can be a parenthesized list (even though it is just a text string).

 void function_taking_varargs(...);

 #define SLIGHTLY_BETTER(x) \
      function_taking_varargs x


 SLIGHTLY_BETTER((a, b, c));
Dale Wilson
  • 9,166
  • 3
  • 34
  • 52
1

This one works pre-C++11 with no ugly variadic function. Instead it uses ugly macros, which at least give a compile-time error. The calling syntax is a little bit different, but not too much. I tried looking for something like SEQ_ENUM, but taking a macro, and couldn't find anything.

First, we'll make our customized printing macros:

#define PRINT_ONE(elem) BOOST_PP_STRINGIZE(elem) " = " << elem
#define PRINT_ONE_COMMA(r, data, elem) BOOST_PP_STRINGIZE(elem) " = " << elem << ", " <<

Next, some utilities for printing:

#define LAST(seq) BOOST_PP_SEQ_ELEM(BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(seq)), seq)
#define REST(seq) BOOST_PP_SEQ_SUBSEQ(seq, 0, BOOST_PP_DEC(BOOST_PP_SEQ_SIZE(seq)))

Finally, we can iterate through and print:

#define PRINT(seq) std::cout << BOOST_PP_SEQ_FOR_EACH(PRINT_ONE_COMMA, _, REST(seq)) PRINT_ONE(LAST(seq)) << '\n';

The usage is as follows:

PRINT((var1)(var2)(var3)) // var1 = 5, var2 = 3, var3 = 7

Here's a live example.

chris
  • 60,560
  • 13
  • 143
  • 205
0

If you are using a compiler that supports C++11 or a compiler with a non-standard extension to C++03:

With two macros and a template:

#include <iostream>

struct None {};

template <typename T>
struct Log
{
    static void write(const char* name, const T& value) {
        std::clog << name << " = " << value << '\n';
    }
};

template<>
struct Log<None>
{
    static void write(const char*, const None&) {}
};

#define LOG_VALUE_DETAIL(A, B, C, D, E, ...) do { \
    Log<decltype(A)>::write(#A, A); \
    Log<decltype(B)>::write(#B, B); \
    Log<decltype(C)>::write(#C, C); \
    Log<decltype(D)>::write(#D, D); \
    Log<decltype(E)>::write(#E, E); \
} while(0)

#define LOG_VALUE(...) LOG_VALUE_DETAIL(__VA_ARGS__, None(), None(), None(), None(), None())

inline int f() { return 3; }

int main()
{

    int a = 0;
    int b = 1;
    int c = 2;
    LOG_VALUE(a, b, c, f());
    return 0;
}
Dale Wilson
  • 9,166
  • 3
  • 34
  • 52