2
votes

First working code:

#include <iostream>

// NOTE: requires compiler which has __PRETTY_FUNCTION__, like gcc or clang
#define DUMP(v) std::cerr << __PRETTY_FUNCTION__ << ':' << __LINE__ << ':' << #v << "='" << (v) << "'\n"

int main(int argc) {

    DUMP(argc);
    DUMP(argc+1);
    return 0;
}

which gives stderr output (gcc):

int main(int):8:argc='1'
int main(int):9:argc+1='2'

Now I'd like to have variadic macro with any number of arguments, so for example

DUMP(argc, argc+1); would work, with output like:

int main(int):8:argc='1',argc+1='2'


But I could not yet come up with a nice solution. Is this even possible with a macro? If not, how about templates, or combination of macros and templates? C++11 and Boost are ok if needed, and solution can be gcc-specific if there's no standard way. Looking for actual code, which makes the variadic DUMP work like described above, or at least show equivalent info.

2
Ok, I have a solution! It works... But I'm not entirely sure how (it is heavily ripped off). I will spend a few more minutes trying to understand it, then post it anyway.BoBTFish
Ok, I've added my answer now. Good luck.BoBTFish

2 Answers

3
votes

Using Boost.PP, you can write this:

#define DUMP_EACH(r, data, v) std::cerr << __PRETTY_FUNCTION__ << ':' << __LINE__ << ':' << #v << "='" << (v) << "'\n"
#define DUMP(...) BOOST_PP_SEQ_FOR_EACH(DUMP_EACH, ~, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

You will also need to add -DBOOST_PP_VARIADICS=1 when you compile. If you don't want to use boost you can write a simple FOR_EACH macro to iterate over each argument, like this:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* This will call a macro on each argument passed in */
#define FOR_EACH(macro, ...) CAT(FOR_EACH_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define FOR_EACH_1(m, x1) m(x1)
#define FOR_EACH_2(m, x1, x2) m(x1) m(x2)
#define FOR_EACH_3(m, x1, x2, x3) m(x1) m(x2) m(x3)
#define FOR_EACH_4(m, x1, x2, x3, x4) m(x1) m(x2) m(x3) m(x4)
#define FOR_EACH_5(m, x1, x2, x3, x4, x5) m(x1) m(x2) m(x3) m(x4) m(x5)
#define FOR_EACH_6(m, x1, x2, x3, x4, x5, x6) m(x1) m(x2) m(x3) m(x4) m(x5) m(x6)
#define FOR_EACH_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1) m(x2) m(x3) m(x4) m(x5) m(x6) m(x7)
#define FOR_EACH_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1) m(x2) m(x3) m(x4) m(x5) m(x6) m(x7) m(x8)

Then you can define your dump macro like this:

#define DUMP_EACH(v) std::cerr << __PRETTY_FUNCTION__ << ':' << __LINE__ << ':' << #v << "='" << (v) << "'\n"
#define DUMP(...) FOR_EACH(DUMP_EACH, __VA_ARGS__)
1
votes

Ok. This is NOT NICE. But it works, up to a prespecified number of arguments (I got lazy at 4). It is heavily ripped off from here (seriously, click and upvote). It uses the trick of literally expanding the arguments in front of a prewritten list, pulling off the correct number from a fixed position at the back. It then calls the correct macro name by appending the number. Clear? Nope! Here's the code:

#include <iostream>

// Pretty normal macro tricks

#define STRINGIZE(a) STRINGIZE1(a)
#define STRINGIZE1(a) STRINGIZE2(a)
#define STRINGIZE2(a) #a

#define CONCAT(a, b)  CONCAT1(a, b)
#define CONCAT1(a, b) CONCAT2(a, b)
#define CONCAT2(a, b) a##b

// Faux-recursively dump all the arguments

#define DUMP_1(first) std::cout << STRINGIZE(first) << "='" << first << "'\n";

#define DUMP_2(first, ...) \
do {\
    std::cout << STRINGIZE(first) << "='" << first << "': ";\
    DUMP_1(__VA_ARGS__);\
} while (false)

#define DUMP_3(first, ...) \
do {\
    std::cout << STRINGIZE(first) << "='" << first << "': ";\
    DUMP_2(__VA_ARGS__);\
} while (false)

#define DUMP_4(first, ...) \
do {\
    std::cout << STRINGIZE(first) << "='" << first << "': ";\
    DUMP_3(__VA_ARGS__);\
} while (false)


// Count the arguments

// Construct the forward/backward list:
#define COUNT_ARGS(...)  COUNT_ARGS_(__VA_ARGS__, RSEQ())
// Forward the list on (macro pain):
#define COUNT_ARGS_(...) COUNT_ARGS_N(__VA_ARGS__)
// The n+1th element is the count (predetermined to support up to n)
#define COUNT_ARGS_N(_1, _2, _3, _4, N, ...) N
#define RSEQ() 4, 3, 2, 1


// This just calls the correct DUMP_#
#define DUMP_(N, ...) CONCAT(DUMP_, N)(__VA_ARGS__)
// Start the line, and start the "recursion"
#define DUMP(...) \
do {\
    std::cout << __PRETTY_FUNCTION__ << ':' << __LINE__ << ": "; \
    DUMP_(COUNT_ARGS(__VA_ARGS__), __VA_ARGS__); \
} while (false)

int main(int argc, char* argv[])
{
    DUMP(argc);

    int i = 10;
    const char str[] = "Hello, world";
    DUMP(i, str);

    return 0;
}

Output:

$ ./a.out 
int main(int, char**):49: argc='1'
int main(int, char**):52: i='10': str='Hello, world'

Edit: Here's a simple version of the counting part (the hard part):

#include <iostream>

#define COUNT_ARGS(...)  COUNT_ARGS_(__VA_ARGS__, RSEQ())
#define COUNT_ARGS_(...) COUNT_ARGS_N(__VA_ARGS__)
#define COUNT_ARGS_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define RSEQ() 6, 5, 4, 3, 2, 1

int main()
{
    std::cout << COUNT_ARGS(a, b) << '\n';
    std::cout << COUNT_ARGS(a, b, c, d) << '\n';

    return 0;
}

Output:

1
2

How does this work?

Well say you set it up to count up to 6, as I have. It constructs a long list, and pulls off the 7th element. That always contains the correct value, because the list it pulls that value from is constructed by putting all the arguments the user gave, followed by a count in reverse.

So by adding another argument, you push the backwards count along 1, and get a higher number from it. Look at this picture: enter image description here