Вопрос

Check the following macro:

#define INPUT (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h")

Goal is to add double parentheses around each tuple resulting in:

((char, "microsecond", "us"))((int, "millisecond", "ms"))((int, "second", "s"))((int, "minute", "min"))((float, "hour", "h"))

Now I use the following macros to do this job:

#define ADD_PAREN_1(A, B, C) ((A, B, C)) ADD_PAREN_2
#define ADD_PAREN_2(A, B, D) ((A, B, C)) ADD_PAREN_1
#define ADD_PAREN_1_END
#define ADD_PAREN_2_END
#define OUTPUT0 ADD_PAREN_1 INPUT
#define OUTPUT1 BOOST_PP_CAT( OUTPUT0, _END )

The result is as follows:

OUTPUT0 is fine:

((char, "microsecond", "us")) ((int, "millisecond", C)) ((int, "second", "s")) ((int, "minute", C)) ((float, "hour", "h")) ADD_PAREN_2

But when BOOST_PP_CAT is called the result of OUTPUT1 is:

float

I do not understand this behaviour. Any hints ?

Note I use Visual Studio 2010

Это было полезно?

Решение

The preprocessor works by scanning and expanding. So when it expands your OUTPUT0 macro, it gives:

ADD_PAREN_1 INPUT
^

Then it scans the next token to see if it is a parenthesis, and if it is it will invoke ADD_PAREN_1 as a function macro. However, it will only see INPUT, So it doesn't invoke ADD_PAREN_1. Next it scans and expands the next token:

ADD_PAREN_1 INPUT
            ^

Which will result in this:

ADD_PAREN_1 (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h")
            ^

Next when you try to use OUTPUT1, it will expand to this:

BOOST_PP_CAT( OUTPUT0, _END )

Which BOOST_PP_CAT will expand OUTPUT0 and then concat the tokens, so you will ultimately get this:

 ADD_PAREN_1 (char, "microsecond", "us")(int, "millisecond", "ms")(int, "second", "s")(int, "minute", "min")(float, "hour", "h") ## _END

As you can see you are concating a parenthesis with _END, which is not allowed, and results in a compiler error. In Visual Studio, you may see different results, as their preprocessor works in mysterious ways.

Ultimately, to make it work you just need to apply an extra scan in the OUTPUT0 macro, something like this:

#define X(x) x
#define OUTPUT0 X(ADD_PAREN_1 INPUT)

Which will work in C preprocessors, I don't know if it will exactly work in Visual Studio(I don't have access to it right now to check), but I do know this works:

#define ADD_PAREN(x) BOOST_PP_CAT(ADD_PAREN_1 x, _END)
#define ADD_PAREN_1(A, B, C) ((A, B, C)) ADD_PAREN_2
#define ADD_PAREN_2(A, B, D) ((A, B, C)) ADD_PAREN_1
#define ADD_PAREN_1_END
#define ADD_PAREN_2_END
#define OUTPUT1 ADD_PAREN(INPUT)

Which is similar to how they do it in boost. See how the BOOST_FUSION_ADAPT_ASSOC_STRUCT_FILLER macro is used here.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top