|
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