Open Source im professionellen Einsatz

Metafunktionen

Template Metaprogramming erzeugt den gewünschten C++-Sourcecode zur Compilezeit. Funktionen und Daten, die zur Compilezeit zur Anwendung kommen, nennt man Metafunktionen und Metadaten. Metafunktionen sind Klassentemplates (Zeilen 4 und 9 in Listing 3), der C++-Funktion »myFunction(arg1,arg2)« etwa entspricht die Metafunktion »My Function<Arg1,Arg2>« .

Das Ergebnis der Metafunktion wird per Konvention, falls es ein Typ ist, durch »::type« (Zeile 30), falls es eine integrale Konstante ist, durch »::value« (Zeile 14 in Listing 2) zur Verfügung gestellt. Diese Konvention erleichtert das Programmieren mit Metafunktionen deutlich, da Metafunktionen mehrere Returnwerte zurückgeben können.

Typen und integrale Daten sind die Metadaten, auf denen Metafunktionen wirken. Metadaten werden aber nicht modifiziert, sondern auf Anfrage neu erzeugt. Zur Compilezeit gibt es keine Variablen, sondern nur symbolische Namen. Da C++ weder String- noch Fließkommakonstanten als Templateparameter erlaubt, lassen sich diese beim Template Metaprogramming nicht verwenden. Als Case-Struktur zur Compilezeit dient bei dieser Programmiertechnik Pattern Matching in Form partieller oder vollständiger Templatespezialisierung. Template Metaprogramming setzt Pattern Matching nach dem Best-fit-Prinzip ein, das die am genauesten passende Variante auswählt. Dies steht im Gegensatz zum vertrauteren Pattern Matching nach dem First-fit-Prinzip, das bei Ausnahmebehandlung in C++, Java oder Python gilt.

Die C++-Syntax verlangt, dass der Programmierer das primäre Template (Zeile 4 in Listing 3) als erstes zumindest deklariert, bevor er die Spezialisierungen (Zeile 9) definiert. Pattern Matching und Rekursion als Strukturelemente (Listing 2), die Abwesenheit von Variablen – die Anzeichen häufen sich, dass Template Metaprogramming eine funktionale Sprache ist. Grund genug, sich zu fragen: Lässt sich mit Template Metaprogramming im funktionalen Stil programmieren?

Map macht's möglich

Neben »filter« und »reduce« ist »map« die klassische Funktion aus der funktionalen Programmierung. Sie nimmt eine Funktion und eine Liste an und gibt eine neue Liste zurück, indem sie die Funktion auf jedes Listenelement anwendet. Was liegt näher, als sie als Metafunktion umzusetzen? Die zwei elementaren Bausteine, um zur Compilezeit Listen zu implementieren, sind Variadic Templates [3] und der Container »std::tuple« [2]. Beide finden sich im kommenden C++0x-Standard und können mit beliebig vielen Argumenten umgehen. Wie die Klassentemplates auf »std::tuple« beziehungsweise wie Metafunktionen auf Metadaten wirken, zeigt Listing 4.

Listing 4

Die Map-Funktion zur Compilezeit

01 #include <iostream>
02 #include <tuple>
03
04 // Int2Type
05
06 template <int v>
07 struct Int2Type {
08     const static int value= v;
09 };
10
11 // a few class templates
12
13 struct ApplyId{
14     template<typename T>
15     struct apply{
16       typedef Int2Type<T::value> type;
17     };
18 };
19
20 struct MakeTen{
21     template<typename T>
22     struct apply{
23       typedef Int2Type<10> type;
24     };
25 };
26
27 struct DoubleMe{
28     template<typename T>
29     struct apply{
30       typedef Int2Type<2*(T::value)> type;
31     };
32 };
33
34 struct AbsMe{
35     template<typename T>
36     struct apply{
37       typedef Int2Type< (T::value > 0)? T::value : -(T::value) > type;
38     };
39 };
40
41 // helper function for output
42
43 template< typename head >
44 void showMe( std::tuple< head> ){
45   std::cout << head::value << "\n";
46 };
47
48 template< typename head, typename ... tail>
49 void showMe( std::tuple< head, tail ... > ){
50   std::cout << head::value << " ," ;
51   showMe( std::tuple< tail ...>() );
52 }
53
54 // map function
55
56 template<typename F, typename ... Elements> struct map;
57
58 template< typename F, typename ... Elements>
59 struct map<F,std::tuple<Elements ...> >{
60     typedef std::tuple<typename F::template apply<Elements>::type ... > type;
61 };
62
63 int main(){
64
65     std::cout << "original tupel: " << std::endl;
66     typedef std::tuple<Int2Type<-5>,Int2Type<-4>,Int2Type<-3>,Int2Type<-2>,Int2Type<-1>,Int2Type<0>,
67                        Int2Type<1>,Int2Type<2>,Int2Type<3>,Int2Type<4>,Int2Type<5> >constNumbers;
68     showMe( constNumbers() );
69
70     std::cout << "\napply identity: " << std::endl;
71     typedef map<ApplyId, constNumbers >::type idConstNumbers;
72     showMe( idConstNumbers() );
73
74     std::cout << "\nset each value to 10" << std::e ndl;
75     typedef map<MakeTen, constNumbers >::type makeTenConstNumbers;
76     showMe( makeTenConstNumbers() );
77
78     std::cout << "\ndouble each value" << std::endl;
79     typedef map<DoubleMe, constNumbers >::type doubleMeConstNumbers;
80     showMe( doubleMeConstNumbers() );
81
82     std::cout << "\nabsolute value" << std::endl;
83     typedef map<AbsMe, constNumbers >::type absMeConstNumbers;
84     showMe( absMeConstNumbers() );
85
86 };

Vor der Theorie kommt die Praxis. Kompiliert und ausgeführt, ergibt der Quelltext die Ausgabe in Abbildung 4. Das Programm besteht aus der »map« -Metafunktion (Zeile 56), die beliebig viele Argumente annimmt, sie in ein »std::tuple« verpackt und die Metafunktionen »ApplyId« , »MakeTen« , »DoubleMe« und »AbsMe« auf ihre einzelnen Argumente (Zeile 60) anwendet. Das einfache Funktionstemplate »showMe« gibt den Container zu Laufzeit aus.

Abbildung 4: Ganz funktionale Schule: Die in der Templatesprache umgesetzte Map-Funktion wendet eine Funktion auf jedes Element einer Liste an.

Abbildung 4: Ganz funktionale Schule: Die in der Templatesprache umgesetzte Map-Funktion wendet eine Funktion auf jedes Element einer Liste an.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 7 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook