BOOST_PP_SEQ_FOR_EACH_I

Hi all, I don't think that reentrancy mechanism is working for this macro. Is it supposed to? The following example illustrates my problem (I tried both automatic and manual methods): #include <boost/preprocessor/seq/for_each_i.hpp> #define seq\ ((1)(2)(3))\ ((4)(5)(6))\ ((7)(8)(9)) #define MACRO(r, data, i, elem)\ BOOST_PP_SEQ_FOR_EACH_I(MACRO2, ~, elem) #define MACRO1(r, data, i, elem)\ BOOST_PP_SEQ_FOR_EACH_I_R(r, MACRO2, ~, elem) #define MACRO2(r, data, i, elem)\ elem BOOST_PP_SEQ_FOR_EACH_I(MACRO, ~, seq) BOOST_PP_SEQ_FOR_EACH_I(MACRO1, ~, seq) Both preprocessors I tried, VC71 and GCC 3.4.2, produce the same result: BOOST_PP_SEQ_FOR_EACH_I(MACRO2, ~, (1)(2)(3) ) BOOST_PP_SEQ_FOR_EACH_I(MACRO2, ~, (4)(5)(6) ) BOOST_PP_SEQ_FOR_EACH_I(MACRO2, ~, (7)(8)(9) ) BOOST_PP_SEQ_FOR_EACH_I_M(3, (MACRO2, ~, (1)(2)(3) (nil), 0)) BOOST_PP_SEQ_FOR_EACH_I_M(4, (MACRO2, ~, (2)(3) (nil), 1)) BOOST_PP_SEQ_FOR_EACH_I_M(5, (MACRO2, ~, (3) (nil), 2)) BOOST_PP_SEQ_FOR_EACH_I_M(4, (MACRO2, ~, (4)(5)(6) (nil), 0)) BOOST_PP_SEQ_FOR_EACH_I_M(5, (MACRO2, ~, (5)(6) (nil), 1)) BOOST_PP_SEQ_FOR_EACH_I_M(6, (MACRO2, ~, (6) (nil), 2)) BOOST_PP_SEQ_FOR_EACH_I_M(5, (MACRO2, ~, (7)(8)(9) (nil), 0)) BOOST_PP_SEQ_FOR_EACH_I_M(6, (MACRO2, ~, (8)(9) (nil), 1)) BOOST_PP_SEQ_FOR_EACH_I_M(7, (MACRO2, ~, (9) (nil), 2)) Which, I think, indicates that reentrancy is not working. Am I missing something? Regards, Arkadiy

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Arkadiy Vertleyb
Hi Arkadiy.
I don't think that reentrancy mechanism is working for this macro. Is it supposed to?
SEQ_FOR_EACH_I is not a reentrant construct. Basically, only macros with a "concatenation" variant (e.g. REPEAT_ ## z) are reentrant. All others are reentrant into those macros. This is a fundamental flaw of the pp-lib design. In order to allow what you're suggesting, I'd have to make hundreds of copies of every higher-order macro in the library. Is there any particular reason why you need 'i' in the outer dimension? If not, regular SEQ_FOR_EACH and SEQ_FOR_EACH_I should work together: #include <boost/preprocessor/seq/for_each.hpp> #include <boost/preprocessor/seq/for_each_i.hpp> #define A(r, data, elem) \ BOOST_PP_SEQ_FOR_EACH_I_R(r, B, ~, elem) \ /**/ #define B(r, data, i, elem) elem BOOST_PP_SEQ_FOR_EACH( A, ~, ((1)(2)(3)) ((4)(5)(6)) ((7)(8)(9)) ) (Of course, this design limitation doesn't exist in Chaos. All higher-order macros are reentrant--without implementation replication.) Regards, Paul Mensonides

Hi Paul,
Is there any particular reason why you need 'i' in the outer dimension? If not, regular SEQ_FOR_EACH and SEQ_FOR_EACH_I should work together:
Actually yes, I do need it -- I am generating series of typedefs, each one depending on the previous, and I need to generate names for them. I also need 'i' in the inner loop. My guess is, I have to use REPEAT, passing sequence as auxiliary data, and using SEQ_ELEM to access current element. This looks a little clumsier, and probably less efficient (?) than more native SEQ_FOR_EACH, but it's fine with me as long as I know that this is the best I can do in this case (REPEAT *is* reentrant, right?). Also, do I understand correctly, that, if I wanted to wrap the call to REPEAT in yet another macro, something like SEQ_ENUM[_TRAILING](seq, macro), I would have to take care myself of this macro to be reentrant? In the simplest case, I would just have to provide two identical macros, SEQ_ENUM_0 and SEQ_ENUM_1, and use the first one outside, and the second -- inside (this would take care of one layer nesting).
(Of course, this design limitation doesn't exist in Chaos. All higher-order macros are reentrant--without implementation replication.)
But I would still have to take special care of reentrancy of my own macros, correct? I actually think disallowing recursion of function-like macros is totally artificial and unjustified in the first place :-( Regards, Arkadiy

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Arkadiy Vertleyb
Hi Paul,
Is there any particular reason why you need 'i' in the outer dimension? If not, regular SEQ_FOR_EACH and SEQ_FOR_EACH_I should work together:
Actually yes, I do need it -- I am generating series of typedefs, each one depending on the previous, and I need to generate names for them. I also need 'i' in the inner loop.
Okay.
My guess is, I have to use REPEAT, passing sequence as auxiliary data, and using SEQ_ELEM to access current element. This looks a little clumsier, and probably less efficient (?) than more native SEQ_FOR_EACH, but it's fine with me as long as I know that this is the best I can do in this case (REPEAT *is* reentrant, right?).
There are a variety of ways that you could do it. The first way that I thought of would be to transform the outer sequence with SEQ_FOR_EACH_I and then process the result with SEQ_FOR_EACH_I... #include <boost/preprocessor/seq/for_each.hpp> #include <boost/preprocessor/seq/for_each_i.hpp> #include <boost/preprocessor/tuple/elem.hpp> #define A(r, _, i, elem) ((i, elem)) #define B(r, _, pair) \ BOOST_PP_SEQ_FOR_EACH_I( \ C, \ BOOST_PP_TUPLE_ELEM(2, 0, pair), \ BOOST_PP_TUPLE_ELEM(2, 1, pair) \ ) \ /**/ #define C(r, i, j, elem) elem(i, j) BOOST_PP_SEQ_FOR_EACH( B, ~, BOOST_PP_SEQ_FOR_EACH_I( A, ~, ((a)(b)(c)) ((d)(e)(f)) ((g)(h)(i)) ) ) --> a(0, 0) b(0, 1) c(0, 2) d(1, 0) e(1, 1) f(1, 2) g(2, 0) h(2, 1) i(2, 2) That is probably what I'd do, and is probably more efficient than REPEAT w/SEQ_ELEM. To answer you're other question, yes, REPEAT is reentrant.
Also, do I understand correctly, that, if I wanted to wrap the call to REPEAT in yet another macro, something like SEQ_ENUM[_TRAILING](seq, macro), I would have to take care myself of this macro to be reentrant? In the simplest case, I would just have to provide two identical macros, SEQ_ENUM_0 and SEQ_ENUM_1, and use the first one outside, and the second -- inside (this would take care of one layer nesting).
Basically, yes. This is pretty much what the library has to do as well for anything reentrant (but with far more replications). I've been thinking about various ways to generalize some of this that I might be able to get away with in the pp-lib (read: ...that I might be able to implement even on typical preprocessors...). However, that might imply always doing everything automatically--meaning with automatic recursion on everything and no syntax for direct reentrance (i.e. no z, r, d, etc., state variables). Even if I take away the compiler workarounds in the pp-lib, and even if I don't use any of the "fancy" things that Chaos does, there are still some fundamental problems with the pp-lib (IMO). In particular, I don't like this exact situation where reuse of algorithms to create other algorithms creates a dependency and removing the dependency requires removing the reuse. I also don't like there being multiple states (such as z, r, and d). The main idea is to distinguish between entry point and algorithmic step. As an example, WHILE needs to be able to make many algorithmic steps, but it doesn't really need all that many entry points (i.e. is unlikely that a user would need more than, say, two or three nested invocations of WHILE). Given that, it is possible to map a small set of entry points onto a larger set of algorithmic steps. The questions are whether it is possible to implement this on buggy preprocessors (which I think it is) and whether it is worth it because it is still less than ideal.
(Of course, this design limitation doesn't exist in Chaos. All higher-order macros are reentrant--without implementation replication.)
But I would still have to take special care of reentrancy of my own macros, correct?
Yes, but it isn't terribly difficult. There is a difference also between recursion and arbitrary reentrancy. I.e. designing a recursive algorithm with Chaos is fairly easy, designing a higher-order recursive algorithm is slightly more difficult, but still fairly easy. Neither involve macro replication.
I actually think disallowing recursion of function-like macros is totally artificial and unjustified in the first place :-(
I agree. Not only that, but it is also the single greatest cause of inefficiency in complex macro expansion. OTOH, the lack of recursion allows some meaningful things that could not be done any other way (assuming the preprocessor is otherwise the same). Regards, Paul Mensonides

Paul, Thanks for the clarification. The problem is now solved.
Yes, but it isn't terribly difficult. There is a difference also between recursion and arbitrary reentrancy. I.e. designing a recursive algorithm with Chaos is fairly easy, designing a higher-order recursive algorithm is slightly more difficult, but still fairly easy. Neither involve macro replication.
Beautifull. Unfortunately, switching to Chaos is not an option for us right now, because of the portability reasons. Also, our lib is already in the review queue, and it's not an appropriate moment to make such a drammatic change -- we use Boost.Preprocessor very heavily. Despite some unfortunate problems, like one we discussed, I think Boost.Preprocessor is a great library, especially considering what it has to deal with. Regards, Arkadiy
participants (2)
-
Arkadiy Vertleyb
-
Paul Mensonides