
Hi, The typeof library defines a macro, REGISTER_TEMPLATE, that is intended for use by the library users. Once people started using the library, they eventually wanted to use this macro inside the Boost PP looping constructs, such as SEQ_FOR_EACH, etc. Since the implementation of REGISTER_TEMPLATE itself uses quite a few looping constructs, people ran into re-entrancy issues. I would like the users to be able to use the macro with whichever looping construct they prefer, and, AFAIU, the easiast (only?) way to achieve this would be to limit the macro implementation to only use automatically re-entrant macros, such as BOOST_PP_REPEAT. Is this the right way to go for this particular case, and generally for any macro that is in the library interface? Thanks, Arkadiy

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Arkadiy Vertleyb
Hi,
The typeof library defines a macro, REGISTER_TEMPLATE, that is intended for use by the library users. Once people started using the library, they eventually wanted to use this macro inside the Boost PP looping constructs, such as SEQ_FOR_EACH, etc.
Since the implementation of REGISTER_TEMPLATE itself uses quite a few looping constructs, people ran into re-entrancy issues.
Which looping constructs does it use?
I would like the users to be able to use the macro with whichever looping construct they prefer, and, AFAIU, the easiast (only?) way to achieve this would be to limit the macro implementation to only use automatically re-entrant macros, such as BOOST_PP_REPEAT.
That depends. If you can (re)specify the implementation in terms of only one "recursion state" (such as 'z' for REPEAT), then it is best to provide both: #define REGISTER(...) REGISTER_Z(DEDUCE_Z(), ...) #define REGISTER_Z(z, ...) // ... If the implementation requires (or is significantly better using) multiple states, then, yes, you should have it do everything automatically.
Is this the right way to go for this particular case, and generally for any macro that is in the library interface?
That's the general answer. The pp-lib fails to provide a unifying way to deal with recursion, which is why these problems pop up from time to time. Regards, Paul Mensonides

"Paul Mensonides" <pmenso57@comcast.net> wrote
Since the implementation of REGISTER_TEMPLATE itself uses quite a few looping constructs, people ran into re-entrancy issues.
Which looping constructs does it use?
Currently at least the following: REPEAT ENUM SEQ_ENUM SEQ_FOR_EACH_I SEQ_FOLD_LEFT
I would like the users to be able to use the macro with whichever looping construct they prefer, and, AFAIU, the easiast (only?) way to achieve this would be to limit the macro implementation to only use automatically re-entrant macros, such as BOOST_PP_REPEAT.
That depends. If you can (re)specify the implementation in terms of only one "recursion state" (such as 'z' for REPEAT), then it is best to provide both:
#define REGISTER(...) REGISTER_Z(DEDUCE_Z(), ...) #define REGISTER_Z(z, ...) // ...
Is there any significant benefit of specifying z manually? Does it depend on the depth of nesting (typeof just has a sequence inside another sequence)? Regards, Arkadiy

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Arkadiy Vertleyb
Which looping constructs does it use?
Currently at least the following:
REPEAT ENUM SEQ_ENUM SEQ_FOR_EACH_I SEQ_FOLD_LEFT
Which one is failing?
"recursion state" (such as 'z' for REPEAT), then it is best to provide both:
#define REGISTER(...) REGISTER_Z(DEDUCE_Z(), ...) #define REGISTER_Z(z, ...) // ...
Is there any significant benefit of specifying z manually? Does it depend on the depth of nesting (typeof just has a sequence inside another sequence)?
There *can* be a significant benefit in speed, but it usually isn't from a top level invocation. I.e. at the top-level, REPEAT should always be used directly (and let it deduce 'z' itself). But, inner REPEAT's can be a lot more efficient if it doesn't have to deduce 'z' repeatedly. For example, #include <boost/preprocessor/arithmetic/inc.hpp> #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/punctuation/comma_if.hpp> #include <boost/preprocessor/repetition/repeat.hpp> #define FIXED(z, n, x) \ BOOST_PP_COMMA_IF(n) x \ /**/ #define TTP(z, n, id) \ BOOST_PP_COMMA_IF(n) \ template< \ BOOST_PP_REPEAT(BOOST_PP_INC(n), FIXED, class) \ > class BOOST_PP_CAT(id, n) \ /**/ BOOST_PP_REPEAT(50, TTP, T) The inner REPEAT in TTP deduces 'z' over and over and over, which is a waste of time. 'z' is already known (passed as an argument), so it is more efficient to use it: #define TTP(z, n, id) \ BOOST_PP_COMMA_IF(n) \ template< \ BOOST_PP_REPEAT_ ## z(BOOST_PP_INC(n), FIXED, class) \ > class BOOST_PP_CAT(id, n) \ /**/ Likewise, it is convenient for other macros that need 'z' (explicitly or implicitly) to provide variants that take it as an argument. Those variants aren't the primary interface, but they leave the efficiency choice to the user. In the case of 'z', the deduction is doing a binary search on only three possible values for 'z', so it isn't all that significant. For 'd', on the other hand, it is doing a somewhat more significant binary search on 256 possible values. However, I think that if your macro is using multiple different library recursion states (e.g. 'z' and 'd'), you might as well just have a single automatic interface. Most-likely-to-be-minor efficiency gains aren't worth a combinatorial explosion of interfaces. [This whole thing is the pp-lib's greatest problem. There is no unifying and extensible way that recursion is accomplished. Instead, there are multiple recursion states that aren't very extensible without macro replication. Chaos solves this problem completely, but the techniques used are way too advanced for what can be put into Boost.] Regards, Paul Mensonides

"Paul Mensonides" wrote
[mailto:boost-bounces@lists.boost.org] On Behalf Of Arkadiy Vertleyb
Which looping constructs does it use?
Currently at least the following:
REPEAT ENUM SEQ_ENUM SEQ_FOR_EACH_I SEQ_FOLD_LEFT
Which one is failing?
I know that REPEAT is working and SEQ_FOR_EACH_I is failing -- if you want me I can try the rest. Happy New Year! Regards, Arkadiy

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Arkadiy Vertleyb
REPEAT ENUM SEQ_ENUM SEQ_FOR_EACH_I SEQ_FOLD_LEFT
Which one is failing?
I know that REPEAT is working and SEQ_FOR_EACH_I is failing -- if you want me I can try the rest.
As you alluded to, SEQ_FOR_EACH_I is not auto-recursive, and thus can't be used inside itself. In order to make it as generally useful as possible, you can respecify you're macro so it only uses auto-recursive constructs. To give you an idea of why it isn't designed to work... SEQ_FOR_EACH_I is implemented in terms of FOR. SEQ_FOR_EACH_I passes to FOR a predicate (which determines if the sequence is "nil"), an operation (which increments 'i' and pops the head of the sequence), and a target macro (which calls the user-defined macro passed to SEQ_FOR_EACH_I). In order to make the above work, you'd need to replicate SEQ_FOR_EACH_I and the target macro passed to FOR however many times you want it to be reentrant. Of course, for consistency, you'd need to do that for all of the other higher-order algorithms that are built on top of other higher-order algorithms in the library--which is a lot of replication. I consider this a huge extensibility failure of the pp-lib (and have for several years now).
Happy New Year!
To you as well. Regards, Paul Mensonides
participants (2)
-
Arkadiy Vertleyb
-
Paul Mensonides