Aus Linux-Magazin 01/2011

Template Metaprogramming mit C++

© Juri Samsonov, 123RF.com

C++-Programme arbeiten zur Laufzeit, der eingebaute Template-Mechanismus aber zur Compilezeit. Geschickte Entwickler nutzen dies für Metaprogrammierung im funktionalen Stil, die erstaunlich performante und sogar selbstoptimierende Software ermöglicht.

Obwohl durch Zufall entdeckt, erweitert Template Metaprogramming die Sprache C++ mit einer Mächtigkeit, die sich mit klassischem Programmcode nicht erreichen lässt. Der Template-Mechanismus, ursprünglich zum generischen Programmieren (unabhängig vom Datentyp) eingeführt, entpuppte sich als Metasprache in C++, die Typen und Werte zur Compilezeit berechnet. Die resultierenden Programme sind schneller, da sich die Arbeit von der Laufzeit auf die Compilezeit verlagert. Höhere Performance paart sich mit erweiterter Funktionalität.

Wie alles begann

1994 stellte Erwin Unruh von Siemens-Nixdorf dem C++-Standardkomitee sein Primzahlen-Programm vor – das wohl berühmteste C++-Programm, das nicht kompiliert [1]. Die Fehlermeldung als Seiteneffekt des Übersetzungsprozesses war das Revolutionäre seines Programms: Die Primzahlen bis 30 waren in den Fehlerausgaben des Compilers versteckt. Listing 1 zeigt die wesentlichen Zeilen der originalen Ausgabe.

Listing 1

Fehlerausgabe des Primzahlen-Programms

01 |    Type `enum{}' can't be converted to txpe `D<2>' ("primes.cpp",L2/C25).
02 |    Type `enum{}' can't be converted to txpe `D<3>' ("primes.cpp",L2/C25).
03 |    Type `enum{}' can't be converted to txpe `D<5>' ("primes.cpp",L2/C25).
04 |    Type `enum{}' can't be converted to txpe `D<7>' ("primes.cpp",L2/C25).
05 |    Type `enum{}' can't be converted to txpe `D<11>' ("primes.cpp",L2/C25).
06 |    Type `enum{}' can't be converted to txpe `D<13>' ("primes.cpp",L2/C25).
07 |    Type `enum{}' can't be converted to txpe `D<17>' ("primes.cpp",L2/C25).
08 |    Type `enum{}' can't be converted to txpe `D<19>' ("primes.cpp",L2/C25).
09 |    Type `enum{}' can't be converted to txpe `D<23>' ("primes.cpp",L2/C25).
10 |    Type `enum{}' can't be converted to txpe `D<29>' ("primes.cpp",L2/C25).

Der Beweis war erbracht: Template-Instanziierung lässt sich dazu verwenden, Werte zur Compilezeit zu berechnen. Das Programm in Listing 2 beispielsweise rechnet die Fakultät von 5 zur Compilezeit aus. Das primäre oder auch allgemeine Template »template <int N> struct Factorial« ruft sich mit »static int const value= N * Factorial<N-1>::value« rekursiv selbst auf, bis die Template-Spezialisierung »template <> struct Factorial<1>« eintritt. In diesem Fall wird die Rekursion mit dem Wert 1 beendet und das Ergebnis berechnet.

Listing 2

Fakultät-Berechnung zur Compilezeit

01 #include <iostream>
02
03 template <int N>
04 struct Factorial{
05     static int const value= N * Factorial<N-1>::value;
06 };
07
08 template <>
09 struct Factorial<1>{
10     static int const value = 1;
11 };
12
13 int main(){
14     std::cout << Factorial<5>::value << std::endl;
15     std::cout << 120 << std::endl;
16     return 0;
17 }

Da die Template-Instanziierung und somit auch die Rekursion zur Compilezeit abläuft, liegt der Wert von »Factorial <5>::value« zur Laufzeit des resultierenden Programms als Konstante vor. Das Übersetzen des Quellcode mit Debug-Informationen per »g++ -g -o fakultaet fakultaet.cpp« und ein anschließender Objektdump mit »objdump -S fakultaet« bringen die Magie in doppelter Hinsicht ans Tageslicht (Abbildung 1).

Abbildung 1: Der Objektdump des Fakultät-Programms enthält das Ergebnis der Berechnung.

Abbildung 1: Der Objektdump des Fakultät-Programms enthält das Ergebnis der Berechnung.

Zum einen lautet die Fakultät von 5 in hexadezimaler Schreibweise »x78« . Genau dieser Wert steht in der Assembler-Anweisung »mov $0x78,$esi« . Zum anderen resultiert aus dem Sourcecode »std::cout << Factorial<5>::value << std::endl« der gleiche Assembler-Code wie aus dem Sourcecode »std::cout << 120 << std::endl« , in dem der Wert der Fakultät von 5 schon als Konstante vorliegt.

Charakteristiken

Template Metaprogramming ist eine Meta-Technik in C++, in der der Compiler die Templates zur Compilezeit interpretiert und sie in C++-Quelltext transformiert. Diesen temporär erzeugten Code übersetzt er zusammen mit dem restlichen Sourcecode. C++ ist insofern eine Zwei-Level-Sprache, weil der statische Code zur Compilezeit, der resultierende dynamische Code aber zur Laufzeit ausgeführt wird (Abbildung 2).

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 7 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Comments
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben