2
0
mirror of https://github.com/boostorg/tti.git synced 2026-01-26 07:02:14 +00:00
Files
tti/doc/TTINestedType.qbk
2011-02-10 00:21:18 +00:00

194 lines
8.5 KiB
Plaintext

[section:tti_nested_type Nested Types]
[heading The problem]
The goal of the TTI library is never to produce a compiler error by just
using the functionality in the library, whether it is invoking its function-like macros
or instantiating the macro metafunctions created by them, and whether the inner element exists
or not. In this sense The TTI library macros for introspecting an enclosing type for
an inner element work very well. But there is one exception to this general case.
That exception is the crux of the discussion regarding nested types which follows.
The metafunctions generated by the TTI macros all work with 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.
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 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.
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 TTI_HAS_TYPE(InnerType) macro and it's related
tti::has_type_InnerType metafunction to determine if the nested type 'InnerType' exists. This solution is perfectly valid
and, 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 TTI library offers a better solution in the form of constructs which work with
nested types without producing a compiler error if the nested type does not exist, but still
are able to do the introspecting for inner elements that our TTI macro metafunctions do.
We have already seen one of those constructs, the macro TTI\_MEMBER\_TYPE,
which generates a metafunction based on the name
of an inner type. But instead of telling us whether that inner type exists it instead returns
a typedef 'type' which is that inner type if it exists, else it is an unspecified type if it
does not. In this way we have created a metafunction, very similar in functionality to
boost::mpl::identity, but which still returns some unspecified marker 'type' if our nested type is invalid.
We can use the functionality of TTI\_MEMBER\_TYPE to construct nested types
for our other macro metafunctions, without having to use the T::InnerType syntax and produce a compiler
error if no such type actually exists within our scope. We can even do this in deeply nested contexts
by stringing together, so to speak, a series of these macro metafunction results.
As an 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:
TTI_MEMBER_TYPE(FindType)
TTI_MEMBER_TYPE(AType)
TTI_MEMBER_TYPE(BType)
TTI_MEMBER_TYPE(CType)
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.
typedef typename
tti::member_type_FindType
<
typename tti::member_type_CType
<
typename tti::member_type_BType
<
typename tti::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.
As one example, let's ask whether FindType has a static member data called MyData of type 'int'. We add:
TTI_HAS_STATIC_MEMBER(MyData)
Next we create our metafunction:
tti::has_static_member_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.
We can also directly find out whether the deeply nested type 'FindType'
actually exists in a similar manner. Our metafunction would be:
TTI_HAS_TYPE(FindType)
tti::has_type_FindType
<
typename
tti::member_type_CType
<
typename
tti::member_type_BType
<
typename
tti::member_type_AType
<
T
>::type
>::type
>::type
>
Because this duplicates much of our code for the 'MyFindType' typedef to
create our nested type, we can instead, and much more easily, pass our type 'MyFindType', since we already
have it in the form of a type,
to another metafunction called 'tti::valid_member_type', which returns a boolean constant
which is 'true' if our nested exists or 'false' if it does not.
Using this functionality with our 'MyFindType' type above
we could create the nullary metafunction:
tti::valid_member_type
<
MyFindType
>
directly instead of replicating the same functionality with our 'tti::has_type_FindType' metafunction.
The using of TTI\_MEMBER\_TYPE to create a nested type which may or may not exist, and which can
subsequently be used with our macro metafunctions whenever a nested type is required, without producing
a compiler error when the type does not actually exist, is the main reason we have separate
but similar functionality among our macro metafunctions to determine whether a member data, a member function, or a static member
function exists within an enclosing type.
In the more general case, when using TTI\_HAS\_MEMBER and TTI\_HAS\_STATIC\_MEMBER, the signature for the member
data, member function, and the function portion of a static member function is a composite type. This makes
for a syntactical notation which is easy to specify, but because of that composite type notation we
can not use the nested type functionality in TTI\_MEMBER\_TYPE very easily. But
when we use the TTI\_HAS\_MEMBER\_DATA, TTI\_HAS\_MEMBER\_FUNCTION, and TTI\_HAS\_STATIC\_MEMBER\_FUNCTION
the composite types in our signatures are broken down into their individual types so that using
TTI\_MEMBER\_TYPE, if necessary, for one of the individual types is easy.
[heading A more elegant solution]
Although using TTI\_MEMBER\_TYPE represents a good solution to creating a nested type
without the possible compile-time error of the T::InnerType syntax, reaching in to
specify all those ::type expressions, along with their repeated 'typename',
does get syntactically tedious.
Because of this the TTI library offers a parallel set of
metafunctions to the macro metafunctions where the 'types' specified are themselves nullary metafunctions.
This parallel set of metafunctions, using nullary metafunctions to specify individual types,
rather than the actual types themselves,
are called 'nullary type metafunctions'. In this group there is also a nullary metafunction
paralleling our TTI\_MEMBER\_TYPE macro metafunction, and therefore a further construct
making the specifying of nested types easy and error-free to use.
This group of nullary type metafunctions will be fully explained later.
[endsect]