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