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"> - + So, let’s say we have an expression “expr” IntroductionIntroduction. 1

General +class=MsoHyperlink>General idea. 2

Encoding +class=MsoHyperlink>Encoding and decoding a type. 4

ImplementationImplementation. 6

Encoding +class=MsoHyperlink>Encoding and decoding templates. 9

Different +class=MsoHyperlink>Different kinds of template parameters or polymorphism with macros. 10

Handling +class=MsoHyperlink>Template +template parameters. 14

+ +

Handling unused sequence elements. 14

A +class=MsoHyperlink>A word about complexity. 17

 

-

Introduction

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.

-

General idea

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.

-

Encoding and decoding a type

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.

-

Implementation

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;

 

-

Encoding and decoding templates

Let’s consider std::pair class template.

  typedef std::pair<

-

    typename d0::type,

+

    typename d0::type,

    typename d1::type

@@ -1029,7 +1047,7 @@ only have type parameters. more interesting however once we get to consider integral and template template parameters.

-

Different kinds of template parameters or polymorphism with macros

@@ -1094,8 +1112,7 @@ mpl::push_back<

  typedef typename encode_type<

-

    -v0,

+

    v0,

    P0

@@ -1144,7 +1161,20 @@ yes">

 

-

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):

 

@@ -1154,8 +1184,8 @@ yes">

#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))

@@ -1178,7 +1208,7 @@ BOOST_PP_SEQ_ELEM(1, This)

 

-

Now we need a virtual function:

+

Now we need a virtual function call mechanism:

 

@@ -1194,6 +1224,34 @@ for dispatching.

 

+

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:

 

@@ -1264,8 +1322,8 @@ elem)(elem) BOOST_PP_CAT(P, n)

#define REGISTER_TEMPLATE_ENCODE_PARAM(r, data, n, elem)\

-

    VIRTUAL(ENCODE, elem)(elem, -n)

+

    VIRTUAL(ENCODE, +elem)(elem, n)

 

@@ -1285,14 +1343,14 @@ encode_type_impl<V, Name<BOOST_PP_ENUM_PARAMS(Size, P)> >\

  {\

-

    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.

-

Handling unused sequence elements

+

Template template parameters

+ +

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. 

+ +

Handling +unused sequence elements

Let’s revisit our TYPEOF macro implementation.  We left it in the following state:

@@ -1349,20 +1771,19 @@ N>::type::value

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

@@ -1388,20 +1809,19 @@ class=NewCode>mpl::vector0<>, T>::type, N>::type::value

decode_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.

-

A word about complexity

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 no no : - debug debug_compliant + debug release debug_compliant release_compliant ; diff --git a/test/main.cpp b/test/main.cpp index 633bfb4..b8432cc 100755 --- a/test/main.cpp +++ b/test/main.cpp @@ -202,8 +202,8 @@ void lvalue_typeof_test() BOOST_STATIC_ASSERT((boost::is_same::value)); BOOST_STATIC_ASSERT((boost::is_same::value)); BOOST_STATIC_ASSERT((boost::is_same::value)); - //BOOST_STATIC_ASSERT((boost::is_same::value)); - //BOOST_STATIC_ASSERT((boost::is_same::value)); + //BOOST_STATIC_ASSERT((boost::is_same::value)); //msvc + BOOST_STATIC_ASSERT((boost::is_same::value)); } #define BOOST_TYPEOF_TEXT "Noncopyable..."