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