Foreach macro on macros arguments

Viewed 26086

I wonder if it is possible to write a macro foreach on macros arguments. Here is what want to do:

#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL(...) ? ? ? THE PROBLEM ? ? ? 

And possible usage:

int a = 1, b = 3, d = 0;
PRINT_ALL(a,b,d);

Here is what I achieved so far

#define FIRST_ARG(arg,...) arg
#define AFTER_FIRST_ARG(arg,...) , ##__VA_ARGS__     
#define PRINT(a) printf(#a": %d", a)
#define PRINT_ALL PRINT(FIRST_ARG(__VA_ARGS__)); PRINT_ALL(AFTER_FIRST_ARG(__VA_ARGS__))

This is a recursive macro, which is illegal. And another problem with that is stop condition of recursion.

7 Answers

You can use Boost.PP (after adding Boost's boost folder to your list of include directories) to get macros for this. Here's an example (tested with GCC 8.1.0):

#include <iostream>
#include <limits.h>
#include <boost/preprocessor.hpp>

#define WRITER(number,middle,elem) std::cout << \
    number << BOOST_PP_STRINGIZE(middle) << elem << "\n";
#define PRINT_ALL(...) \
    BOOST_PP_SEQ_FOR_EACH(WRITER, =>, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

int main (int argc, char *argv[])
{
    PRINT_ALL(INT_MAX, 123, "Hello, world!");
}

Output:

2=>2147483647
3=>123
4=>Hello, world!

The BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) part converts the variable-argument list to Boost's traditional way of expressing multiple arguments as a single argument, which looks like this: (item1)(item2)(item3).

Not sure why it starts numbering the arguments at two. The documentation just describes the first parameter as "the next available BOOST_PP_FOR repetition".

Here's another example that defines an enum with the ability to write it as a string to an ostream, which also enables Boost's lexical_cast<string>:

#define ENUM_WITH_TO_STRING(ENUMTYPE, ...)                   \
    enum ENUMTYPE {                                          \
        __VA_ARGS__                                          \
    };                                                       \
    inline const char* to_string(ENUMTYPE value) {           \
        switch (value) {                                     \
            BOOST_PP_SEQ_FOR_EACH(_ENUM_TO_STRING_CASE, _,   \
               BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))        \
            default: return nullptr;                         \
        }                                                    \
    }                                                        \
    inline std::ostream& operator<<(std::ostream& os, ENUMTYPE v)\
        { return os << to_string(v); }
#define _ENUM_TO_STRING_CASE(_,__,elem)                      \
    case elem: return BOOST_PP_STRINGIZE(elem);

ENUM_WITH_TO_STRING(Color, Red, Green, Blue)

int main (int argc, char *argv[])
{
    std::cout << Red << Green << std::endl;
    std::cout << boost::lexical_cast<string>(Blue) << std::endl;
}

Output:

RedGreen
Blue
Related