diff --git a/doc/compliant/typeof_internals.htm b/doc/compliant/typeof_internals.htm index 4562b20..19993f1 100755 --- a/doc/compliant/typeof_internals.htm +++ b/doc/compliant/typeof_internals.htm @@ -7,22 +7,22 @@ xmlns="http://www.w3.org/TR/REC-html40"> - +
General +class=MsoHyperlink>General idea
Encoding +class=MsoHyperlink>Encoding and decoding a type
Encoding +class=MsoHyperlink>Encoding and decoding templates
Different +class=MsoHyperlink>Different kinds of template parameters or polymorphism with macros
Handling +class=MsoHyperlink>Template +template parameters
+ +Handling unused sequence elements
A +class=MsoHyperlink>A word about complexity
This document describes the internals of style="mso-spacerun: yes"> It is related to so called “compliant” implementation – one that uses partial template specializations to encode and decode types, and is not to be confused with the other two implementations that -currently exist (or will soon exist) under the umbrella of the proposed +currently exist (or once existed) under the umbrella of the proposed BOOST_TYPEOF macro – Peder Holt’s “vintage” implementation, that trades partial template specialization for function overloading and compile time constants, as well as recently invented by Igor Chesnokov MSVC-specific typeof trick.
@@ -390,17 +406,18 @@ they are found and reported.
It has to be stressed that the idea of breaking a type into multiple -compile-time integers by using partial template specializations is not new, and -belongs, to the best of my knowledge, to Steve Dewhurst, who described it in -his famous CUJ article “A BIT-Wise Typeof Operator”. The idea of applying MPL to this problem belongs to David -Abrahams, see It has to be stressed that the idea of breaking a type into +multiple compile-time integers by using partial template specializations is not +new, and belongs, to the best of my knowledge, to Steve Dewhurst, who +described it in his famous CUJ article “A BIT-Wise Typeof Operator”. The idea of applying MPL to this problem +belongs to David Abrahams, see http://thread.gmane.org/gmane.comp.lib.boost.devel/76208.
-
The main thing that distinguishes this implementation from others available is the ease of definition of new specializations for @@ -415,16 +432,22 @@ class Tpl>
REGISTER_TEMPLATE(foo, (class)(int)(TEMPLATE((class)(unsigned -int)))) /* now foo can be handled by TYPEOF */
+REGISTER_TEMPLATE(foo, +(class)(int)(TEMPLATE((class)(unsigned int)))) /* now foo can be handled +by TYPEOF */
The implementation of this REGISTER_TEMPLATE macro, as well as many other useful specializations (for functions, arrays, etc.), has become -possible because of extensive usage of the Boost Preprocessor Library.
+possible because of extensive usage of the Boost Preprocessor Library. -Let’s say we have an expression “expr”.
decode_type<mpl::vector<\
-mpl::int_<sizeof(at(expr, mpl::int_<0>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<0>()))>,\
-mpl::int_<sizeof(at(expr, mpl::int_<1>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<1>()))>,\
-mpl::int_<sizeof(at(expr, mpl::int_<2>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<2>()))>,\
-…\
+…\
-mpl::int_<sizeof(at(expr, mpl::int_<N>()))>\
+mpl::int_<sizeof(at(expr, +mpl::int_<N>()))>\
> >::type
@@ -587,7 +609,7 @@ sequence.Let’s now explore these three issues in more detail.
-Let’s consider the following type:
@@ -604,8 +626,8 @@ either a type or a template or a modifier of the original type:
-+-- pointer -- int
++ +-- pointer -- int
pointer -- const -- std::pair --+
@@ -618,17 +640,13 @@ either a type or a template or a modifier of the original type:
pointer 1
+pointer 1
-const 2
+const 2
-std::pair 3
+std::pair 3
-int 4
+int 4
std::string 5
@@ -768,7 +786,7 @@ parameters: of integers, and then decoded back, let’s now see how this all can be implemented. -The described type encoding can be implemented with partial @@ -869,11 +887,12 @@ V> style="mso-spacerun: yes"> \
V, \
+style="mso-spacerun: yes"> \mpl::int_<ID> - \
+class=NewCode>ID> \> @@ -890,8 +909,8 @@ decode_type_impl<mpl::int_<ID>, Iter> \
{ \
+style="mso-spacerun: yes"> +\ typedef Name type;
Let’s consider std::pair class template.
typedef std::pair< typename d0::type, typename d0::type, typename d1::typeEncoding and decoding templates
Different kinds of template parameters or polymorphism with
macros
@@ -1094,8 +1112,7 @@ mpl::push_back<
typedef typename encode_type<
-v0,
v0,
P0
Let’s now define “virtual functions”:
+The first “class”, TYPE_PARAM, doesn’t define any +properties. The only data contained +inside the “object” is the class information, which is used for dispatching, +and can be thought of as an analogue of the vptr.
+ +
The second class, INTEGRAL_PARAM, defines the only property, +which is an integral type of the parameter.
+ +
Let’s now implement “virtual functions” (note how their +names are formed – this is used in the virtual function call mechanism):
#define TYPE_PARAM_ENCODE(This, n)\
typedef
-typename encode_type<v ## n, P ## n>::type\
+typedef typename encode_type<v ## n, P ## n>::type\
BOOST_PP_CAT(v, BOOST_PP_INC(n))
Now we need a virtual function:
+Now we need a virtual function call mechanism:
Now the following:
+ +
VIRTUAL(ENCODE, obj)(obj, 2)
+ +
will expand into either
+ +
TYPE_PARAM_ENCODE(obj, 2)
+ +
or
+ +
INTEGRAL_PARAM_ENCODE(obj, 2),
+ +
depending of what the “class” of obj is.
+ +
Before we can finish conversion of our encode_type specialization, we need to transform
@@ -1250,8 +1308,8 @@ TYPE_PARAM
Assuming this transformation is done with the macro called TRANSFORM_PARAMS, -we can define our encoding specialization like this:
+Assuming this transformation is done with the macro called +TRANSFORM_PARAMS, we can define our encoding specialization like this:
#define REGISTER_TEMPLATE_ENCODE_PARAM(r, data, n, elem)\
-VIRTUAL(ENCODE, elem)(elem, -n)
+VIRTUAL(ENCODE, +elem)(elem, n)
{\
-typedef typename mpl::push_back<V, -mpl::int_<ID> >::type V0;\
+typedef typename +mpl::push_back<V, mpl::int_<ID> >::type V0;\
BOOST_PP_SEQ_FOR_EACH_I(REGISTER_TEMPLATE_ENCODE_PARAM, ~, Params)\
-typedef -BOOST_PP_CAT(V, Size) type;\
+typedef BOOST_PP_CAT(V, +Size) type;\
};\
@@ -1321,13 +1379,377 @@ meaning)
It’s worth noting here that we also support the third -template parameter type, template template parameters. With three different types, and half a dozen -“virtual functions”, such polymorphic approach really pays off.
+The library also supports the third template parameter type, +template template parameters. Let’s now +take a look at how it’s done.
-The good news is +that templates already have unique integer Ids, which are assigned to them +during the registration. Placing this +Id into the encoding sequence, and then restoring the template from this Id is +all that needs to be done.
+ +
Unfortunately, +templates, although strange it might sound, are not the first-class citizens in +template metaprogramming. True, a +template can be passed into a metafunction, as a template template parameter, +but it turns out to be not very convenient. +Also, a template can never be returned as a result of a metafunction.
+ +
The Typeof library +solves this problem by converting templates into types. It does this by instantiating templates with +well-known dummy types and values – int for type parameters, and zero for +integrals. Such type is used to move +the template around, and later the template gets extracted using partial +template specialization (this approach is somewhat similar to one used by MPL +to convert metafunctions into metafunction classes). For example, consider the std::pair class template. Its type representation would be +std::pair<int, int>. The +following code might then be used to extract the template:
+ +
template<class T>
+ +struct extract; // not +defined
+ +
template<template<class, +class> class Tpl, class T1, class T2>
+ +struct extract<Tpl<T1, +T2> >
+ +{
+ +typedef Tpl<T1*, T2*> type; /* use Tpl in any way you want +*/
+ +};
+ +
Armed with this +approach, let’s go back to our template template parameters.
+ +
Let’s declare a +metafunction to encode a template:
+ +
template<class V, +class T>
+ +struct encode_template; +// not defined
+ +
Note that it +accepts a type rather than a template. +This type is the instantiation of the template that was discussed just a +moment ago. Hadn’t we used this +approach, it wouldn’t have been possible to declare this:
+ +
template<class V, +template<???> class Tpl>
+ +struct encode_template; +// not defined
+ +
Now let us provide +the specialization for std::pair:
+ +
template<class V, +class P0, class P1>
+ +struct encode_template +<V, std::pair<P0, P1> >
+ +: mpl::push_back<V, mpl::int_<3> +> /* we still use 3 as an Id of std::pair */
+ +{};
+ +
Similarly, let’s +implement decoding:
+ +
template<class T, +class Iter>
+ +struct +decode_template_impl; // not defined
+ +
template<class +Iter>
+ +struct decode_template : +decode_template_impl<
+ +typename mpl::deref<Iter>::type,
+ +typename mpl::next<Iter>::type
+ +>
+ +{};
+ +
template<class +Iter>
+ +struct +decode_template_impl<mpl::int_<3>, Iter>
+ +{
+ +typedef int P0;
+ +typedef std::pair<int, int> type;
+ +typedef Iter iter;
+ +};
+ +
As you can see, the +iterator is de-referenced to determine the Id, which is used to match against a +template specialization. The template +specialization for std::pair’s Id returns the std::pair class template by +instantiating it with two ints.
+ +
The encode_template +and decode_template_impl specializations are added to REGISTER_TEMPLATE macro, +thus making it possible to use registered templates as template template +parameters.
+ +
We now can encode +and decode templates. Recall that +earlier we talked about template encoding actually meaning encoding a type, +which is an instantiation of a template. +Let’s now return to the template encoding in this sense, and see how we +can add template template parameters support.
+ +
Let’s assume we +have the following template definition:
+ +
template<class T, +template <class> class Tpl>
+ +struct B
+ +{};
+ +
If its ID happens +to be 15, its encoding specialization will look like following:
+ +
template<class V, class P0, template<class> class +P1>
+ +struct encode_type<V, +B<P0, P1> >
+ +{
+ +typedef typename mpl::push_back<V, +mpl::int_<15> >::type V0;
+ + typedef typename encode_type<V0,
+P0>::type V1;
typedef typename encode_template<V1,
+P1<int> >::type V2;
typedef V2 type;
+ +};
+ ++ +
The decoding +specialization will be:
+ +
template<class +Iter>
+ +struct decode_type_impl<mpl::int_<15>, +Iter>
+ +{
+ +typedef Iter iter0;
+ +
typedef decode_type<iter0> d0;
typedef typename d0::type P0;
typedef typename d0::iter iter1;
typedef decode_template<iter1> d1;
typedef typename d1::type P1;
typedef typename d1::iter iter2;
typedef typename B<P0, +???>::type type; /* need to do something else */ +
+ +typedef iter2 iter;
+ +};
+ +
The only missing +piece is how to instantiate B. Its +second parameter is a template, but we have a type (which is an instantiation +of the needed template with dummy parameters).
+ +
As we discussed in +the beginning of this section, we have to use partial template specialization +to extract the template. The commented +line in the above code can be replaced with the following code:
+ +
. . .
+ +template<class T0, class T1>
+ +struct decode_params;
+ +
template<class +T0, template <class> class T1>
+ +struct decode_params<T0, T1<int> >
+ +{
+ +typedef B<T0, T1> type;
+ +};
+ +typedef typename decode_params<P0, +P1>::type type;
+ +. . .
+ +
Note how +decode_params extracts the template from its second parameter, and uses it when +defining the resulting type. The first +parameter, on the other hand, is a type, and therefore passed through +decode_params as is.
+ +
The code discussed +in this section is generated as a result of the expansion of the +REGISTER_TEMPLATE macro. The code +specific to template template parameters (green) is handled by the third +polymorphic “class” – TEMPLATE_PARAM, whose only property is a sequence that +describes parameters of the template:
+ +
#define
+TEMPLATE_PARAM(Params) (TEMPLATE_PARAM)(Params)
With three +“classes” and half a dozen “virtual functions” such plymorphic approach really +pays off.
+ +Let’s revisit our TYPEOF macro implementation. We left it in the following state:
@@ -1349,20 +1771,19 @@ N>::type::valuedecode_type<mpl::vector<\
-mpl::int_<sizeof(at(expr, mpl::int_<0>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<0>()))>,\
-mpl::int_<sizeof(at(expr, mpl::int_<1>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<1>()))>,\
-mpl::int_<sizeof(at(expr, mpl::int_<2>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<2>()))>,\
-…\
+…\
-mpl::int_<sizeof(at(expr, mpl::int_<N>()))>\
+mpl::int_<sizeof(at(expr, +mpl::int_<N>()))>\
> >::type
@@ -1388,20 +1809,19 @@ class=NewCode>mpl::vector0<>, T>::type, N>::type::valuedecode_type<mpl::begin<mpl::vector<\
-mpl::int_<sizeof(at(expr, mpl::int_<0>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<0>()))>,\
-mpl::int_<sizeof(at(expr, mpl::int_<1>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<1>()))>,\
-mpl::int_<sizeof(at(expr, mpl::int_<2>()))>,\
+mpl::int_<sizeof(at(expr, +mpl::int_<2>()))>,\
-…\
+…\
-mpl::int_<sizeof(at(expr, mpl::int_<N>()))>\
+mpl::int_<sizeof(at(expr, +mpl::int_<N>()))>\
>::type>::type
@@ -1500,8 +1920,8 @@ T>::type>::type::value)>()))>,\
--mpl::int_<sizeof(at(expr, mpl::int_<(\
+mpl::int_<sizeof(at(expr, +mpl::int_<(\
1 < sizeof(size(expr)) ? 1 : 0\
@@ -1534,11 +1954,10 @@ T>::type>::type::value
It’s now trivial for anybody familiar with the Boost -Preprocessor Library to re-write this nicely, so let’s omit this. You can always see the result at -boost/typeof/compliant/typeof_impl.hpp.
+Preprocessor Library to re-write this nicely, so let’s omit this. You can always see the result at boost/typeof/compliant/typeof_impl.hpp. -Looking at the resulting TYPEOF macro, it may seem that the
diff --git a/test/Jamfile b/test/Jamfile
index b8b6995..7dd36c1 100755
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -19,5 +19,5 @@ exe typeof