Funktionale Programmierung und Template Metaprogramming
Funktionale Programmiersprachen sind in den letzten Jahren immer beliebter geworden, denn viele Abläufe lassen sich funktional einfacher und in weniger Zeilen formulieren als in imperativen Sprachen (eine Kombination dieser Paradigmen bietet übrigens die Programmiersprache F#, die von Microsoft entwickelt wurde).
Betrachten wir zum Vergleich die Berechnung der Exponentialfunktion (für ganzzahlige Exponenten). Eine sehr naive Implementierung in C++ würde wie folgt aussehen:
long exp(long base, long exponent) { long result = 1; for(long i=0; i<exponent; ++i) { result *= base; } return result; }
Diese Formulierung mag zwar einfach sein, ist aber nicht besonders intuitiv. Die rekursive Definition erscheint eingängiger:
long exp(long base, long exponent) { if(exponent == 1) return base; else return exp(base, exponent-1) * base; }
Die Schleife wurde ersetzt durch einen rekursiven Aufruf der Funktion. Lediglich das verbliebene if verunreinigt die funktionale Definition. Betrachten wir das selbe Beispiel in der funktionalen Programmiersprache Erlang:
exp(B, 1) -> B; exp(B, E) -> exp(B, E-1) * B.
Hier wird ebenfalls eine Funktion exp definiert, allerdings wird diese für den zweiten Parameter “überladen”. Die in C++ notwendige if Abfrage wird dadurch in die Funktionsdefinition absorbiert (streng genommen ist die Abfrage E == 1 immer noch vorhanden, sie wird aber implizit vom Erlang Interpreter vorgenommen).
Weitgehend unbekannt unter den meisten C++ Programmierern ist die Tatsache, dass sich mit Hilfe von Templates funktionale Paradigmen abbilden lassen, die statische Code-Generierung und/oder Berechnungen zur Compile-Zeit zulassen. Die obige rekursive Definition der Exponentialfunktion lässt sich beispielsweise folgendermaßen implementieren:
template<int B, int E> struct Exp { enum { value = Exp<B, E-1>::value * B }; }; template<int B> struct Exp<B, 1> { enum { value = B }; };
Durch Verwendung von Exp<2, 4>::value im Code berechnet der Compiler rekursiv den Wert der Exponentialfunktion. Wichtig anzumerken ist allerdings, dass dieser Wert lediglich als Konstante verwendet werden kann, denn aufgrund des statischen Charakters von Templates ist eine dynamische Berechnung so nicht möglich.
Man mag sich nun fragen, welchen Mehrwert Template Metaprogramming bietet, wenn keine dynamischen Berechnungen damit möglich sind. In unserem Beispiel mag das auf den ersten Blick nicht deutlich werden, aber betrachten wir dazu das folgende Codefragment:
#define THREETOTHEPOWEROFSIX 729 #define FIVETOTHEPOWEROFSEVENTEEN 762939453125 long stupid_function(int a) { return a * pow(3, 6) * pow(5, 17); } long normal_function(int a) { return a * THREETOTHEPOWEROFSIX * FIVETOTHEPOWEROFSEVENTEEN; } long nice_function(int a) { return a * Exp<3, 6>::value * Exp<5, 17>::value; }
Welche Funktion ist performanter und leichter verständlich? Weitere interessante Betrachtungen des Template Metaprogramming folgen in einem der nächsten Artikel.