Access:

» Boost.MPL: your ticket to the metaprogramming journey

Related categories:

Aleksey Gurtovoy
Viewed: 3488 | Article date: 2006-07-18 14:28:49

The Boost.MPL is an open-source, general-purpose, high-level C++ template metaprogramming framework. It's also known as a library that makes template metaprogramming fun, understandable, and practical for real-world use. This article was conceived as a gentle, lightweight introduction to both the domain (C++ template metaprogramming) and the library.

The Boost.MPL is an open-source, general-purpose, high-level C++ template metaprogramming framework. It's also known as a library that makes template metaprogramming fun, understandable, and practical for real-world use. This article was conceived as a gentle, lightweight introduction to both the domain (C++ template metaprogramming) and the library, and is intended to be more a "food for thought" kind of material than an in-depth course on the topic (If after reading throught this you feel that you are ready for some more material, please see the Boost.MPL resources on the net and the book on the topic, "C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond" (http://boost-consulting.com/mplbook/)).

About the author

Aleksey Gurtovoy is a technical lead for MetaCommunications and a contributing member of the Boost C++ community. Aleksey is the original author of the Boost Metaprogramming Library. He has been working with C++ since 1993, and holds a M.S. degree in computer science from Krasnoyarsk Technical State University, Russia. Contact with the author: agurtovoy@meta-comm.com.

What's this all about?

It turned out a while ago that C++ compile-time facilities - namely, templates, - are actually Turing-complete. That is, they can be made to perform any calculation that a computer is capable of - at the compile time. Granted, the ways in which such compile-time computations could interact with the traditional run-time environment seemed pretty limited, but the fact was there - inadvertently, C++ has had its own, Turing-complete, compile-time sub-language embedded into it.

Then, as if the former wasn't mind-blowing enough by itself, Todd Veldhuizen showed us how to apply these theoretical capabilities to do something very practical and equally amazing: his Blitz++ library made an extensive use of compile-time computations to allow C++ numeric applications to perform on par, and sometimes outperform, Fortran 77/90. After years of being dismissed as too slow for scientific computing, C++ was suddenly on top of the game, and the magic of compile-time program manipulation was the main driving force behind it! It was the year of 1995, and the beginning of the C++ metaprogramming revolution.

I probably don't need to tell you how immensely the language has advanced since then, in many ways, but if asked for the three most important things that happened to C++ in these years, my list would be: standardization of the STL, emergence of Boost, and template metaprogramming becoming ready for the mainstream. It's been about ten years, and now we have the experience, the understanding, and the tools to confidently invite you on a journey to learn the new ways to solve problems in C++.

Motivation

Perhaps you are wondering why one would be excited about the possibility to perform arbitrary computations at compile time. What would be a motivation for that, anyway? There is a number of different use cases (which we are going to look at in a second), but most often, the answer to that is maintaining the desired level of abstraction under other design constraints such as space and time efficiency and static type safety.

Consider this trivial, metaprogramming-free, example:

enum fmtflags

{

boolalpha = 1L << 0 // == 1

, dec = 1L << 1 // == 2

, fixed = 1L << 2 // == 4

, hex = 1L << 3 // == 8

, internal = 1L << 4 // == 16

...

};

The comments aside, the above is the idiomatic way to define a bitmask type in C++. Obscure as it might seem to an outsider, to the seasoned C++ developers the bitshift operators pattern provides a clear advantage of being less error-prone and more maintainable than using the actual precomputed powers of 2. The essence of that advantage lies in the fact that the code (largely) preserves the original level of abstraction the author was operating at when writing it down. Because of this, we are saved from both the necessity to reconstruct the original concept(s) behind (i.e. the fact that the 1, 2, 4, 8, 16 series correspond to 2^0, 2^1, etc.) and from possible errors in the, let's be frank, mundane task of computing the increasing powers of 2 by ourselves (Although one would think that powers-of-two series are deeply wired into a developer's mind, novices' experience shows that it's actually relatively easy to make an error of inadvertently skipping a number from the series).

