"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

Reply via email to