captain_flammy
captain_flammy

Reputation: 147

How to print a variable number of parameters with a macro?

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];}}

Upvotes: 3

Views: 3013

Answers (5)

John Zwinck
John Zwinck

Reputation: 249333

#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.

Upvotes: 6

user2249683
user2249683

Reputation:

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;
}

Upvotes: 0

Jarod42
Jarod42

Reputation: 217663

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

Upvotes: 3

Qaz
Qaz

Reputation: 61940

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.

Upvotes: 1

Dale Wilson
Dale Wilson

Reputation: 9434

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));

Upvotes: 1

Related Questions