tti_nested_type.qbk 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. [/
  2. (C) Copyright Edward Diener 2011,2012
  3. Distributed under the Boost Software License, Version 1.0.
  4. (See accompanying file LICENSE_1_0.txt or copy at
  5. http://www.boost.org/LICENSE_1_0.txt).
  6. ]
  7. [section:tti_nested_type Nested Types]
  8. Besides the functionality of the TTI library which queries whether some inner element
  9. of a given name within a type exists, the library also includes functionality for
  10. generating a nested type if it exists, else a marker type if it does not exist. By
  11. marker type is meant a type either internally created by the library, with no functionality,
  12. or designated by the end-user to represent the same idea.
  13. First I will explain the syntax and use of this functionality and then the reason it exists in
  14. the library.
  15. The functionality is a metafunction created by the macro [macroref BOOST_TTI_MEMBER_TYPE].
  16. The macro takes a single parameter, which is the name of a nested type. We will call this our
  17. 'named type'. The macro generates a metafunction called `member_type_'named_type'` which, passed
  18. an enclosing type, returns the named type if it exists, else a marker type if it does not.
  19. As with our other macros we can use the alternative form of the macro
  20. [macroref BOOST_TTI_TRAIT_MEMBER_TYPE] to pass first the name of the metafunction
  21. to be generated and then the name of the 'named type'. After that the functionality
  22. of our resulting metafunction is exactly the same.
  23. Its general explanation is given as:
  24. [table:tbmacronested TTI Nested Type Macro Metafunction
  25. [
  26. [Inner Element]
  27. [Macro]
  28. [Template]
  29. [Specific Header File]
  30. ]
  31. [
  32. [Type]
  33. [
  34. [macroref BOOST_TTI_MEMBER_TYPE](name)
  35. ]
  36. [
  37. `member_type_'name'`
  38. class T = enclosing type
  39. class U = (optional) marker type
  40. returns = the type of 'name' if it exists, else a marker type, as the typedef 'type'.
  41. The invoked metafunction also holds the marker type as the typedef 'boost_tti_marker_type'.
  42. This is done for convenience so that the marker type does not have to be remembered.
  43. ]
  44. [[headerref boost/tti/member_type.hpp `member_type.hpp`]]
  45. ]
  46. ]
  47. The marker type is purely optional. If not specified a type internal to the TTI library,
  48. which has no functionality, is used. Unless there is a specific reason for the end-user
  49. to provide his own marker type, he should let the TTI library use its own internal
  50. marker type.
  51. A simple example of this functionality would be:
  52. #include <boost/tti/member_type.hpp>
  53. BOOST_TTI_MEMBER_TYPE(ANamedType)
  54. typedef typename member_type_ANamedType<EnclosingType>::type AType;
  55. If type 'ANamedType' is a nested type of 'EnclosingType' then
  56. AType is the same type as 'ANamedType', otherwise AType is a
  57. marker type internal to the TTI library.
  58. Now that we have explained the syntax of BOOST_TTI_MEMBER_TYPE
  59. we can now answer the question of why this functionality to create
  60. a 'type' exists when looking for a nested type of an enclosing type.
  61. [heading The problem]
  62. The metafunctions generated by the TTI macros all work with various types, whether in specifying
  63. an enclosing type or in specifying the type of some inner element, which may also involve
  64. types in the signature of that element, such as a parameter or return type of a function.
  65. The C++ notation for a nested type, given an enclosing type 'T' and an inner type 'InnerType',
  66. is 'T::InnerType'. If either the enclosing type 'T' does not exist, or the inner type 'InnerType'
  67. does not exist within 'T', the expression 'T::InnerType' will give a compiler error if we attempt
  68. to use it in our template instantiation of one of TTI's macro metafunctions.
  69. This is a problem if we want to be able to introspect for the existence of inner elements
  70. to an enclosing type without producing compiler errors. Of course if we absolutely know what
  71. types we have and that a nested type exists, and these declarations are within our scope, we can
  72. always use an expression like 'T::InnerType' without compiler error. But this is often not the
  73. case when doing template programming since the type being passed to us at compile-time in a class
  74. or function template is chosen at instantiation time and is created by the user of a template.
  75. One solution to this is afforded by the library itself. Given an enclosing type 'T'
  76. which we know must exist, either because it is a top-level type we know about or
  77. it is passed to us in some template as a 'class T' or 'typename T', and given an inner type
  78. named 'InnerType' whose existence we would like ascertain, we can use a `BOOST_TTI_HAS_TYPE(InnerType)`
  79. macro and it's related `has_type_InnerType` metafunction to determine if the nested type 'InnerType'
  80. exists. This solution is perfectly valid, and in conjunction with Boost MPL's selection metafunctions,
  81. we can do compile-time selection to generate the correct template code.
  82. However this does not scale that well syntactically if we need to drill down further
  83. from a top-level enclosing type to a deeply nested type, or even to look for some deeply nested
  84. type's inner elements. We are going to be generating a great deal of `boost::mpl::if_` and/or
  85. `boost::mpl::eval_if` type selection statements to get to some final condition where we know we
  86. can generate the compile-time code which we want.
  87. [heading The solution]
  88. The solution given by BOOST_TTI_MEMBER_TYPE is that we can create a type as the return
  89. from our metafunction, which is the same type as a nested type if it exists or some other
  90. marker type if it does not, and then work with that returned type without producing a
  91. compiler error. If we had to use the 'T::InnerType' syntax to specify our type, where 'T' represents
  92. out enclosing type and 'InnerType' our nested type, and there was no nested type 'InnerType' within the
  93. enclosing type 'T, the compiler would give us an error immediately.
  94. By using BOOST_TTI_MEMBER_TYPE we have a type to work with even when such a type
  95. really does not exist. Naturally if the type does not exist, the type which we
  96. have to work with, being a marker type, will generally not fulfill any other further functionality
  97. we want from it. This is good and will normally produce the correct results in further uses of the type
  98. when doing metafunction programming. Occasionally the TTI produced marker type, when our nested
  99. type does not exist, is not sufficient for further metafunction programming. In that rare case the
  100. end-user can produce his own marker type to be used if the nested type does not exist. In any case,
  101. whether the nested type exists, whether the TTI default supplied marker type is used, or whether
  102. an end-user marker type is used, template metaprogramming can continue without a compilation
  103. problem. Furthermore this scales better than having to constant check for nested type existence
  104. via BOOST_TTI_HAS_TYPE in complicated template metaprogramming code.
  105. [heading Checking if the member type exists]
  106. Once we use BOOST_TTI_MEMBER_TYPE to generate a nested type if it exists we will normally
  107. use that type in further metafunction programming. Occasionally, given the type we generate,
  108. we will want to ask if the type is really our nested type or the marker type instead. Essentially
  109. we are asking if the type generated is the marker type or not. If it is the marker type, then
  110. the type generated is not the nested type we had hoped for. If it is not the marker type, then
  111. the type generated is the nested type we had hoped for. This is easy enough to do for the template
  112. metaprogrammer but TTI makes it easier by providing either of two metafunctions to do this calculation.
  113. These two metafunctions are 'boost::tti::valid_member_type' and 'boost::tti::valid_member_metafunction':
  114. [table:existtbmacronested TTI Nested Type Macro Metafunction Existence
  115. [
  116. [Inner Element]
  117. [Macro]
  118. [Template]
  119. [Specific Header File]
  120. ]
  121. [
  122. [Type]
  123. [None]
  124. [
  125. [classref boost::tti::valid_member_type]
  126. class T = a type
  127. class U = (optional) marker type
  128. returns = true if the type exists, false if it does not.
  129. 'Existence' is determined by whether the type
  130. does not equal the marker type of BOOST_TTI_MEMBER_TYPE.
  131. ]
  132. [[headerref boost/tti/member_type.hpp `member_type.hpp`]]
  133. ]
  134. [
  135. [Type]
  136. [None]
  137. [
  138. [classref boost::tti::valid_member_metafunction]
  139. class T = a metafunction type
  140. returns = true if the return 'type' of the metafunction exists,
  141. false if it does not.'Existence' is determined by whether
  142. the return 'type' does not equal the marker type of
  143. BOOST_TTI_MEMBER_TYPE.
  144. ]
  145. [[headerref boost/tti/member_type.hpp `member_type.hpp`]]
  146. ]
  147. ]
  148. In our first metafunction, 'boost::tti::valid_member_type', the first
  149. parameter is the return 'type' from invoking the metafunction generated
  150. by BOOST_TTI_MEMBER_TYPE. If when the metafunction was invoked a user-defined
  151. marker type had been specified, then the second optional parameter is that
  152. marker type, else it is not necessary to specify the optional second template
  153. parameter. Since the marker type is saved as the nested type
  154. boost::tti::marker_type once we invoke the metafunction generated by
  155. BOOST_TTI_MEMBER_TYPE we can always use that as our second template parameter
  156. to 'boost::tti::valid_member_type' if we like.
  157. The second metafunction, boost::tti::valid_member_metafunction, makes the
  158. process of passing our nested 'type' and our marker type a bit easier. Here
  159. the single template parameter is the invoked metafunction generated by
  160. BOOST_TTI_MEMBER_TYPE itself. It then picks out from the invoked metafunction
  161. both the return 'type' and the nested boost::tti::marker_type to do the correct
  162. calculation.
  163. A simple example of this functionality would be:
  164. #include <boost/tti/member_type.hpp>
  165. struct UDMarkerType { };
  166. BOOST_TTI_MEMBER_TYPE(ANamedType)
  167. typedef member_type_ANamedType<EnclosingType> IMType;
  168. typedef member_type_ANamedType<EnclosingType,UDMarkerType> IMTypeWithMarkerType;
  169. then
  170. boost::tti::valid_member_type<IMType::type>::value
  171. boost::tti::valid_member_type<IMTypeWithMarkerType::type,IMTypeWithMarkerType::boost_tti_marker_type>::value
  172. or
  173. boost::tti::valid_member_metafunction<IMType>::value
  174. boost::tti::valid_member_metafunction<IMTypeWithMarkerType>::value
  175. gives us our compile-time result.
  176. [heading An extended nested type example]
  177. As an extended example, given a type T, let us create a metafunction where there is a nested type FindType
  178. whose enclosing type is eventually T, as represented by the following structure:
  179. struct T
  180. {
  181. struct AType
  182. {
  183. struct BType
  184. {
  185. struct CType
  186. {
  187. struct FindType
  188. {
  189. };
  190. }
  191. };
  192. };
  193. };
  194. In our TTI code we first create a series of member type macros for each of our nested
  195. types:
  196. BOOST_TTI_MEMBER_TYPE(AType)
  197. BOOST_TTI_MEMBER_TYPE(BType)
  198. BOOST_TTI_MEMBER_TYPE(CType)
  199. BOOST_TTI_MEMBER_TYPE(FindType)
  200. Next we can create a typedef to reflect a nested type called FindType which has the relationship
  201. as specified above by instantiating our macro metafunctions. We have to do this in the reverse
  202. order of our hypothetical 'struct T' above since the metafunction `BOOST_TTI_MEMBER_TYPE` takes
  203. its enclosing type as its template parameter.
  204. typedef typename
  205. member_type_FindType
  206. <
  207. typename member_type_CType
  208. <
  209. typename member_type_BType
  210. <
  211. typename member_type_AType
  212. <
  213. T
  214. >::type
  215. >::type
  216. >::type
  217. >::type MyFindType;
  218. We can use the above typedef to pass the type as FindType
  219. to one of our macro metafunctions. FindType may not actually exist but we will not generate
  220. a compiler error when we use it. It will only generate, if it does not exist, an eventual
  221. failure by having whatever metafunction uses such a type return a false value at compile-time.
  222. As one example, let's ask whether FindType has a static member data called MyData of type 'int'.
  223. We add:
  224. BOOST_TTI_HAS_STATIC_MEMBER_DATA(MyData)
  225. Next we create our metafunction:
  226. has_static_member_data_MyData
  227. <
  228. MyFindType,
  229. int
  230. >
  231. and use this in our metaprogramming code. Our metafunction now tells us whether the nested type
  232. FindType has a static member data called MyData of type 'int', even if FindType does not actually
  233. exist as we have specified it as a type. If we had tried to do this using normal C++ nested type
  234. notation our metafunction code above would be:
  235. has_static_member_data_MyData
  236. <
  237. typename T::AType::BType::CType::FindType,
  238. int
  239. >
  240. But this fails with a compiler error if there is no such nested type, and
  241. that is exactly what we do not want in our compile-time metaprogramming code.
  242. In the above metafunction we are asking whether or not FindType has a static
  243. member data element called 'MyData', and the result will be 'false' if either
  244. FindType does not exist or if it does exist but does not have a static member data
  245. of type 'int' called 'MyData'. In neither situation will we produce a compiler error.
  246. We may also be interested in ascertaining whether the deeply nested
  247. type 'FindType' actually exists. Our metafunction, using BOOST_TTI_MEMBER_TYPE
  248. and repeating our macros from above, could be:
  249. BOOST_TTI_MEMBER_TYPE(FindType)
  250. BOOST_TTI_MEMBER_TYPE(AType)
  251. BOOST_TTI_MEMBER_TYPE(BType)
  252. BOOST_TTI_MEMBER_TYPE(CType)
  253. BOOST_TTI_HAS_TYPE(FindType)
  254. has_type_FindType
  255. <
  256. typename
  257. member_type_CType
  258. <
  259. typename
  260. member_type_BType
  261. <
  262. typename
  263. member_type_AType
  264. <
  265. T
  266. >::type
  267. >::type
  268. >::type
  269. >
  270. But this duplicates much of our code when we generated the 'MyFindType' typedef.
  271. Instead we use the functionality already provided by 'boost::tti::valid_member_type'.
  272. Using this functionality with our 'MyFindType' type above we create the nullary
  273. metafunction:
  274. boost::tti::valid_member_type
  275. <
  276. MyFindType
  277. >
  278. directly instead of replicating the same functionality with our 'has_type_FindType'
  279. metafunction.
  280. [endsect]