Section 1.5.  Why Metaprogramming?
Team LiB
Previous Section Next Section

1.5. Why Metaprogramming?

So, what are the benefits of metaprogramming? There are definitely simpler ways to address the same kinds of problems we've been discussing here. Let's take a look at two other approaches and see how they stack up when applied to the interpretation of binary numerals and parser construction.

1.5.1. Alternative 1: Runtime Computation

Most straightforwardly, we could do the computation at runtime instead of compile time. For example, we might use one of the binary function implementations shown earlier, or a parsing system could be designed to interpret the input grammar at runtime the first time we ask it to parse.

The most obvious reason to rely on a metaprogram is that by doing as much work as possible before the resulting program starts, we get faster programs. When a grammar is compiled, YACC performs substantial parse table generation and optimization steps that, if done at runtime, could noticeably degrade a program's overall performance. Similarly, because binary does its work at compile time, its ::value is available as a compile-time constant, and the compiler can encode it directly in the object code, saving a memory lookup when it is used.

A subtler but perhaps more important argument for using a metaprogram is that the result of the computation can interact more deeply with the target language. For example, the size of a C++ array can only be legally specified by a compile-time constant like binary<N>::valuenot by a runtime function's return value. The brace-enclosed actions in a YACC grammar can contain arbitrary C/C++ code to be executed as part of the generated parser. That's only possible because the actions are processed during grammar compilation and passed on to the target C/C++ compiler.

1.5.2. Alternative 2: User Analysis

Instead of doing computation at runtime or compile time, we could just do it by hand. After all, it's common practice to translate binary numbers to hexadecimal so that they can be used directly as C++ literals, and the translation steps performed by YACC and Boost.Spirit to convert the grammar description into a parser are well-known.

If the alternative is writing a metaprogram that will only be used once, one could argue that user analysis is more convenient: It certainly is easier to translate one binary number than to write a correct metaprogram to do so. It only takes a few such instances to tip the balance of convenience in the opposite direction, though. Furthermore, once the metaprogram is written, its benefits of convenience can be spread across a community of other programmers.

Regardless of how many times it's used, a metaprogram enables its user to write more expressive code, because she can specify the result in a form that corresponds to her mental model. In a context where the values of individual bits are meaningful, it makes much more sense to write binary<101010>::value than 42 or the traditional 0x2a. Similarly, the C source to a handwritten parser usually obscures the logical relationships among its grammar elements.

Finally, because humans are fallible, and because the logic of a metaprogram only needs to be written once, the resulting program is more likely to be correct and maintainable. Translating binary numbers is such a mundane task that it's easy to pay too little attention and get it wrong. By contrastas anyone who's done it can attestwriting parse tables by hand requires too much attention, and preventing mistakes is reason enough to use a parser generator such as YACC.

1.5.3. Why C++ Metaprogramming?

In a language such as C++, where the domain language is just a subset of the language used in the rest of the program, metaprogramming is even more powerful and convenient.

  • The user can enter the domain language directly, without learning a foreign syntax or interrupting the flow of her code.

  • Interfacing metaprograms with other code, especially other metaprograms, becomes much smoother.

  • No additional build step (like the one imposed by YACC) is required.

In traditional programming it is very common to find oneself trying to achieve the right balance of expressivity, correctness, and efficiency. Metaprogramming often allows us to interrupt that classic tension by moving the computation required for expressivity and correctness from runtime to compile time.

    Team LiB
    Previous Section Next Section