| [/ |
| (C) Copyright Edward Diener 2011,2012 |
| Distributed under the Boost Software License, Version 1.0. |
| (See accompanying file LICENSE_1_0.txt or copy at |
| http://www.boost.org/LICENSE_1_0.txt). |
| ] |
| |
| [section:tti_nested_type Nested Types] |
| |
| Besides the functionality of the TTI library which queries whether some inner element |
| of a given name within a type exists, the library also includes functionality for |
| generating a nested type if it exists, else a marker type if it does not exist. By |
| marker type is meant a type either internally created by the library, with no functionality, |
| or designated by the end-user to represent the same idea. |
| |
| First I will explain the syntax and use of this functionality and then the reason it exists in |
| the library. |
| |
| The functionality is a metafunction created by the macro [macroref BOOST_TTI_MEMBER_TYPE]. |
| The macro takes a single parameter, which is the name of a nested type. We will call this our |
| 'named type'. The macro generates a metafunction called `member_type_'named_type'` which, passed |
| an enclosing type, returns the named type if it exists, else a marker type if it does not. |
| |
| As with our other macros we can use the alternative form of the macro |
| [macroref BOOST_TTI_TRAIT_MEMBER_TYPE] to pass first the name of the metafunction |
| to be generated and then the name of the 'named type'. After that the functionality |
| of our resulting metafunction is exactly the same. |
| |
| Its general explanation is given as: |
| |
| [table:tbmacronested TTI Nested Type Macro Metafunction |
| [ |
| [Inner Element] |
| [Macro] |
| [Template] |
| [Specific Header File] |
| ] |
| [ |
| [Type] |
| [ |
| [macroref BOOST_TTI_MEMBER_TYPE](name) |
| ] |
| [ |
| `member_type_'name'` |
| |
| class T = enclosing type |
| |
| class U = (optional) marker type |
| |
| returns = the type of 'name' if it exists, else a marker type, as the typedef 'type'. |
| |
| The invoked metafunction also holds the marker type as the typedef 'boost_tti_marker_type'. |
| This is done for convenience so that the marker type does not have to be remembered. |
| ] |
| [[headerref boost/tti/member_type.hpp `member_type.hpp`]] |
| ] |
| ] |
| |
| The marker type is purely optional. If not specified a type internal to the TTI library, |
| which has no functionality, is used. Unless there is a specific reason for the end-user |
| to provide his own marker type, he should let the TTI library use its own internal |
| marker type. |
| |
| A simple example of this functionality would be: |
| |
| #include <boost/tti/member_type.hpp> |
| |
| BOOST_TTI_MEMBER_TYPE(ANamedType) |
| |
| typedef typename member_type_ANamedType<EnclosingType>::type AType; |
| |
| If type 'ANamedType' is a nested type of 'EnclosingType' then |
| AType is the same type as 'ANamedType', otherwise AType is a |
| marker type internal to the TTI library. |
| |
| Now that we have explained the syntax of BOOST_TTI_MEMBER_TYPE |
| we can now answer the question of why this functionality to create |
| a 'type' exists when looking for a nested type of an enclosing type. |
| |
| [heading The problem] |
| |
| The metafunctions generated by the TTI macros all work with various types, whether in specifying |
| an enclosing type or in specifying the type of some inner element, which may also involve |
| types in the signature of that element, such as a parameter or return type of a function. |
| The C++ notation for a nested type, given an enclosing type 'T' and an inner type 'InnerType', |
| is 'T::InnerType'. If either the enclosing type 'T' does not exist, or the inner type 'InnerType' |
| does not exist within 'T', the expression 'T::InnerType' will give a compiler error if we attempt |
| to use it in our template instantiation of one of TTI's macro metafunctions. |
| |
| This is a problem if we want to be able to introspect for the existence of inner elements |
| to an enclosing type without producing compiler errors. Of course if we absolutely know what |
| types we have and that a nested type exists, and these declarations are within our scope, we can |
| always use an expression like 'T::InnerType' without compiler error. But this is often not the |
| case when doing template programming since the type being passed to us at compile-time in a class |
| or function template is chosen at instantiation time and is created by the user of a template. |
| |
| One solution to this is afforded by the library itself. Given an enclosing type 'T' |
| which we know must exist, either because it is a top-level type we know about or |
| it is passed to us in some template as a 'class T' or 'typename T', and given an inner type |
| named 'InnerType' whose existence we would like ascertain, we can use a `BOOST_TTI_HAS_TYPE(InnerType)` |
| macro and it's related `has_type_InnerType` metafunction to determine if the nested type 'InnerType' |
| exists. This solution is perfectly valid, and in conjunction with Boost MPL's selection metafunctions, |
| we can do compile-time selection to generate the correct template code. |
| |
| However this does not scale that well syntactically if we need to drill down further |
| from a top-level enclosing type to a deeply nested type, or even to look for some deeply nested |
| type's inner elements. We are going to be generating a great deal of `boost::mpl::if_` and/or |
| `boost::mpl::eval_if` type selection statements to get to some final condition where we know we |
| can generate the compile-time code which we want. |
| |
| [heading The solution] |
| |
| The solution given by BOOST_TTI_MEMBER_TYPE is that we can create a type as the return |
| from our metafunction, which is the same type as a nested type if it exists or some other |
| marker type if it does not, and then work with that returned type without producing a |
| compiler error. If we had to use the 'T::InnerType' syntax to specify our type, where 'T' represents |
| out enclosing type and 'InnerType' our nested type, and there was no nested type 'InnerType' within the |
| enclosing type 'T, the compiler would give us an error immediately. |
| |
| By using BOOST_TTI_MEMBER_TYPE we have a type to work with even when such a type |
| really does not exist. Naturally if the type does not exist, the type which we |
| have to work with, being a marker type, will generally not fulfill any other further functionality |
| we want from it. This is good and will normally produce the correct results in further uses of the type |
| when doing metafunction programming. Occasionally the TTI produced marker type, when our nested |
| type does not exist, is not sufficient for further metafunction programming. In that rare case the |
| end-user can produce his own marker type to be used if the nested type does not exist. In any case, |
| whether the nested type exists, whether the TTI default supplied marker type is used, or whether |
| an end-user marker type is used, template metaprogramming can continue without a compilation |
| problem. Furthermore this scales better than having to constant check for nested type existence |
| via BOOST_TTI_HAS_TYPE in complicated template metaprogramming code. |
| |
| [heading Checking if the member type exists] |
| |
| Once we use BOOST_TTI_MEMBER_TYPE to generate a nested type if it exists we will normally |
| use that type in further metafunction programming. Occasionally, given the type we generate, |
| we will want to ask if the type is really our nested type or the marker type instead. Essentially |
| we are asking if the type generated is the marker type or not. If it is the marker type, then |
| the type generated is not the nested type we had hoped for. If it is not the marker type, then |
| the type generated is the nested type we had hoped for. This is easy enough to do for the template |
| metaprogrammer but TTI makes it easier by providing either of two metafunctions to do this calculation. |
| These two metafunctions are 'boost::tti::valid_member_type' and 'boost::tti::valid_member_metafunction': |
| |
| [table:existtbmacronested TTI Nested Type Macro Metafunction Existence |
| [ |
| [Inner Element] |
| [Macro] |
| [Template] |
| [Specific Header File] |
| ] |
| [ |
| [Type] |
| [None] |
| [ |
| [classref boost::tti::valid_member_type] |
| |
| class T = a type |
| |
| class U = (optional) marker type |
| |
| returns = true if the type exists, false if it does not. |
| 'Existence' is determined by whether the type |
| does not equal the marker type of BOOST_TTI_MEMBER_TYPE. |
| ] |
| [[headerref boost/tti/member_type.hpp `member_type.hpp`]] |
| ] |
| [ |
| [Type] |
| [None] |
| [ |
| [classref boost::tti::valid_member_metafunction] |
| |
| class T = a metafunction type |
| |
| returns = true if the return 'type' of the metafunction exists, |
| false if it does not.'Existence' is determined by whether |
| the return 'type' does not equal the marker type of |
| BOOST_TTI_MEMBER_TYPE. |
| ] |
| [[headerref boost/tti/member_type.hpp `member_type.hpp`]] |
| ] |
| ] |
| |
| In our first metafunction, 'boost::tti::valid_member_type', the first |
| parameter is the return 'type' from invoking the metafunction generated |
| by BOOST_TTI_MEMBER_TYPE. If when the metafunction was invoked a user-defined |
| marker type had been specified, then the second optional parameter is that |
| marker type, else it is not necessary to specify the optional second template |
| parameter. Since the marker type is saved as the nested type |
| boost::tti::marker_type once we invoke the metafunction generated by |
| BOOST_TTI_MEMBER_TYPE we can always use that as our second template parameter |
| to 'boost::tti::valid_member_type' if we like. |
| |
| The second metafunction, boost::tti::valid_member_metafunction, makes the |
| process of passing our nested 'type' and our marker type a bit easier. Here |
| the single template parameter is the invoked metafunction generated by |
| BOOST_TTI_MEMBER_TYPE itself. It then picks out from the invoked metafunction |
| both the return 'type' and the nested boost::tti::marker_type to do the correct |
| calculation. |
| |
| A simple example of this functionality would be: |
| |
| #include <boost/tti/member_type.hpp> |
| |
| struct UDMarkerType { }; |
| |
| BOOST_TTI_MEMBER_TYPE(ANamedType) |
| |
| typedef member_type_ANamedType<EnclosingType> IMType; |
| typedef member_type_ANamedType<EnclosingType,UDMarkerType> IMTypeWithMarkerType; |
| |
| then |
| |
| boost::tti::valid_member_type<IMType::type>::value |
| boost::tti::valid_member_type<IMTypeWithMarkerType::type,IMTypeWithMarkerType::boost_tti_marker_type>::value |
| |
| or |
| |
| boost::tti::valid_member_metafunction<IMType>::value |
| boost::tti::valid_member_metafunction<IMTypeWithMarkerType>::value |
| |
| gives us our compile-time result. |
| |
| [heading An extended nested type example] |
| |
| As an extended example, given a type T, let us create a metafunction where there is a nested type FindType |
| whose enclosing type is eventually T, as represented by the following structure: |
| |
| struct T |
| { |
| struct AType |
| { |
| struct BType |
| { |
| struct CType |
| { |
| struct FindType |
| { |
| }; |
| } |
| }; |
| }; |
| }; |
| |
| In our TTI code we first create a series of member type macros for each of our nested |
| types: |
| |
| BOOST_TTI_MEMBER_TYPE(AType) |
| BOOST_TTI_MEMBER_TYPE(BType) |
| BOOST_TTI_MEMBER_TYPE(CType) |
| BOOST_TTI_MEMBER_TYPE(FindType) |
| |
| Next we can create a typedef to reflect a nested type called FindType which has the relationship |
| as specified above by instantiating our macro metafunctions. We have to do this in the reverse |
| order of our hypothetical 'struct T' above since the metafunction `BOOST_TTI_MEMBER_TYPE` takes |
| its enclosing type as its template parameter. |
| |
| typedef typename |
| member_type_FindType |
| < |
| typename member_type_CType |
| < |
| typename member_type_BType |
| < |
| typename member_type_AType |
| < |
| T |
| >::type |
| >::type |
| >::type |
| >::type MyFindType; |
| |
| We can use the above typedef to pass the type as FindType |
| to one of our macro metafunctions. FindType may not actually exist but we will not generate |
| a compiler error when we use it. It will only generate, if it does not exist, an eventual |
| failure by having whatever metafunction uses such a type return a false value at compile-time. |
| |
| As one example, let's ask whether FindType has a static member data called MyData of type 'int'. |
| We add: |
| |
| BOOST_TTI_HAS_STATIC_MEMBER_DATA(MyData) |
| |
| Next we create our metafunction: |
| |
| has_static_member_data_MyData |
| < |
| MyFindType, |
| int |
| > |
| |
| and use this in our metaprogramming code. Our metafunction now tells us whether the nested type |
| FindType has a static member data called MyData of type 'int', even if FindType does not actually |
| exist as we have specified it as a type. If we had tried to do this using normal C++ nested type |
| notation our metafunction code above would be: |
| |
| has_static_member_data_MyData |
| < |
| typename T::AType::BType::CType::FindType, |
| int |
| > |
| |
| But this fails with a compiler error if there is no such nested type, and |
| that is exactly what we do not want in our compile-time metaprogramming code. |
| |
| In the above metafunction we are asking whether or not FindType has a static |
| member data element called 'MyData', and the result will be 'false' if either |
| FindType does not exist or if it does exist but does not have a static member data |
| of type 'int' called 'MyData'. In neither situation will we produce a compiler error. |
| |
| We may also be interested in ascertaining whether the deeply nested |
| type 'FindType' actually exists. Our metafunction, using BOOST_TTI_MEMBER_TYPE |
| and repeating our macros from above, could be: |
| |
| BOOST_TTI_MEMBER_TYPE(FindType) |
| BOOST_TTI_MEMBER_TYPE(AType) |
| BOOST_TTI_MEMBER_TYPE(BType) |
| BOOST_TTI_MEMBER_TYPE(CType) |
| |
| BOOST_TTI_HAS_TYPE(FindType) |
| |
| has_type_FindType |
| < |
| typename |
| member_type_CType |
| < |
| typename |
| member_type_BType |
| < |
| typename |
| member_type_AType |
| < |
| T |
| >::type |
| >::type |
| >::type |
| > |
| |
| But this duplicates much of our code when we generated the 'MyFindType' typedef. |
| Instead we use the functionality already provided by 'boost::tti::valid_member_type'. |
| Using this functionality with our 'MyFindType' type above we create the nullary |
| metafunction: |
| |
| boost::tti::valid_member_type |
| < |
| MyFindType |
| > |
| |
| directly instead of replicating the same functionality with our 'has_type_FindType' |
| metafunction. |
| |
| [endsect] |