Now, I say "largely" because, while idiomatic, obviously the notation above could be more explicit. For instance, we could imagine something along these lines (Even more higher-level definition would be something like: bitmask fmtflags { boolalpha, dec, fixed, hex, internal ... }; but unfortunately that's beyond the reach of the C++ abstraction capabilities (mechanisms):

enum fmtflags

{

boolalpha = bitmask(0)

, dec = bitmask(1)

, fixed = bitmask(2)

, hex = bitmask(3)

, internal = bitmask(4)

...

};

Bringing this higher level of abstraction to live presents an somewhat unexcepted challenge though: bitmask can't be an ordinary function unless we are willing to give up the "integral constant expression" status of the flags and switch to run-time constants:

inline long bitmask( int n ) { return 1L << n; }

typedef scalar_type<long,struct fmtflags_tag> fmtflags;

fmtflags const boolalpha( bitmask(0) );

fmtflags const dec( bitmask(1) );

fmtflags const fixed( bitmask(2) );

...

The price for going this route would be an increased cost of bitmask computations and inability to use the result of these computations in the context where an integral constant expression is required, such as a non-type template parameters. Depending on the use cases at hand, this well could be an acceptable compromise. However, as most of us know from experience, the apparent tension between raising the bar of abstraction and effeciency is anything but untypical. In the pre-metaprogramming era, we just had to bite the bullet and go with whatever was the "lesser evil" under the particular design constraints. Armed with the contemporary metaprogramming techniques and tools, we can often alleviate or avoid the conflict altogether by shifting significant parts of the work required for supporting the desired level of abstraction to compile time.

Terminology

So far I informally used "compile-time computations" and "metaprogramming" as interchangeable terms, which, as you could guess, is not entirely correct. It's time to clean up our terminology.

First of all, it's worth noting that, despite the obvious numeric flavor of the phrase "compile-time computations", the ability to perform compile-time calculations with numbers is not what lies at the essence of the template metaprogramming. In fact, while undoubtedly more convenient and efficient, compile-time arithmetics can be emulated, down to the last bit, by the means of type manipulation (It would be horribly verbose and inefficient, but it could be done pretty easily. Please see http://TODO to get a general idea). The latter is the core of our subject and forms the foundation of most of the middle - to large - scale metaprograms.

Unsurprisingly, the definition of metaprogram in C++ is directly derived from that fact: the colloquial meaning of the term is simply "code that operates on other program elements, such as types or functions" (A more general, formal definition goes along the lines of "a program that manipulates other programs or itself").

Some of you will be quick to point out that under this definition most of the C++ code that utilizes templates or/and preprocessor can be considered a metaprogram. So the question is, when does the use of templates/preprocessor become metaprogramming? One possible answer is:

  • When the problem at hand is solved using "explicit" metaprogramming tools, that is, using library or language facilities created for the specific purpose of metaprogramming.

  • The potential metaprogram operates on families of program entities, as opposite to concrete entities.

For example, according to this classification, this is a metaprogram:

 

// explicit metaprogramming primitive; "returns" T1 or T2
// depending on the value of C
template< bool C, typename T1, typename T2 >
struct if_ { ... };

// allocates space for an object of class T on heap or "inplace"
// depending on its size
template< typename T > struct lightweight
{
// ...
typename if_< sizeof(T) <= sizeof(T*)
,
inplace_storage<T>
,
heap_storage<T>
>::
type impl_;
};
while this is not:
template< typename T, bool inplace >
struct lightweight_impl { ... };

template< typename T >
struct lightweight_impl<T,true> { ... };

// allocates space for an object of class T on heap or "inplace"
// depending on its size
template< typename T > struct lightweight
{
// ...
lightweight_impl< T, sizeof(T) <= sizeof(T*) > impl_;
};

If you don't understand these two snippets, don't worry, we are about to cover them in detail in the next section!

A d v e r t i s e m e n t
Linux BSD Unix ranking vote

Page: 1 2 3 4
Buy article Buy subscription
Buy now add to cart
add to cart
Standard price: 2€/$3 Standard price: 25€/$30
Buy article for as little as (2€/$3) each allow access to individual articles. Buy a full access to our Software Developers's Journal archive portal. You will be able to read the articles from all archive issues from year 2005 and 2006. For just 25€/$30 you get unrestricted access to the entire website for the whole year.
SDJhakin9

.SDJ Users:


.:Login
.:Password

[Register]
[Forgotten your password?]

...Shopping Cart

sum: 0 €
Choose currency:

...Topics

...Advertisement

www.acunetix.com www.verifysoft.com

...Conferences




...Print Edition Archive

...Affiliate Program



 

 

Subscribe | Contact Us | Newsletter | Privacy policy | Regulations | See all issues | About SDJ
Copyright C 2006 by Software Developer's Journal. All rights reserved.