"Paul Mensonides" <[EMAIL PROTECTED]> writes: > ----- Original Message ----- > From: "Paul Mensonides" <[EMAIL PROTECTED]> >> > Unfortunately I like all of the above except the last one. I'd even >> > like the last one, perhaps best of all, if it were: >> > >> > BOOST_WORKAROUND(__SUNPRO_CC, (?) <= 0x530) >> > >> > So I think I'll have to ask other people to weigh in here. Do you >> > have any preferences, anyone? >> >> BOOST_WORKAROUND(__SUNPRO_CC, (!) <= 0x530) > > [Here's an implementation of this from scratch, because it is easier to > explain that way.
Thanks for the tutorial! > If you don't care how it works, skip to the end which is > the final, clean solution (a lot cleaner that this!)] > > We need several helper macros: > > ----- CAT ----- > > #define CAT(a, b) CAT_P(a, b) > #define CAT_P(a, b) a ## b > > The macro delays to allow the arguments 'a' and 'b' to expand if necessary. > This avoids having to have delays in the macros elsewhere. > > ----- ELEM_x ----- > > #define ELEM_0(a, b) a > #define ELEM_1(a, b) b > > These two macros simply return the first or second argument depending on the > suffix. These two macros form the shared implementation of the next two. > > ----- IIF ----- > > #define IIF(c, t, f) CAT(ELEM_, cond)(f, t) > > If 'c' is 1 then this macro returns 't'. If 'c' is 0, it returns 'f'. > [Note: I use the name "IIF" here to distinguish it from "IF" which performs > a boolean conversion on the condition operand.] In effect, "CAT_P(ELEM_, > cond)" returns either "ELEM_0" or "ELEM_1" which immediately expands against > "(f, t)". BTW, IIF always bothered me when I saw it; it's non-memnonic, and I never knew what it did. IF_BOOL would have been better in that respect. > ----- SPLIT ----- > > #define SPLIT(n, im) CAT(ELEM_, n)(im) > > This one is trickier. The argument 'im' is a single argument that > immediately expands to two arguments (in and of itself). OK. What you mean is that im *must expand* to two arguments, though. It's a requirement on the invoker of the split macro. > This macro returns > either the first or second of those two arguments. E.g. > > #define NAME Abrahams, David > > SPLIT(0, NAME) // Abrahams > SPLIT(1, NAME) // David > > ----- IS_UNARY ----- > > #define IS_UNARY(expr) /* ... */ \ > SPLIT( \ > 0, \ > CAT( \ > IS_UNARY_, \ > IS_UNARY_CHECK expr \ > ) \ > ) \ > /**/ > #define IS_UNARY_CHECK(_) 1 > > #define IS_UNARY_1 1, NIL > #define IS_UNARY_IS_UNARY_CHECK 0, NIL > > This one is the most complicated. 'expr' must begin with either a unary > parenthesisized expression or not--for example: "(!) 0x0" vs. "<= 0x0". In > particular, it *MUST NOT* be a binary parenthesized expression (such as "(1, > 2)"). If 'expr' begins with "(...)" (where the ellipsis is a placeholder > for a single macro parameter) this macro expands to 1, otherwise it expands > to 0. How it does this is simple. It attempts to expand a unary macro by > placing it in front of 'expr'. If that macro expands, it will yield 1. The > result of this "attempt" to expand a unary macro is then concatenated to > "IS_UNARY_". This concatenation can result in two things: IS_UNARY_1 or > IS_UNARY_IS_UNARY_CHECK--depending on whether or not the test macro > expanded. Both of these are macros that expand to a binary > comma-expression. The reason they don't just expand to 1 and 0 is because > we need to get rid of everything that trails the original 'expr'. For > example, with "(!) 0x0" will end up being "1, 0x0 NIL" at this point, and > "<= 0x0" will end up being "0, <= 0x0 NIL". Altogether the "CAT(...)" in > the above is a single argument that immediately expands into two > arguments--which is the type of parameter that SPLIT accepts. Therefore, we > use SPLIT to get the first of these two results, and ignore the trailing > junk value. > > So, ultimately: > > IS_UNARY( <= 0x00 ) // 0 > IS_UNARY( (!) <= 0x00 ) // 1 > > The last helper we need is a way to get the exclamation point (!) out of the > expression "test" (e.g. "(x) expr" in this example) and a way to extract the > the expression that follows: > > #define OP(a) a, > > SPLIT(0, OP (x) expr) // yields: x > SPLIT(1, OP (x) expr) // yields: expr > > So if we have the necessary extractors for something like this: > > SPLIT(0, (!) <= 0x123) // yields: ! > SPLIT(1, (!) <= 0x123) // yields: <= 0x123 Er, SPLIT(0, OP (!) <= 0x123) // yields: ! SPLIT(1, OP (!) <= 0x123) // yields: <= 0x123 ?? > Next we need two control path macros that represent the "normal" usage and > the "extended" usage: > > #define NORMAL(symbol, test) && (symbol test) > #define EXTENDED(symbol, test) \ > && (BOOST_DETECT_OUTDATED_WORKAROUNDS != 0) \ > && 1 / ( \ > BOOST_PP_SPLIT(0, OP test) (symbol BOOST_PP_SPLIT(1, OP > test)) \ > ? 0 : 1 \ > ) \ > /**/ > > And finally, > > #define BOOST_WORKAROUND(symbol, test) \ > ( \ > ((symbol != 0) IIF(IS_UNARY(test), EXTENDED, NORMAL)((symbol), test) > \ > ) \ > /**/ OK. I'd prefer to factor the initial && out of NORMAL/EXTENDED and into BOOST_WORKAROUND. Any reason why not? > > ----------- final implementation ----------- > > The PP lib already contains most of this stuff and accounts for buggy > preprocessors as well: > > #include <boost/preprocessor/cat.hpp> > #include <boost/preprocessor/control/iif.hpp> > #include <boost/preprocessor/detail/is_unary.hpp> > #include <boost/preprocessor/detail/split.hpp> > > #define BOOST_WORKAROUND_OP(x) x, > > #define BOOST_WORKAROUND_NORMAL(symbol, test) && (symbol test) > > #define BOOST_WORKAROUND_EXTENDED(symbol, test) \ > && (BOOST_DETECT_OUTDATED_WORKAROUNDS != 0) \ > && 1 / ( \ > BOOST_PP_SPLIT(0, BOOST_WORKAROUND_OP test) \ > (symbol BOOST_PP_SPLIT(1, BOOST_WORKAROUND_OP test)) ? 0 : 1 \ > ) \ > /**/ > > #define BOOST_WORKAROUND(symbol, test) \ > ( \ > ((symbol != 0) \ > BOOST_PP_IIF( \ > BOOST_PP_IS_UNARY(test), \ > BOOST_WORKAROUND_EXTENDED, \ > BOOST_WORKAROUND_NORMAL \ > )((symbol), test) \ > ) \ > /**/ > > [Note: The BOOST_PP_IS_UNARY macro will detect the difference between "(x)" > and "x"--regardless of what 'x' is (as long as it doesn't contain a > non-parenthesis-nested comma). This macro doesn't work to its full > potential on all the preprocessors that use the Borland configuration. It > will work fine in this scenario though.] > > [Note: The reason that I'm using "(!)" is that it warnings/errors when the > expression: "symbol op value" is _not_ true. This, of course, is > equivalent to "(~)" but not "(?)". The actual operator has to be used > somehow to enforce the proper syntax, Oh, I'm not so sure how valuable that is. It seems as though people could screw this up by using (+) instead of (!). > "(?)" could be used this way because of the ternary conditional if > you want.] (!) is fine. Someone expressed concern about using the PP lib in the definition of this macro, because, after all, the PP lib itself might want to use it. I presume you're not worried about that, though. -Dave -- David Abrahams [EMAIL PROTECTED] * http://www.boost-consulting.com Boost support, enhancements, training, and commercial distribution _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost