202 lines
12 KiB
Plaintext
202 lines
12 KiB
Plaintext
[/
|
|
(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_functionality General Functionality]
|
|
|
|
The elements of a type about which a template metaprogrammer might be interested in finding
|
|
out at compile time are:
|
|
|
|
* Does it have a nested type with a particular name ?
|
|
* Does it have a nested type with a particular name which fulfills some other possibility for that nested type.
|
|
* Does it have a nested class template with a particular name ?
|
|
* Does it have a nested class template with a particular name and a particular signature ?
|
|
* Does it have a member function with a particular name and a particular signature ?
|
|
* Does it have member data with a particular name and of a particular type ?
|
|
* Does it have a static member function with a particular name and a particular signature ?
|
|
* Does it have static member data with a particular name and of a particular type ?
|
|
* Does it have either member data or static member data with a particular name and of a particular type ?
|
|
* Does it have either a member function or static member function with a particular name and of a particular type ?
|
|
|
|
These are some of the compile-time questions which the TTI library answers. It
|
|
does this by creating metafunctions, which can be used at compile-time, using
|
|
C++ macros. Each of the metafunctions created returns a compile time constant
|
|
bool value which answers one of the above questions at compile time. When the
|
|
particular element above exists the value is 'true', or more precisely
|
|
boost::mpl::true_, while if the element does not exist the value is 'false',
|
|
or more precisely boost::mpl::false_. In either case the type of this value
|
|
is boost::mpl::bool_.
|
|
|
|
This constant bool value, in the terminology of the Boost MPL library, is called an 'integral
|
|
constant wrapper' and the metafunction generated is called a 'numerical metafunction'. The
|
|
results from calling the metafunction can be passed to other metafunctions for type selection,
|
|
the most popular of these being the boolean-valued operators in the Boost MPL library.
|
|
|
|
All of the questions above attempt to find an answer about an inner element with
|
|
a particular name. In order to do this using template metaprogramming, macros are used
|
|
so that the name of the inner element can be passed to the macro. The macro will then
|
|
generate an appropriate metafunction, which the template metaprogrammer can then use to
|
|
introspect the information that is needed. The name itself of the inner element is always passed
|
|
to the macro as a macro parameter, but other macro parameters may also be needed in some cases.
|
|
|
|
All of the macros start with the prefix `BOOST_TTI_`, create their metafunctions as class
|
|
templates in whatever scope the user invokes the macro, and come in two forms:
|
|
|
|
# In the simplest macro form, which I call the simple macro form, the 'name' of the inner element
|
|
is used directly to generate the name of the metafunction as well as serving as the 'name'
|
|
to introspect. In generating the name of the metafunction from the macro name, the
|
|
`BOOST_TTI_` prefix is removed, the rest of the macro name is changed to lower case, and an
|
|
underscore ( '_' ) followed by the 'name' is appended. As an example, for the macro
|
|
`BOOST_TTI_HAS_TYPE(MyType)` the name of the metafunction is `has_type_MyType` and it will
|
|
look for an inner type called 'MyType'.
|
|
# In a more complicated macro form, which I call the complex macro form, the macro starts with
|
|
`BOOST_TTI_TRAIT_` and a 'trait' name is passed as the first parameter, with the 'name' of the inner
|
|
element as the second parameter. The 'trait' name serves solely to completely name the metafunction in
|
|
whatever scope the macro is invoked. As an example, for the macro
|
|
`BOOST_TTI_TRAIT_HAS_TYPE(MyTrait,MyType)` the name of
|
|
the metafunction is `MyTrait` and it will look for an inner type called `MyType`.
|
|
|
|
Every macro metafunction has a simple macro form and a corresponding complex macro form.
|
|
Once either of these two macro forms are used for a particular type of inner element, the
|
|
corresponding macro metafunction works exactly the same way and has the exact same functionality.
|
|
|
|
In the succeeding documentation all macro metafunctions will be referred by their simple form
|
|
name, but remember that the complex form can always be used instead. The complex form is useful
|
|
whenever using the simple form could create a duplicate name in the same name space, thereby
|
|
violating the C++ one definition rule.
|
|
|
|
[heading Macro Metafunction Name Generation]
|
|
|
|
For the simple macro form, even though it is fairly easy to remember the algorithm by which
|
|
the generated metafunction is named, TTI also provides, for each macro metafunction,
|
|
a corresponding 'naming' macro which the end-user can use and whose sole purpose
|
|
is to expand to the metafunction name. The naming macro for each macro metafunction has the form:
|
|
'corresponding-macro'_GEN(name).
|
|
|
|
As an example, BOOST_TTI_HAS_TYPE(MyType) creates a metafunction
|
|
which looks for a nested type called 'MyType' within some enclosing type. The name of the metafunction
|
|
generated, given our rule above is 'has_type_MyType'. A corresponding macro called
|
|
BOOST_TTI_HAS_TYPE_GEN, invoked as BOOST_TTI_HAS_TYPE_GEN(MyType) in our example, expands to the
|
|
same 'has_type_MyType' name. These name generating macros, for each of the metafunction generating macros,
|
|
are purely a convenience for end-users who find using them easier than remembering the name-generating
|
|
rule given above.
|
|
|
|
[section:tti_functionality_nm_gen Macro metafunction name generation considerations]
|
|
|
|
Because having a double underscore ( __ ) in a name is reserved by the C++ implementation,
|
|
creating C++ identifiers with double underscores should be avoided by the end-user. When using
|
|
a TTI macro to generate a metafunction using the simple macro form, TTI appends a single
|
|
underscore to the macro name preceding the name of the element that is being introspected.
|
|
The reason for doing this is because Boost discourages as non-portable C++ identifiers with mixed
|
|
case letters and the underscore then becomes the normal way to separate parts of an identifier
|
|
name so that it looks understandable. Because of this decision to use the underscore to generate
|
|
the metafunction name from the macro name, any inner element starting with an underscore will cause
|
|
the identifier for the metafunction name being generated to contain a double underscore.
|
|
|
|
A rule to avoid this problem is:
|
|
|
|
* When the name of the inner element to be introspected begins with an underscore, use
|
|
the complex macro form, where the name of the metafunction is specifically given.
|
|
|
|
Furthermore because TTI often generates not only a metafunction for the end-user to use but some
|
|
supporting detail metafunctions whose identifier, for reasons of programming clarity, is the same
|
|
as the metafunction with further letters appended to it separated by an underscore, another rule is:
|
|
|
|
* When using the complex macro form, which fully gives the name of the generated
|
|
macro metafunction, that name should not end with an underscore.
|
|
|
|
Following these two simple rules will avoid names with double underscores being
|
|
generated by TTI.
|
|
|
|
[heading Reusing the named metafunction]
|
|
|
|
When the end-user uses the TTI macros to generate a metafunction for introspecting an inner
|
|
element of a particular type, that metafunction can be re-used with any combination of valid
|
|
template parameters which involve the same type of inner element of a particular name.
|
|
|
|
As one example of this let's consider two different types called
|
|
'AType' and 'BType', for each of which we want to determine whether an inner type called
|
|
'InnerType' exists. For both these types we need only generate a single macro metafunction in
|
|
the current scope by using:
|
|
|
|
BOOST_TTI_HAS_TYPE(InnerType)
|
|
|
|
We now have a metafunction, which is a C++ class template, in the current scope whose C++ identifier
|
|
is 'has_type_InnerType'. We can use this same metafunction to introspect the existence of the nested type
|
|
'InnerType' in either 'AType' or 'BType' at compile time. Although the syntax for doing this has no yet
|
|
been explained, I will give it here so that you can see how 'has_type_InnerType' is reused:
|
|
|
|
# 'has_type_InnerType<AType>::value' is a compile time constant telling us whether 'InnerType' is a
|
|
type which is nested within 'AType'.
|
|
|
|
# 'has_type_InnerType<BType>::value' is a compile time constant telling us whether 'InnerType' is a
|
|
type which is nested within 'BType'.
|
|
|
|
As another example of metafunction reuse let's consider a single type, called 'CType', for which we want to
|
|
determine if it has either of two overloaded member functions with the same name of 'AMemberFunction'
|
|
but with the different function signatures of 'int (int)' and 'double (long)'. For both these
|
|
member functions we need only generate a single macro metafunction in the current scope by using:
|
|
|
|
BOOST_TTI_HAS_MEMBER_FUNCTION(AMemberFunction)
|
|
|
|
We now have a metafunction, which is a C++ class template, in the current scope whose C++ identifier
|
|
is 'has_member_function_AMemberFunction'. We can use this same metafunction to introspect the
|
|
existence of the member function 'AMemberFunction' with either the function signature of
|
|
'int (int)' or 'double (long)' in 'CType' at compile time. Although the syntax for doing this has no yet
|
|
been explained, I will give it here so that you can see how 'has_type_InnerType' is reused:
|
|
|
|
# 'has_member_function_AMemberFunction<CType,int,boost::mpl::vector<int> >::value' is a
|
|
compile time constant telling us whether 'AMemberFunction' is a member function of type 'CType'
|
|
whose function signature is 'int (int)'.
|
|
|
|
# 'has_member_function_AMemberFunction<CType,double,boost::mpl::vector<long> >::value' is a
|
|
compile time constant telling us whether 'AMemberFunction' is a member function of type 'CType'
|
|
whose function signature is 'double (long)'.
|
|
|
|
These are just two examples of the ways a particular macro metafunction can be reused. The two
|
|
'constants' when generating a macro metafunction are the 'name' and 'type of inner element'. Once
|
|
the macro metafunction for a particular name and inner element type has been generated, it can be reused
|
|
for introspecting the inner element(s) of any enclosing type which correspond to that name and
|
|
inner element type.
|
|
|
|
[heading Avoiding ODR violations]
|
|
|
|
The TTI macro metafunctions are generating directly in the enclosing scope in which the
|
|
corresponding macro is invoked. This can be any C++ scope in which a class template can
|
|
be specified. Within this enclosing scope the name of the metafunction
|
|
being generated must be unique or else a C++ ODR ( One Definition Rule ) violation will occur.
|
|
This is extremely important to remember, especially when the enclosing scope can occur in
|
|
more than one translation unit, which is the case for namespaces and the global scope.
|
|
|
|
Because of ODR, and the way that the simple macro form metafunction name is directly dependent
|
|
on the inner element and name of the element being introspected, it is the responsibility of the
|
|
programmer using the TTI macros to generate metafunctions to avoid ODR within a module
|
|
( application or library ). There are a few general methods for doing this:
|
|
|
|
# Create unique namespace names in which to generate the macro metafunctions and/or generate
|
|
the macro metafunctions in C++ scopes which can not extend across translation units. The most
|
|
obvious example of this latter is within C++ classes.
|
|
|
|
# Use the complex macro form to specifically name the metafunction generated in order to
|
|
provide a unique name.
|
|
|
|
# Avoid using the TTI macros in the global scope.
|
|
|
|
For anyone using TTI in a library which others will eventually use, it is important
|
|
to generate metafunction names which are unique to that library.
|
|
|
|
TTI also reserves not only the name generated by the macro metafunction for its use
|
|
but also any C++ identifier sequence which begins with that name. In other words
|
|
if the metafunction being generated by TTI is named 'MyMetafunction' using the complex
|
|
macro form, do not create any C++ construct with an identifier starting with 'MyMetaFunction',
|
|
such as 'MyMetaFunction_Enumeration' or 'MyMetaFunctionHelper' in the same scope.
|
|
All names starting with the metafunction name in the current scope should be considered
|
|
out of bounds for the programmer using TTI.
|
|
|
|
[endsect]
|
|
|
|
[endsect]
|
|
|