
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Tobias Schwinger
Here are two questions regarding the BOOST_PP_IS_*ARY macros:
Is it safe to use this technique with sequences, e.g.
#define OPT_SEQ_CAT(seq) \ BOOST_PP_IIF(BOOST_PP_IS_UNARY(seq),BOOST_PP_SEQ_CAT, \ BOOST_PP_TUPLE_EAT(1))(seq)
OPT_SEQ_CAT(-) // expands to nothing OPT_SEQ_CAT((b)(o)(o)(s)(t)) // expands to 'boost'
?!
Yes--with regard to what I say below. Those macros detect "parenthetic expressions"--which are non-pathological (see below) sequences of preprocessing tokens that begin with something that's parenthesized. E.g. these work also: IS_UNARY((+) -) IS_UNARY((+)(not, unary)) What compilers/preprocessors do you usually use?
And why do these components live in 'detail' (as opposed to 'tuple')? Because of Borland?
Because of Borland and others. The problem with these macros is that they don't work on several supported compilers. On others, they work, but only sometimes (IOW, they are unstable). In the latter case, it isn't just a simple case of "if you pass it this, it doesn't work". Rather, it is the result of fundamentally broken preprocessors that do things in weird orders (probably as some sort of optimization scheme). The library can get away with using them internally because the library is doing its best to force certain things to happen "nearby" where they should happen--and its doing it all over the entire library (look at the the VC configuration, for example). The result for client use is highly unpredictable because client code doesn't contain the scaffolding (the pp-lib bends over backward to minimize the this in user code). On reasonably good preprocessors, this isn't a problem at all, and using them is perfectly safe and predictable. ----- Regarding your DO_UNARY-esque macros... They would be more general if they were something like: #define DO_UNARY(macro, x) BOOST_PP_IIF(BOOST_PP_IS_UNARY(x),macro,BOOST_PP_TUPLE_EAT(1)) \ /**/ DO_UNARY(macro, x)(x) Even though it is more verbose, it keeps the actual invocation of 'macro' out of DO_UNARY (so-to-speak). The way you have it will fail if 'macro' tries to use DO_UNARY. This isn't a big deal at a small scale, but it's what I call a "vertical dependency". Vertical dependencies drastically lower the encapsulation of macros, which creates difficult to diagnose problems. The way that I have it won't fail if 'macro' tries to use DO_UNARY. ----- BTW, with variadics, you can do all kinds of fun things related to optional/default arguments or overloading based on number of arguments. Some simple examples... #define A_1(a) -a #define A_2(a, b) a - b #define A_3(a, b, c) a - b - c #define A(...) \ CHAOS_PP_QUICK_OVERLOAD(A_, __VA_ARGS__)(__VA_ARGS__) \ /**/ A(1) // -1 A(1, 2) // 1 - 2 A(1, 2, 3) // 1 - 2 - 3 The QUICK_OVERLOAD macro is a constant-time operation, but is limited to something like 25 arguments. There is also an OVERLOAD macro that doesn't have that limitation, but isn't constant-time (To be technically accurate, it counts n arguments in floor(n / 10) + n mod 10 + 2 steps.) ----- // B(a, b = 123) -> a + b #define B(...) \ CHAOS_PP_NON_OPTIONAL(__VA_ARGS__) \ + CHAOS_PP_DEFAULT(123, __VA_ARGS__) \ /**/ B(1, 2) // 1 + 2 B(67) // 67 + 123 Note that there is no difference (because of placemarkers) between 0 and 1 arguments. Furthermore, emptiness cannot be detected without restricting input (not counting what I call pathological input--which is something like passing just LPAREN()). Because of that, optional arguments (with or without default values) must always be "attached" to a non-optional argument--hence the name NON_OPTIONAL. Beyond that, you can have any number of optional or default arguments, but there must always be one that is required--which is fine for the majority of circumstances. // M(x, y = 2, z = 3) -> x + y + z #define M(...) \ CHAOS_PP_NON_OPTIONAL(__VA_ARGS__) \ + CHAOS_PP_DEFAULT_AT(0, 2, __VA_ARGS__), \ + CHAOS_PP_DEFAULT_AT(1, 3, __VA_ARGS__) \ /**/ M(a) // a + 2 + 3 M(a, b) // a + b + 3 M(a, b, c) // a + b + c The library itself uses optional arguments in a variety of places: CHAOS_PP_AUTO_REPEAT(3, A, data) // A(s, 0, data) A(s, 1, data) A(s, 2, data) CHAOS_PP_AUTO_REPEAT(3, B) // B(s, 0) B(s, 1) B(s, 2) CHAOS_PP_AUTO_REPEAT(3, C, d1, d2) // C(s, 0, d1, d2) C(s, 1, d1, d2) C(s, 2, d1, d2) (etc.) As referred to above, the 'count' and 'macro' arguments are required, but the algorithm makes the auxiliary data argument optional (as well as variadic) which is propogates on to the called macro. Regards, Paul Mensonides