123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- [/
- (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]
|