1. What is TMP?
templating meta-programming (template metaprogramming TMP ) is the implementation of template-based C + + The process of the program, which can be executed at compile time . You can think of it: a template meta-Program is a program implemented in C + + that can run inside the C + + compiler, and its output--the C + + source fragments instantiated from the template--will be compiled as usual.
2. Advantages of using TMP
If it doesn't hit you, it's because you don't have enough to think about it.
C + + is not designed for template metaprogramming, but since TMP was discovered in 1990, it proved to be very useful, adding some extensions to the C + + language and standard libraries in order to make TMP easier to use. Yes, TMP is found, not invented. When the template is added to C + +, the TMP feature is introduced. For some people all you need to do is focus on how to use it in a smart and unexpected way.
TMP has two powerful forces. first, it makes things easier , that is, if there is no TMP, these things can be difficult or impossible to achieve . second, because template meta-programming is C + + compile-time execution, which can move some work from the runtime to the compile time. One result is a number of errors that were normally found at run time and are now discoverable at compile time. Another result is that C + + programs that use TMP are more efficient in almost every way: smaller actuators, shorter run times, and less memory requirements . (However, one consequence of moving work from the runtime to the compile time is the increase in compilation times .) Programs that use TMP may consume longer to compile than programs that do not use TMP. )
3. How do I use TMP? 3.1 Analyzing the instance in item 47 again
Consider the pseudo-code written for STL's advance in item 47. I have made bold for the pseudo-code section:
1Template<typename Itert, TypeName distt>2 voidAdvance (itert&ITER, Distt D)3 {4 if(ITER isa random access iterator) {5 6iter + = D;//Use iterator arithmetic7 8}//For random access Iters9 Ten Else { One A - if(d >=0) { while(d--) ++iter; }//Use iterative calls to - Else{ while(d++)--iter; }//+ + or-- for other the}//Iterator Categories -}
We can use typeID to replace pseudo-code to allow the program to execute. This creates a "normal" C + + Method-that is, all work is done at runtime:
1Template<typename Itert, TypeName distt>2 voidAdvance (itert&ITER, Distt D)3 {4 if(typeID (typename std::iterator_traits<itert>::iterator_category) = =5 typeid (Std::random_access_iterator_tag)) {6 7iter + = D;//Use iterator arithmetic8 9}//For random access ItersTen One Else { A - - if(d >=0) { while(d--) ++iter; }//Use iterative calls to the Else{ while(d++)--iter; }//+ + or-- for other -}//Iterator Categories -}
Item 47 indicates that this typeid-based approach is less efficient than using trait, because by using this method, the (1) type test occurs at run time rather than at compile time (2) The code that executes the run-time type test must be visible at runtime . In fact, this example also shows why TMP is more efficient than a "normal" C + + program because the traits method belongs to TMP. Remember that trait makes it possible to perform compile-time if...else operations on types.
I've mentioned something earlier that it's easier in tmp than in "normal" C + +, and a advance example is provided in Item 47. The typeID-based implementation of advance, which is mentioned in Item 47, leads to a compilation problem, as shown in the following example:
1 std::list<int>:: Iterator iter; 2 ... 3 ( ten); // move ITER ten elements forward; 4 // won ' t compile with above impl.
After considering the version of advance that was generated by the call above, replacing the template parameters Itert and Distt with the types of ITER and 10, we get the following code:
1 voidAdvance (std::list<int>::iterator& ITER,intd)2 {3 if(typeID (std::iterator_traits<std::list<int>::iterator>::iterator_category) = =4 typeid (Std::random_access_iterator_tag)) {5 6 iter + = D; 7 8 //error! won ' t compile9 Ten One } A Else { - if(d >=0) { while(d--) + +iter;} - Else{ while(d++)--iter;} the } -}
The problem is the highlighted part, that is, the use of + = statement. In this example, we use + = on the List<int>::iterator, but the list<int>::iterator is a bidirectional iterator (see item 47), so it does not support + =. Only the random-access iterator supports + =. Now, we know that the + = line will never be executed, because the typeID test performed for LIST<INT>::ITERAOTR will never be true, but the compiler has a responsibility to ensure that all the source code is valid, even if it is not executed , When ITER is not a random-access iterator "Iter+=d" is an invalid code. Compare it to the Tratis-based TMP solution, which puts the code implemented for different types into separate functions, each of which operates only on a specific type.
3.2 tmp is Turing's complete
TMP has been shown to be Turing complete (turing-complete), which means that it is strong enough to be able to count anything. With TMP, you can declare variables, perform loops, implement and Invoke functions, and so on. But the concepts that correspond to "normal" C + + look very different. For example, how the If...else condition in Item 47 is represented in TMP by using templates and template specificity. But this is the program level (assembly-level) of TMP. The TMP library (for example, Boost MPL, see item 55) provides a higher level of syntax that does not make you mistaken for "normal" C + +.
The loop in 3.3 tmp is implemented by recursive return
One more glance at how things work in TMP, let's look at the loop. There is no real loop concept in tmp, so the loop effect is done by hand. (If you're uncomfortable with recursion, you need to deal with it before you go into the TMP adventure.) TMP is primarily a functional language, and recursion for functional languages is as important as television to American pop culture: they are inseparable. Even recursion is not a normal recursion, because the TMP loop does not involve recursive function calls, which involves recursive template instantiation (instantiations).
The TMP "Hello World" program calculates the factorial at compile time. It's not an exciting program, nor is "Hello World", but these two examples are useful for introducing languages. The TMP factorial calculation demonstrates the loop by recursively returning the template instance. Also demonstrates how variables are created and used in TMP, as shown in the following code:
1template<unsigned n>//General Case:the Value of2 3 structfactorial {//factorial<n> is n times the value4 5 6 //of factorial<n-1>7 enum{value = n * factorial<n-1>:: value};8 };9Template<>//Special Case:the value ofTen structfactorial<0> {//factorial<0> is 1 One enum{value =1 }; A};
Considering the above template metaprogramming (which is really just a single meta-function factorial), you get the value of factorial (n) by referencing Factorial<n>::value.
The loop portion of the code occurs when the template instance factorial<n> references the template instance factorial<n-1>. Like all recursion, there is a special case for recursion to terminate. Here is the template of the special factorial<0>.
Each instance of the factorial template is a struct, and each struct uses an enum hack (Item 2) to declare a TMP variable called value. Value holds the current value of the recursive calculation. If TMP has a true loop structure, value will be updated each time the loop is made. Since TMP uses a recursive template instance to replace loops, each instance gets a copy of its own value, and each copy has a suitable value for the position in the "loop" to correspond to.
You can use Facorial as follows:
1 int Main () 2 {3 std::cout << factorial<5// prints45 std::cout << factorial<>::value; // Prints 3628800 6 7 }
If you think this is cooler than ice cream, you've got the footage the template meta programmer needs. If templates and specificity, recursive instances and enum hacks, and input like factorial<n-1>::value make you creepy, you're still a "normal" C + + programmer.
What else can 3.4 tmp do?
Of course, factorial has demonstrated the function of TMP, just as the "Hello World" program demonstrates the functionality of any traditional programming language. To make you understand why TMP is worth knowing, it's important to know what it can do, here are three examples:
- ensures that the secondary unit (dimensional unit) is correct. In scientific and engineering applications, it is necessary to make the correct combination of sub-units (e.g., quality, distance and time). Assigning a variable representing mass to a variable that represents speed is wrong, but dividing the distance variable by the time variable and then assigning the result to a velocity variable is no problem. By using TMP, it is possible to ensure that all the combinations of elements in the program (during compilation) are correct, regardless of how complex the calculation is. (This is also an example of using TMP to detect early errors.) An interesting aspect of the use of TMP is its ability to support fractional-to-exponential exponents. This requires that fractions be simplified during compilation, and then the compiler can confirm that, for example, the unit TIME1/2 is the same as TIME4/8. The
- optimizes the matrix operation. Item 21 explains that some functions (including operator*) must return new objects, and item 44 introduces the Squarematrix class, taking into account the following code:
1 typedef squarematrix<double, 10000> bigmatrix;2 BigMatrix M1, M2, M3, M4, M5; Create matrices And3 ...//give them values4 bigmatrix result = M1 * m2 * m3 * M4 * M5; Compute their product
The "normal" way to calculate result will have four calls to create a temporary Matrice object, each of which is applied to the return value of the operator* call. These stand-alone multiplication generates four cycles on matrix elements. It is possible to eliminate temporary objects and merge loops using the Advanced template technology of TMP-expression template (expressions templates), without modifying the syntax of the client code above. The program uses less memory, and the speed of operation can be greatly improved.
- Create a personalized design pattern implementation. Such design patterns as strategy mode, observer pattern, visitor pattern, etc. can be implemented in many ways. Using template-based techniques called policy-based design, we can create templates that represent independent design choices (choice or "policies") that can be combined in any way to produce a personalized pattern implementation. For example, using this technique can create a number of templates that implement smart pointer behavior policies (policies) that can produce hundreds of different smart pointer types (at compile time). This technology has gone beyond the field of programming, such as design patterns and smart pointers, and it has become the foundation of reproductive programming (generative programming).
4. Current status analysis of TMP
TMP is not intended for everyone. Because the syntax is not intuitive, the supporting tools are also weak. (like the debugger provided for template meta-programming.) As a "sudden" language it has only recently been discovered, and some of the conventions of TMP programming are in the experimental phase. However, by moving work from runtime to compile time, the efficiency gains are impressive, and the ability to express some of the behavior (which is difficult or impossible to achieve at runtime) is also attractive.
Support for TMP is in the ascendant period. It is likely that the next version of C + + is shown to support it. The TR1 has been supported (Item 54). Books on the subject have started to come out, and there are more and more information about TMP on the Internet. TMP may never be the mainstream, but for some programmers, especially those who implement the library, will almost certainly become the main tool.
5. Summary
- Template metaprogramming allows you to move work from the runtime to the compile time, which allows you to find errors earlier and improve run-time performance.
- The combined TMP based on policy choices can be used to generate personalized code, and can also be used to prevent the generation of inappropriate code for a particular type.
Reading notes effective C + + Item 48 Understanding template Meta programming