Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2003-01-10 01:28:26


----- Original Message -----
From: "David Abrahams" <dave_at_[hidden]>

> "Paul Mensonides" <pmenso57_at_[hidden]> writes:
>
> > Which could be even shorter yet if we could get away with template
> > template parameters.
>
> We can and do.
>
> How would you imagine it would be spelled with TTP?

You asked for it. ;) Beware, this is the work of a sick mind!
Basically, this implementation reduces the sample to an inline fold of the
form:

operation::step<X>::step<Y>::step<Z>::[ value | type ]

...where 'operation' is the type of operation and where '[ value | type ]'
is either 'value' or 'type'.
Ultimately, we get something similar to this (with the help of a macro to
handle the ::template that is sometimes necessary):

is_present<T>
    item <bool>
    item <int>
    item <double>
    item <std::string>
    ::value

Also, the same underlying implementation is used to build
a completely linearized typelist mechanism:

typedef gen_typelist
    item <char>
    item <signed char>
    item <unsigned char>
    item <short>
    item <unsigned short>
    item <int>
    item <unsigned>
    item <long>
    item <unsigned long>
    ::type integral_types;

Here is the implementation (from scratch):

// identity: yields its template argument as a typedef

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

// map_integral: everyone's favorite plus a typedef
// representing the type of its stored value
// (this is just a hack because I was too lazy to detect it,
// and you can't do it with partial specialization because
// of the dependent second parameter.)

template<class T, T V> struct map_integral : identity<T> {
    static const T value = V;
};

template<class T, T V> const T map_integral<T, V>::value;

// has_member_value: metafunction that returns
// true if its template argument has a nested value
// named 'value'

template<class T> struct has_member_value {
    private:
        template<long> struct helper;
        template<class U> static char check(helper<U::value>*);
        template<class U> static char (& check(...))[2];
    public:
        static const bool value = sizeof(check<T>(0)) == 1;
};

template<class T> const bool has_member_value<T>::value;

namespace detail {

// fold_nil: placeholder for "no previous state"
// it is used by fold_right

struct fold_nil;

// fold_helper: the opposite of fold_nil
// it stores state needed for fold_right to reverse
// its the element order and use fold_left

template<class parent, class element> struct fold_helper { };

// fold_reversal: metafunction that participates in
// building a reversed "list" of elements.

template<class build, class> struct fold_reversal;

// specialization for "no previous state"

template<class build> struct fold_reversal<build, fold_nil> {
    typedef build type;
};

// specialization otherwise

template<class build, class parent, class element>
struct fold_reversal<build, fold_helper<parent, element> > {
    typedef typename fold_reversal<
        typename build::template step<element>, parent
>::type type;
};

// value_if: yields a nested static constant if 'T' has
// a 'value' member. If so, 'T' must also have a member 'type'
// that denotes the 'type' of the static constant.
// This can be deduced, but I didn't feel like bothering to
// do it here. (This is the reason that map_integral above has
// a 'type' typedef.)

template<class T, bool = has_member_value<T>::value> struct value_if { };
template<class T> struct value_if<T, true>
    : map_integral<typename T::type, T::value> { };

} // detail

// the fold_left and fold_right meta-something-or-others
// perform an unraveled fold in a strange way:
// 'fold_left<op, state, transform>::step<X>::step<Y>::step<Z>::type'
//
// effectively yields:
// op(transform(Z), op(transform(Y), op(transform(X), state)))
//
// vice versa with fold_right.
//
// These 'metafunctions' contain a nested template 'step'
// which represents a fold step. These nested templates
// inject there own name via inheritance of their parent
// classes. This allows the syntax:
fold_left<...>::step<int>::step<double>
// etc.. I'm not sure if this is actually legal, but it works on Comeau
C++.
// If it isn't legal, I'd need some kind of jumper typedef that identifies
the
// base class, which would alter the final syntax to:
// fold_left<...>::step<int>::base::step<double> etc.
// ...which is trivial handled by local sugar macros anyway.
//
// the 'op' parameter is the fold operation, which is instantiated
// with the current element and the current state.
//
// the 'state' parameter is the initial state of the accumulation
//
// the 'transform' parameter is a simple way to modify each element
// however you like. The 'is_present' implementation uses this parameter
// with a partially bound 'is_same' metafunction, and uses a logical-or
// metafunction as the 'op' parameter. The 'identity' metafunction is
// the default and simply does nothing.

