Section A.1.  Motivation
Team LiB
Previous Section Next Section

A.1. Motivation

Even with the full power of template metaprogramming and the Boost Metaprogramming Library at our disposal, some C++ coding jobs still require a great deal of boilerplate code repetition. We saw one example in Chapter 5, when we implemented tiny_size:



   template <class T0, class T1, class T2>


   struct tiny_size


     : mpl::int_<3> {};



Aside from the repeated pattern in the parameter list of the primary template above, there are three partial specializations below, which also follow a predictable pattern:



   template <class T0, class T1>


   struct tiny_size<T0,T1,none>


     : mpl::int_<2> {};





   template <class T0>


   struct tiny_size<T0,none,none>


     : mpl::int_<1> {};





   template <>


   struct tiny_size<none,none,none>


     : mpl::int_<0> {};



In this case there is only a small amount of code with such a "mechanical" flavor, but had we been implementing large instead of tiny, there might easily have been a great deal more. When the number of instances of a pattern grows beyond two or three, writing them by hand tends to become error-prone. Perhaps more importantly, the code gets hard to read, because the important abstraction in the code is really the pattern, not the individual instances.

A.1.1. Code Generation

Rather than being written out by hand, mechanical-looking code should really be generated mechanically. Having written a program to spit out instances of the code pattern, a library author has two choices: She can either ship pre-generated source code files, or she can ship the generator itself. Either approach has drawbacks. If clients only get the generated source, they are stuck with whatever the library author generatedand experience shows that if they are happy with three instances of a pattern today, someone will need four tomorrow. If clients get the generator program, on the other hand, they also need the resources to execute it (e.g., interpreters), and they must integrate the generator into their build processes...

A.1.2. Enter the Preprocessor

...unless the generator is a preprocessor metaprogram. Though not designed for that purpose, the C and C++ preprocessors can be made to execute sophisticated programs during the preprocessing phase of compilation. Users can control the code generation process with preprocessor #defines in code or -D options on the compiler's command line, making build integration trivial. For example, we might parameterize the primary tiny_size template above as follows:



   #include <boost/preprocessor/repetition/enum_params.hpp>





   #ifndef TINY_MAX_SIZE


   #  define TINY_MAX_SIZE 3  // default maximum size is 3


   #endif





   template <BOOST_PP_ENUM_PARAMS(TINY_MAX_SIZE, class T)>


   struct tiny_size


     : mpl::int_<TINY_MAX_SIZE>


   {};



To test the metaprogram, run your compiler in its "preprocessing" mode (usually the -E option), with the Boost root directory in your #include path. For instance:[1]

[1] GCC's -P option inhibits the generation of source file and line number markers in preprocessed output.



   g++ -P -E -Ipath/to/boost_1_32_0 -I. test.cpp



Given the appropriate metaprograms, users would be able to adjust not only the number of parameters to tiny_size, but the maximum size of the entire tiny implementation just by #define-ing TINY_MAX_SIZE.

The Boost Preprocessor library [MK04] plays a role in preprocessor metaprogramming similar to the one played by the MPL in template metaprogramming: It supplies a framework of high-level components (like BOOST_PP_ENUM_PARAMS) that make otherwise-painful metaprogramming jobs approachable. In this appendix we won't attempt to cover nitty-gritty details of how the preprocessor works, nor principles of preprocessor metaprogramming in general, nor even many details of how the Preprocessor library works. We will show you enough at a high level that you'll be able to use the library productively and learn the rest on your own.

    Team LiB
    Previous Section Next Section