[preprocessor] nested for_each not working like expected
I can't figure out what I'm doing wrong. According to the docs, this should work: #define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) BOOST_PP_SEQ_FOR_EACH_R(R, M4, ELEM, S1); BOOST_PP_SEQ_FOR_EACH(M2, ~, S0) I expect to get a sequence like (0,1)(0,2)... for each combination. I get gibberish. (Please don't tell me to use FOR_EACH_PRODUCT. In my real use case, M2 and M4 are far more complicated, and a simple product won't do.) Can anybody please tell me what I'm doing wrong? TIA, -- Eric Niebler Boost.org
I can't figure out what I'm doing wrong. According to the docs, this should work:
#define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) BOOST_PP_SEQ_FOR_EACH_R(R, M4, ELEM, S1); BOOST_PP_SEQ_FOR_EACH(M2, ~, S0)
I expect to get a sequence like (0,1)(0,2)... for each combination. I get gibberish. (Please don't tell me to use FOR_EACH_PRODUCT. In my real use case, M2 and M4 are far more complicated, and a simple product won't do.)
Can anybody please tell me what I'm doing wrong? TIA,
According to [1], only BOOST_PP_FOR, BOOST_PP_WHILE, and BOOST_PP_REPEAT are reentrant. Other looping macros, like SEQ_FOR_EACH, are not. I find this annoying from time to time as well. Regards, Nate [1] http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/
[1] http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/
Sorry, that link was useless (silly PP doc format...) I meant: http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/topics/reentrancy... Regards, Nate
On 13-08-07 11:58 AM, Nathan Ridge wrote:
I can't figure out what I'm doing wrong. According to the docs, this should work:
#define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) BOOST_PP_SEQ_FOR_EACH_R(R, M4, ELEM, S1); BOOST_PP_SEQ_FOR_EACH(M2, ~, S0)
I expect to get a sequence like (0,1)(0,2)... for each combination. I get gibberish. (Please don't tell me to use FOR_EACH_PRODUCT. In my real use case, M2 and M4 are far more complicated, and a simple product won't do.)
Can anybody please tell me what I'm doing wrong? TIA,
According to [1], only BOOST_PP_FOR, BOOST_PP_WHILE, and BOOST_PP_REPEAT are reentrant. Other looping macros, like SEQ_FOR_EACH, are not.
Yes, but [2] indicates that SEQ_FOR_EACH is implemented in terms of FOR and has a _R version for reentry. Why is it provided if it doesn't work? [2] http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/ref/seq_for_each_... -- Eric Niebler Boost.org
I can't figure out what I'm doing wrong. According to the docs, this should work:
#define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) BOOST_PP_SEQ_FOR_EACH_R(R, M4, ELEM, S1); BOOST_PP_SEQ_FOR_EACH(M2, ~, S0)
I expect to get a sequence like (0,1)(0,2)... for each combination. I get gibberish. (Please don't tell me to use FOR_EACH_PRODUCT. In my real use case, M2 and M4 are far more complicated, and a simple product won't do.)
Can anybody please tell me what I'm doing wrong? TIA,
According to [1], only BOOST_PP_FOR, BOOST_PP_WHILE, and BOOST_PP_REPEAT are reentrant. Other looping macros, like SEQ_FOR_EACH, are not.
Yes, but [2] indicates that SEQ_FOR_EACH is implemented in terms of FOR and has a _R version for reentry. Why is it provided if it doesn't work?
[2] http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/ref/seq_for_each_...
My understanding is that SEQ_FOR_EACH is reentrant with FOR, but not with itself. So one of the inner and outer loops can use SEQ_FOR_EACH, and the other FOR, and this works fine despite the fact that SEQ_FOR_EACH is implemented in terms of for. However they cannot both use SEQ_FOR_EACH. Also, I believe the _R versions are for efficiency only - that, is, the make re-entrancy more efficient in cases where it would already be possible, just less efficient, without the _R. I'm not certain about this, though. Perhaps Paul or Edward can clarify these points. Regards, Nate
AMDG On 08/07/2013 12:48 PM, Eric Niebler wrote:
On 13-08-07 11:58 AM, Nathan Ridge wrote:
According to [1], only BOOST_PP_FOR, BOOST_PP_WHILE, and BOOST_PP_REPEAT are reentrant. Other looping macros, like SEQ_FOR_EACH, are not.
Yes, but [2] indicates that SEQ_FOR_EACH is implemented in terms of FOR and has a _R version for reentry. Why is it provided if it doesn't work?
The _R version is for reentering BOOST_PP_FOR. BOOST_PP_SEQ_FOR_EACH_R itself is not reentrant.
[2] http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/ref/seq_for_each_...
In Christ, Steven Watanabe
Yes, but [2] indicates that SEQ_FOR_EACH is implemented in terms of FOR and has a _R version for reentry. Why is it provided if it doesn't work?
The `R` is the recursion state for the `BOOST_PP_FOR` macro. Because it is reentrant, you don't pass the state in as a parameter, but instead append it to the the end of the macro(ie `BOOST_PP_FOR_1`, `BOOST_PP_FOR_2`, `BOOST_PP_FOR_3`, etc). `BOOST_PP_SEQ_FOR_EACH_R` uses `BOOST_PP_FOR` internally and provides a parameter for this state so it can reenter `BOOST_PP_FOR` effeciently(otherwise it would need to deduce the state of `BOOST_PP_FOR`). If `BOOST_PP_SEQ_FOR_EACH` was reentrant as well, there would be an additional recursion state that would needed to be appended to the end. Paul Fultz II
As others have stated, `BOOST_PP_SEQ_FOR_EACH` is not reentrant. One simple workaround is to use deferred expressions, like this: #define BOOST_PP_SEQ_FOR_EACH_R_ID() BOOST_PP_SEQ_FOR_EACH_R #define DEFER(x) x BOOST_PP_EMPTY() #define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(R, M4, ELEM, S1); BOOST_PP_EXPAND(BOOST_PP_SEQ_FOR_EACH_R(1, M2, ~, S0)) If more depths are needed, just add more `BOOST_PP_EXPAND` macros. Also, you can reuse the recursion state so you could write `M2` like this: #define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(1, M4, ELEM, S1); or like this, if you don't know the original recursion state: #define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(BOOST_PP_DEC(R), M4, ELEM, S1); Paul Fultz II
On 13-08-07 12:58 PM, paul Fultz wrote:
As others have stated, `BOOST_PP_SEQ_FOR_EACH` is not reentrant. One simple workaround is to use deferred expressions, like this:
#define BOOST_PP_SEQ_FOR_EACH_R_ID() BOOST_PP_SEQ_FOR_EACH_R #define DEFER(x) x BOOST_PP_EMPTY()
#define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(R, M4, ELEM, S1); BOOST_PP_EXPAND(BOOST_PP_SEQ_FOR_EACH_R(1, M2, ~, S0))
If more depths are needed, just add more `BOOST_PP_EXPAND` macros. Also, you can reuse the recursion state so you could write `M2` like this:
#define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(1, M4, ELEM, S1);
or like this, if you don't know the original recursion state:
#define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(BOOST_PP_DEC(R), M4, ELEM, S1);
I won't pretend to understand how this works, but it does. Thanks! -- Eric Niebler Boost.org
On 07/08/13 22:25, Eric Niebler wrote:
On 13-08-07 12:58 PM, paul Fultz wrote:
As others have stated, `BOOST_PP_SEQ_FOR_EACH` is not reentrant. One simple workaround is to use deferred expressions, like this:
#define BOOST_PP_SEQ_FOR_EACH_R_ID() BOOST_PP_SEQ_FOR_EACH_R #define DEFER(x) x BOOST_PP_EMPTY()
#define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(R, M4, ELEM, S1); BOOST_PP_EXPAND(BOOST_PP_SEQ_FOR_EACH_R(1, M2, ~, S0))
If more depths are needed, just add more `BOOST_PP_EXPAND` macros. Also, you can reuse the recursion state so you could write `M2` like this:
#define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(1, M4, ELEM, S1);
or like this, if you don't know the original recursion state:
#define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(BOOST_PP_DEC(R), M4, ELEM, S1);
I won't pretend to understand how this works, but it does. Thanks!
Why not just use BOOST_PP_REPEAT otherwise? It's simple and it always works. Just replace ELEM by BOOST_PP_SEQ_ELEM(N, S0)
On 13-08-09 04:40 AM, Mathias Gaunard wrote:
On 07/08/13 22:25, Eric Niebler wrote:
On 13-08-07 12:58 PM, paul Fultz wrote:
As others have stated, `BOOST_PP_SEQ_FOR_EACH` is not reentrant. One simple workaround is to use deferred expressions, like this:
#define BOOST_PP_SEQ_FOR_EACH_R_ID() BOOST_PP_SEQ_FOR_EACH_R #define DEFER(x) x BOOST_PP_EMPTY()
#define S0 (0)(1)(2)(3) #define S1 (5)(6)(7)(8) #define M4(R, DATA, ELEM) (DATA,ELEM) #define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(R, M4, ELEM, S1); BOOST_PP_EXPAND(BOOST_PP_SEQ_FOR_EACH_R(1, M2, ~, S0))
If more depths are needed, just add more `BOOST_PP_EXPAND` macros. Also, you can reuse the recursion state so you could write `M2` like this:
#define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(1, M4, ELEM, S1);
or like this, if you don't know the original recursion state:
#define M2(R, DATA, ELEM) DEFER(BOOST_PP_SEQ_FOR_EACH_R_ID)()(BOOST_PP_DEC(R), M4, ELEM, S1);
I won't pretend to understand how this works, but it does. Thanks!
Why not just use BOOST_PP_REPEAT otherwise? It's simple and it always works.
Just replace ELEM by BOOST_PP_SEQ_ELEM(N, S0)
Isn't accessing the Nth element of a PP sequence O(N)? Seems to me that what Paul posted would be more efficient. -- Eric Niebler Boost.org
I won't pretend to understand how this works, but it does. Thanks!
Your weclome. Here's how it works. First, it uses a deferred expression, which requires one more scan to fully expand like this: #define EMPTY() #define DEFER(x) x EMPTY() #define EXPAND(...) __VA_ARGS__ #define A() 123 A() // Expands to 123 DEFER(A)() // Expands to A () because it requires one more scan to fully expand EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan This will help dance around getting our macros from being painted blue(which won't expand any further, no matter what). When the preprocessor scans and then expands a macro, it first creates a disabling context. A disabling context is basically a list of tokens, that if the preprocessor see them during the scan it will paint them blue. So in this case, when it expands `BOOST_PP_SEQ_FOR_EACH_R(1, M2, ~, S0)` it will add `BOOST_PP_SEQ_FOR_EACH_R` to the disabling context. So then it scans and expands like this: BOOST_PP_SEQ_FOR_EACH_R_ID ()(1, M4, 0, (5)(6)(7)(8)); BOOST_PP_SEQ_FOR_EACH_R_ID ()(1, M4, 1, (5)(6)(7)(8)); BOOST_PP_SEQ_FOR_EACH_R_ID ()(1, M4, 2, (5)(6)(7)(8)); BOOST_PP_SEQ_FOR_EACH_R_ID ()(1, M4, 3, (5)(6)(7)(8)); After its done, `BOOST_PP_SEQ_FOR_EACH_R` is removed from the disabling context. All we need to do is apply one more scan to expand each of the `BOOST_PP_SEQ_FOR_EACH_R_ID` macros. We can use `BOOST_PP_EXPAND` to do this, but when it expands, `BOOST_PP_SEQ_FOR_EACH_R` is not in the disabling context, so it will fully expand. Hopefully, that makes sense. Perhaps, Paul Mensonides, can explain it better than I can.
Why not just use BOOST_PP_REPEAT otherwise? It's simple and it always works.
Using deferred expression is a more general purpose solution. Eric never gave any details to what he was doing. Perhaps, he was already using `BOOST_PP_REPEAT`. Also, to use effeciently requires passing along another recursion state. Plus, using deferred expressions scales further, if 3 `BOOST_PP_SEQ_FOR_EACH` macros are needed, it can be extended easily.
Isn't accessing the Nth element of a PP sequence O(N)? Seems to me that what Paul posted would be more efficient.
Actually accessing the elements are near random access, because it uses sequence iteration, which is super fast(Still not necessarily faster than using nested `BOOST_PP_SEQ_FOR_EACH` macros, although I haven't taken the time to measure it). If you don't need a data parameter, its much faster using sequence iteration, than `BOOST_PP_SEQ_FOR_EACH`, something like this: #define PRINT(x) x #define PRINT_SEQ(x) BOOST_PP_CAT(PRINT_SEQ_1 x, _END) #define PRINT_SEQ_1(x) PRINT(x) PRINT_SEQ_2 #define PRINT_SEQ_2(x) PRINT(x) PRINT_SEQ_1 #define PRINT_SEQ_1_END #define PRINT_SEQ_2_END PRINT_SEQ((1)(2)(3)) // Expands to 1 2 3 Paul Fultz II
participants (5)
-
Eric Niebler
-
Mathias Gaunard
-
Nathan Ridge
-
paul Fultz
-
Steven Watanabe