// fold_left

template<
    template<class _element, class _state> class op,
    class state,
    template<class> class transform = identity
>
struct fold_left {
    template<
        class element,
        class apply = typename op<
            typename transform<element>::type, state
>::type
>
    struct step :
        fold_left<op, apply, transform>,
        detail::value_if<apply>,
        identity<apply> { };
};

// fold_right

template<
    template<class _element, class _state> class op,
    class state,
    template<class> class transform = identity,
    class previous = detail::fold_nil
>
struct fold_right {
    template<class element> struct step
        : fold_right<
            op, state, transform,
            detail::fold_helper<previous, element>
> {
        typedef typename detail::fold_reversal<
            typename fold_left<op, state, transform>::template
step<element>,
            previous
>::type::type type;
    };
};

// ----- unraveled typelist sample ----- //

// nil

struct nil;

// typelist

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

// typelist_op: fold operation to build a typelist

template<class T, class U> struct typelist_op {
    typedef typelist<T, U> type;
};

// gen_typelist: encapsulation type.
// this type is equivalent in functionality to
// its base class 'fold_right<typelist_op, nil>'

struct gen_typelist : fold_right<typelist_op, nil> { };

// ----- is_same / logical-or sample ----- //

// is_same

template<class T, class U> struct is_same
    : map_integral<bool, false> { };

template<class T> struct is_same<T, T>
    : map_integral<bool, true> { };

// logical_or

template<class X, class Y> struct logical_or {
    typedef map_integral<bool, X::value || Y::value> type;
};

// bind_1st: binds the first argument of a binary metafunction
// yielding a unary template member 'id'

template<
    template<class, class> class binary,
    class T
>
struct bind_1st {
    template<class U> struct id : binary<T, U> { };
};

// is_equiv: a 'typed' version of 'is_same'
// (it returns a type rather than a value)

template<class T, class U> struct is_equiv {
    typedef is_same<T, U> type;
};

// is_present: ultimately yields
// 0 || is_same<T, X> || is_same<T, Y> || is_same<T, Z> ...
// for however many elements are necessary to test against

template<class T> struct is_present
    : fold_left<
        logical_or,
        map_integral<bool, false>,
        bind_1st<is_equiv, T>::template id
> { };

/* -----------------------------

At this point, a typelist can be defined like this:

gen_typelist
    ::step<int>
    ::step<double>
    ::step<char>
    ::type

Note that this is a throughly unraveled typelist
definition (without macros or massive code replication)!

The presence of a certain element can be detected
like this:

is_present<T>
    ::template step<int>
    ::template step<double>
    ::template step<char>
    ::value

Note the use of 'template' because they are dependent
templates. Syntactic sugar can reduce this to:

#define item ::template step

is_present<T>
    item <int>
    item <double>
    item <char>
    ::value

#undef item

----------------------------- */

#include <iostream>
#include <typeinfo>

// sample template function

template<class T> void is_character_type(void) {

    // output whether 'T' is
    // one of character types...

    // sugar macro
    #define item ::template step

    std::cout
        << is_present<T>
            item <char>
            item <signed char>
            item <unsigned char>
            item <wchar_t>
            ::value
        << &std::endl;

    // undefine local macro
    #undef item

    return;
}

int main() {

    // call sample function a few times...
    is_character_type<char>();
    is_character_type<int>();

    // sugar macro
    #define item ::step

    // this is likely going to be
    // a messy output, but once the
    // typedefs are all removed,
    // it is a typelist

    std::cout
        << typeid(
            gen_typelist
                item <char>
                item <int>
                item <unsigned>
            ::type
        ).name() << &std::endl;

    // undefine local macro
    #undef item

    return 0;
}

Of course, good luck trying to compile it (or likely understand it). I only
tried it on Comeau C++, but this stuff is pushing it. ;)

Paul Mensonides


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