Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2003-04-17 15:21:47


Terje Slettebø wrote:
> Is there some way (using PP-lib, or whatever), to pass a template-id
> with more than one parameter (thus, it contains at least one comma),
> to a macro?
>
> E.g.:
>
> #define TEST(a) test<a> t;
>
> TEST(std::pair<char,int>) // Won't work
>
> Maybe some sort of variation of BOOST_PP_IDENTITY:
>
> #define BOOST_PP_IDENTITY1(a) a
> #define BOOST_PP_IDENTITY2(a,b) a,b
> #define BOOST_PP_IDENTITY3(a,b,c) a,b,c
>
> etc.
>
> TEST(BOOST_PP_IDENTITY2(std::pair<char,int>)) // Now ok

I'll answer all three of these emails at once, since they are about the same
thing. First, the above will cause problems, specifically problems like this:

#define T1(a) T2(a)
#define T2(a) test<a> t;

#define IDENTITY2(a, b) a, b

T1(IDENTITY2(std::pair<char, int>)) // error

I.e. by the time that T2 is invoked, IDENTITY2 has already expanded, which
yields too many arguments to T2. What we need is a general solution and a set
of conventions for passing types. This is difficult without variadics to do
with the preprocessor itself. Specifically, you have to use the core language:

template<class T> struct wrap {
    typedef T type;
};

template<class T> struct wrap<void (T)> {
    typedef T type;
};

#define TYPE(x) wrap<void x>::type

TYPE((int)) // okay
TYPE((std::pair<char, int>)) // okay

However, this has its own problems. Specifically, it is using function types to
store the type--which is inside parentheses and therefore protected from the
preprocessor. The use of function types with cause certain things to error:

TYPE(( void )) // error
TYPE(( int (int, int) )) // function type altered to pointer-to-function
TYPE(( int[2] )) // array altered to pointer

There is only one other language construct that might be of use here:
pointers-to-members. Specifically, it is parenthesized, yet does not undergo
promotion/alteration when used in a parameter list. However, it is intrusive:

template<class> struct wrap;
template<class T> struct wrap<void (T::*)(void)> {
    typedef T type;
};

#define TYPE(x) wrap<void T(void)>::type

TYPE(( std::pair<int, int>::* ))
                          ^^^
Also, this cannot be used for non-class types--which can still have commas in
them:

template<class T, class U> struct typelist {
    typedef T head;
    typedef U tail;
};

TYPE(( typelist<int, nil>::head::* )) // error

So, without variadic macros, there is no "good" solution. Instead, you have to
resort to hacks that require you to know the number of unbound commas in the
type:

#define IN(s, x) (s) x
#define OUT(x) BOOST_PP_TUPLE_REM x

#define M1(type) M2(type)
#define M2(type) OUT(type)

MACRO( IN(2, (std::pair<int, int>)) )

In other words, it is a complete pain to deal with them without variadics.
*With* variadics, however, you can do a little better.

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