Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2005-02-18 17:17:38


> -----Original Message-----
> From: boost-bounces_at_[hidden]
> [mailto:boost-bounces_at_[hidden]] On Behalf Of Arkadiy Vertleyb

> > This will work on most preprocessors, but not on those that use the
> Borland
> > configuration. Thanks to Daniel James, I do have a workaround for
> > that
> bug for
> > Borland in particular, but I don't have verification that
> it works on
> other
> > preprocessors that use that configuration (so it has to
> wait until I
> > can separate the implementations).
>
> This definitely has a benefit of not having to provide
> multiple defines (but rather rely on ones in the
> Boost.Preprocessor). However, it uses some internal feature,
> IS_UNARY, which I guess is not a huge drawback since the
> suggestion comes from the author :-)

It would be a public interface if I could make it work across all supported
preprocessors. That might be the case now, but I don't have access to either
Sun's or IBM's preprocessors--both of which use the Borland configuration. The
predicate definition itself is pretty straightforward if you take away the
workarounds:

#define CAT(a, b) PRIMITIVE_CAT(a, b)
#define PRIMITIVE_CAT(a, b) a ## b

#define SPLIT(i, im) PRIMITIVE_CAT(SPLIT_, i)(im)
#define SPLIT_0(a, b) a
#define SPLIT_1(a, b) b

#define IS_NULLARY(x) \
    SPLIT(0, CAT(IS_NULLARY_R_, IS_NULLARY_C x)) \
    /**/
#define IS_NULLARY_C() 1
#define IS_NULLARY_R_1 1, ~
#define IS_NULLARY_R_IS_NULLARY_C 0, ~

#define IS_UNARY(x) IS_NULLARY(IS_UNARY_C x)
#define IS_UNARY_C(a) ()

#define IS_BINARY(x) IS_NULLARY(IS_BINARY_C x)
#define IS_BINARY_C(a, b) ()

#define IS_TERNARY(x) IS_NULLARY(IS_TERNARY_C x)
#define IS_TERNARY_C(a, b, c) ()

// etc.

The library already includes these macros complete with a mess of workarounds.
However, the library doesn't yet contain a Borland compatible version--credits
for this one go to Daniel James:

#include <boost/preprocessor/detail/split.hpp>
#include <boost/preprocessor/punctuation/comma.hpp>
#include <boost/preprocessor/punctuation/paren.hpp>
#include <boost/preprocessor/tuple/eat.hpp>

#define IS_NULLARY(x) \
     IS_NULLARY_2(BOOST_PP_SPLIT(1, IS_NULLARY_C x BOOST_PP_COMMA() 0)) \
     /**/
#define IS_NULLARY_2(x) x
#define IS_NULLARY_C() \
     ~, 1 BOOST_PP_RPAREN() \
     BOOST_PP_TUPLE_EAT(2) BOOST_PP_LPAREN() ~ \
     /**/

#define IS_UNARY(x) IS_NULLARY(IS_UNARY_C x)
#define IS_UNARY_C(a) ()

This should work on Borland's preprocessor, but I can't speak for Sun or IBM.

> The portability issue
> is somewhat of concern -- I would not like to compromise it
> because of this nicety feature...
>
> Do you think my other suggestion might be more portable?
>
> #define TRANSLATE(x) BOOST_PP_SEQ_CAT(\
> (STEP3)\
> (BOOST_PP_CAT(STEP2, BOOST_PP_EXPAND(STEP1 x)))
                                           ^^^^^^^

The main thing that the Borland configuration works around is this situation.
If 'x' is not parenthetic (i.e. the argument list to STEP1), the Borland
preprocessor "merges" it similar to token-pasting, but (if I remember correctly)
it does so without the possibility of constructing a macro name. E.g.

#define A(args) B args
#define B(a, b) a + b

A( 1 ) -> B1
A( (x, y) ) -> x + y

Daniel's version above, effectively just doesn't care that it is doing that or
not. It works by shifting the parameters when the "attempt-to-expand" macro
actually does invoke.

I think that you're best course of action is to use the library's IS_UNARY, but
define you're own IS_UNARY for Borland (as above). If you're concerned with
supporting IBM and Sun, then you can fall back on macro replication and
undefined behavior (concatenation to left-parethesis) because you're dealing
with a finite set of possible inputs (i.e. numbers). I don't think that it is
possible to use a single implementation that will work on all preprocessors.
Note that kind of stuff tends to occur whenever you get into this kind of
low-level detection stuff. For example, the automatic recursion in the library
does a similar thing. For most preprocessor's that merely means attempting to
invoke a macro (where success will produce a particular detectable output).
When the particular macro doesn't expand, it is detected, so the mechanism moves
on to the next one (technically speaking, it doesn't move on to the "next" one,
it does a binary search). The Borland configuration, OTOH, cannot do this
because (at the time) I didn't have an IS_NULLARY (or similar) that I could use
to test whether a macro expanded. Without it, instead of testing for whether
the result is X or not X, I have to test whether the result is X or Y or Z (and
so on). IOW, I have to have literally *thousands* of extra macros just for that
configuration. Now, I do have an IS_NULLARY implementation that will work on
Borland, but other preprocessors (namely IBM and Sun) are using that
configuration. Having those predicates is so useful that it is worth having an
additional configuration (and all that that implies) for Borland. I haven't
done that yet because I'm looking at quite a few more drastic changes (like
unifying library state) as well as "preparing" the library for variadics (etc.).

Regards,
Paul Mensonides


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