Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2003-06-26 19:40:32


> -----Original Message-----
> From: boost-bounces_at_[hidden]

> > it doesn't directly support variadics as a data structure. The
> > reasons for this are simple: 1) Variadic data can contain
> open commas
> > and that interferes with parameter lists if you need to pass around
> > more than one structure or if you need to use the variadic argument
> > for something else.
>
> Very true, but in the common case you just have 1 variadic
> list. In the remaining cases you must require the extra
> parenthisis. For the implementation I posted, that only
> occurs within internal library calls, and the extra
> parenthisis are not required in the interface.

The common case is variadic auxiliary data as in FILTER, TRANSFORM,
REPEAT, FOR_EACH, etc.. The other common case is variadic state as in
constructs such as FOR, WHILE, FOLD_LEFT, FOLD_RIGHT, etc.. If you use
the variadic parameter to store your data type that you are processing,
that means that you cannot have variadic auxiliary data or variadic
state. I'll give you a small example. If I was to implement addition:

// WHILE(pred, op, state)

#define ADD(x, y) \
        TUPLE_ELEM( \
                2, 0, \
                WHILE(ADD_P, ADD_O, (x, y)) \
        ) \
        /**/
#define ADD_P(d, xy) TUPLE_ELEM(2, 1, xy)
#define ADD_O(d, xy) \
        ( INC(TUPLE_ELEM(2, 0, xy)), DEC(TUPLE_ELEM(2, 1, xy)) )
        /**/

// WHILE(pred, op, ...)

#define ADD(x, y) \
        VARIADIC_ELEM( \
                0, WHILE(ADD_P, ADD_O, x, y) \
     ) \
        /**/
#define ADD_P(d, x, y) y
#define ADD_O(d, x, y) INC(x), DEC(y)

That's an example of how much cleaner and more efficient it is with
variadic state. The same applies to auxiliary data also:

#define BETWEEN(list, x, y) \
        FILTER(BETWEEN_P, list, x, y) \
        /**/
#define BETWEEN_P(d, elem, x, y) \
        BITAND( LESS_EQUAL(x, elem), LESS_EQUAL(elem, y) ) \
        /**/

The point is this: it is *far* more useful to allow variadic auxiliary
data or state than it is half-support a data type that consistents of
pure variadic data.

The framework is there in Chaos to define such things fairly easily.
They just won't match anything else in the library. There is no such
framework in the pp-lib, however.

> It seems to me that "empty" is a close enough approximation
> to "end of the parameters," in practice. Here is an
> implementation of IS_EMPTY that I don't think relies on any
> undefined behavior. With two caveats:

Empty is a valid element. It is not an approximation of "end of
parameters". So, first, you can't use that a rogue value. Instead, you
have to use something much more pathological.

> 1) it cannot differenciate between foo(a,) and foo(a) where foo is:
>
> #define foo(x, ...) BOOST_PP_VA_IS_EMPTY(__VA_ARGS__)

Invoking 'foo' without two arguments is undefined. You can't do this:

foo(x) // error

gcc allows this, but it is invalid. You *must* invoke it with the
comma:

foo(x,)

>
> 2) it causes most compilers to spit out a warning if the
> final parameter ends in the name of a function-like macro. e.g.:
>
> foo(a, BOOST_PP_CAT) /* causes a warning message */

This is the point. You cannot pass certain types of data. There is *no
way* to implement IS_EMPTY generally. Trust me, I've implemented it a
hundred different ways. You must always restrict some sort of input.
I.e. no function-like macros, no operators, etc.. Period. Likewise,
you cannot detect the size of a variadic parameter without restricting
the size of the variadic parameter. To further emphasize my point:
Wave is an extremely strict preprocessor that won't preprocess your
code. The code is not legal. If you want to detect whether or not
you're "at-the-end", limit the size of the variadic parameters to (for
example) 10 and do this:

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

#define SPLIT(i, ...) PRIMITIVE_CAT(SPLIT_, i)(__VA_ARGS__)
#define SPLIT_0(a, ...) a
#define SPLIT_1(a, ...) __VA_ARGS__

#define IS_VARIADIC(...) \
    SPLIT(0, CAT(IS_VARIADIC_R_, IS_VARIADIC_C __VA_ARGS__)) \
    /**/
#define IS_VARIADIC_C(...) 1
#define IS_VARIADIC_R_1 1,
#define IS_VARIADIC_R_IS_VARIADIC_C 0,

#define END (,,,,,,,,,,())

#define IS_END(...) IS_END_I(__VA_ARGS__,)
#define IS_END_I(x, ...) \
    IS_VARIADIC(IS_END_II x,) \
    /**/
#define IS_END_II(a, b, c, d, e, f, g, h, i, j, ...) __VA_ARGS__

IS_END( a, b, c, d, END )
IS_END( END )

You can expand the END value to hundreds of commas and change IS_END_II
likewise. That, or similar pathological input, is the only way that you
can do this without undefined behavior in the absence of well-defined
token-pasting.

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