----- Original Message ----- From: "David Abrahams" <[EMAIL PROTECTED]>
> "Paul Mensonides" <[EMAIL PROTECTED]> 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 _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost