Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2002-12-14 10:49:30


"Paul Mensonides" <pmenso57_at_[hidden]> writes:

> ----- Original Message -----
> From: "Paul Mensonides" <pmenso57_at_[hidden]>
>> > 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
   dave_at_[hidden] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk