Aspekt-Orientierte Programmierung mit C++



Ähnliche Dokumente
Wintersemester Maschinenbau und Kunststofftechnik. Informatik. Tobias Wolf Seite 1 von 29

HSR Rapperswil 2001 Markus Rigling. Programmieren: Templates Auflage

C++ Quellcodes zum Buch Kapitel 5

C++ Templates - eine kleine Einführung. Allgemein. Funktionstemplates. Allgemein. Funktionstemplates. Klassentemplates

DAP2-Programmierpraktikum Einführung in C++ (Teil 2)

C++ Notnagel. Ziel, Inhalt. Programmieren in C++

Ein kleiner Blick auf die generische Programmierung

4. Objektorientierte Programmierung mit C++

4. Objektorientierte Programmierung mit C++

Programmierkurs C/C++

Problem: Vererbung erlaubt die Wiederverwendung d von Programmcode, virtuelle Funktionen ermöglichen dabei den Austausch gewisser Funktionalität

Programmieren 2 C++ Überblick

Polymorphe for-anweisung 237

Aufbau von Klassen. class punkt {...

Einführung in die Systemprogrammierung

7. Übung Informatik II - Objektorientierte Programmierung

1 Motivation und Einleitung

Objektorientierte Programmierung mit C Strukturierte Anweisungen: Exception Handling

C++ Templates - eine kleine Einführung. Funktionstemplates. Fabian Scheler, Peter Ulbrich, Niko Böhm. 20. Oktober 2008

Info-2, Klausurvorbereitung, SS 2016

C++ - Objektorientierte Programmierung Polymorphie

Generative Programmierung

Name: Klausur Informatik III WS 2003/04

3. Exkurs in weitere Arten der Programmierung

Programmierkurs. Steffen Müthing. January 18, Interdisciplinary Center for Scientific Computing, Heidelberg University

C++ Templates - eine kleine Einführung

Klausur: Grundlagen der Informatik I, am 06. Februar 2009 Gruppe: A Dirk Seeber, h_da, Fb Informatik. Nachname: Vorname: Matr.-Nr.

Generative Programmierung

Crashkurs C++ Wiederholung

spectj AOP mit Java, Konzepte und Beispiele

Die Anwesenheitspflicht beim Seminar gilt für diejenigen Teilnehmer, die einen Schein erwerben wollen. Für die Nachmittagsübungen gilt keine

initializer lists (nicht für Referenzen... unklar warum...)

Programmieren in C++ Überblick

Klausur: Grundlagen der Informatik I, am 27. März 2009 Gruppe: F Dirk Seeber, h_da, Fb Informatik. Nachname: Vorname: Matr.-Nr.

Grundlagen der Informatik

Innere Klassen. Innere Klassen. Page 1. Lernziele: innere Klassen, statische geschachtelte Klassen, anonyme Klassen.

Programmieren 2 C++ Überblick

Angewandte Mathematik in OOP WS 2011/12. Abschluss-Test

Programmieren in C++ Templates

Angewandte Mathematik und Programmierung

C++ - Einführung in die Programmiersprache Header-Dateien und Funktionen. Leibniz Universität IT Services Anja Aue

Objektorientierte Programmierung II

Programmierkurs C/C++

Thema heute: Vererbung und Klassenhierarchien. Abgeleitete Klassen. Vererbung von Daten und Funktionen. Virtuelle Funktionen

Informatik - Übungsstunde

Synthesizing Objects [1]

5. Behälter und Iteratoren. Programmieren in C++ Überblick. 5.1 Einleitung. Programmieren in C++ Überblick: 5. Behälter und Iteratoren

Kapitel 13. Abstrakte Methoden und Interfaces. Fachgebiet Knowledge Engineering Prof. Dr. Johannes Fürnkranz

Übung zur Vorlesung Wissenschaftliches Rechnen Sommersemester 2012 Auffrischung zur Programmierung in C++, 2. Teil

Synthesizing Objects [1]

Objektorientiertes Programmieren mit C++ für Fortgeschrittene

Objektorientierte Programmierung

Programmiersprache 1 (C++) Prof. Dr. Stefan Enderle NTA Isny

(Ausnahmebehandlung)

Klausur im WS 2003/04 : Informatik III

Metaprogrammierung 372

Schriftlicher Test (120 Minuten) VU Einführung ins Programmieren für TM. 25. Jänner 2016

4 Generische Programmierung. 4.1 Klassen-Templates (*) 4.2 Funktions-Templates (*) 4.3 Besondere Programmiertechniken (Smart Pointer)

variadic templates Templates mit variabler Argumentanzahl?

19. Vererbung und Polymorphie

Einführung in das Objektorientierte Programmieren mit C++

Praxis der Programmierung

Prof. Dr. Markus Gross Informatik I für D-ITET (WS 03/04)

19. Vererbung und Polymorphie

kurze Wiederholung class templates

Objektorientierte Programmierung mit C++ SS 2007

Informatik I (D-ITET)

7. Vererbung und Polymorphie

Realisierungsmöglichkeiten für parametrische Polymorphie

Programmierkurs. Steffen Müthing. December 7, Interdisciplinary Center for Scientific Computing, Heidelberg University

FOG. Thomas Grzenkowski.

Wie schreibe ich ein Powerbook

Mapra: C++ Teil 6. Felix Gruber, Sven Groß. IGPM, RWTH Aachen. 13. Juni 2017

Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung)

11 Vererbung und Klassenhierarchie

18. Vererbung und Polymorphie

Kapitel 11: Vererbung Ziele von Klassen Einführung in die Informatik für struct Naturwissenschaftler und Ingenieure

18. Vererbung und Polymorphie

C++ - Objektorientierte Programmierung Konstruktoren und Destruktoren

Klausurvorbereitung Lösung

Zusammenfassung. Mit bjam und ein paar Zeilen in einem Jamroot oder Jamfile lässt sich das Kompilieren und Linken einfach automatisieren

Kapitel 5: Interfaces

Klausur: Informatik, am 11. Juli 2013 Gruppe: B Dirk Seeber, h_da, Fb Informatik. Nachname: Vorname: Matr.-Nr.: Punkte:

Coding Conventions. for C++ Reto Carrara Gyrenstrasse 17 CH-8967 Widen +4179/

Aufgabenblatt 11 Musterlösung

Objektorientierte Programmierung mit C++ den sog. default constructor X (){}

OOP und Angewandte Mathematik. Eine Einführung in die Anwendung objektorientierter Konzepte in der angewandten Mathematik

Was du ererbt von Deinen Vätern hast, erwirb es, um es zu besitzen. J. W. v. Goethe.

Programmierung und Angewandte Mathematik

Modernes C++ Compilation Firewall Exception-Safe Function Calls. Philippe Lieser

HSR Rapperswil 2001 Markus Rigling. Programmieren: Smart Pointer Auflage

Objektorientierte Programmierung. Kapitel 22: Aufzählungstypen (Enumeration Types)

Kapitel 9. Programmierkurs. Attribute von Klassen, Methoden und Variablen. 9.1 Attribute von Klassen, Methoden und Variablen

Einführung in C# Teil 3. Matthias Nübling

C++ Klassen, Vererbung. Philipp Lucas. Sebastian Hack. Wintersemester 2008/09. saarland.

Neben der Verwendung von Klassen ist Vererbung ein wichtiges Merkmal objektorientierter

Lösung der OOP-Prüfung WS12/13

Transkript:

Aspekt-Orientierte Programmierung mit C++ Georg Blaschke (georg.blaschke@stud.informatik.uni-erlangen.de) Friedrich-Alexander-Universität Erlangen-Nürnberg Lehrstuhl für Betriebssysteme und Verteilte Systeme 17. Juni 2003 Um aspekt-orientierte Programmierung zu betreiben, ist es nicht unbedingt nötig spezielle Compiler einzusetzen oder den fachlichen Code mit dem Aspektcode vor der Übersetung mit Hilfe eines zusätzlichen Programms zusammenzuführen. Mit einem herkömmlichen C++ Compiler (GNU,VCC) ist es möglich, AOP in einem Projekt zu verwenden. 1 Einleitung und Motivation Dieses Dokument beruht zum größten Teil auf einen dreiteiligen Bericht in der Zeitschrift ix[1], der eine Untersuchung der Programmiersprache C++ hinsichtlich ihrer Eignung für aspekt-orientiertes Programmieren beschreibt. Als Rahmenbedingungen gelten, dass aspekt-orientierter Code in Standard- C++ geschrieben ist, ohne zusätzliche Software (wie z.b. ein Präprozessor) auskommt, auf existierenden Quellcode anwendbar ist und sich mit GNU C++ 2.95.2 bzw. V++ 6.0 übersetzen läßt. Ausserdem soll der vorhandene Quellcode möglichst keinen Änderungen unterworfen sein. Anhand von Beispielen werden nun einige Möglichkeiten aufgezeigt, Aspektcode mit fachlichen Code zu verküpfen. Dieser gewobene Code findet dann seine Anwendung im Clientcode, also in einer konkreten Anwendung. Abbildung 1 verdeutlicht die Benutzt-Beziehung zwischen den verschiedenen Arten von Code. Aspektcode <<benutzt>> fachlicher Code <<benutzt>> gewobener Code <<benutzt>> Clientcode Abbildung 1: Beziehung zwischen verschiedenen Code-Arten 2 Basistechniken Anhand der einfachen Implementierung eines Stacks (Listing 1) werden verschiedene Methoden erläutert, den Tracing-Aspekt auf eine C++-Klasse anzuwenden. class IntStack IntStack() : size (0) void push(const int& i) elements [ size ++] = i; Listing 1: IntStack 1

; int pop() return elements [ size ]; private: enum max size = 3 ; int elements [ max size ]; int size ; 2.1 Manipulation der Quellcodes Die wohl simpelste Lösung, Aspekte auf Fachcode anzuwenden, ist, den Aspektcode direkt in den Fachcode hineinzuschreiben. Zu diesem Zweck wird eine Helferklasse implementiert, deren Konstruktor und Destruktor den Text Before bzw. After und den Übergedenen Funktionsnamen ausgeben. Der Grund, weswegen für eine Zeichenaugabe eine ganze Klasse implementiert wird, liegt darin, dass es in C/C++ nicht möglich ist, Code anzugeben, der nach der Beendigung einer Funktion ausgeführt werden soll. Durch die Verwendung der Helferklasse ReportTracing wird jedoch genau dies erreicht, da die Instanz der Klasse erst nach Ausführung des Funktionsrumpfes zerstört wird, somit also auch ihr Destruktor mit Tracing-Textausgabe ausgeführt wird. Listing 2: Report Tracing class ReportTracing // Hilfsklasse zur Implementierung des Tracing Aspekts ReportTracing(const char function): function ( function ) cout << Before << function << endl; ReportTracing() cout << After << function << endl; private: const char function ; ; class IntStack IntStack() : size (0) ReportTracing t( constructor ); void push(const int& i) elements [ size ++] = i; return; int pop() return elements [ size ]; private: enum max size = 3 ; int elements [ max size ]; int size ; ; 2

Der grobe Eingriff in den Quellcode verstößt natürlich gegen eine der Voraussetzung, und hat eigentlich nichts mit AOP zu tun, da der Aspekt nicht zentral gekapselt ist, sondern in an mehreren Stellen den fachspezifischen Code durchzieht. Dies ist in Listing 1 durch einen Unterstrich gekennzeichnet. 2.2 Komposition mit Weiterleitung Um einen Aspekt seperat vom Fachcode zu implementieren, kann man dessen Code in eine eigene Klasse schreiben, die alle Member-Funktionen der Fachklasse implementiert und den Aufruf an die jeweilige Methode der Fachklasse weiterleitet. class IntStackWithTracing IntStackWithTracing() ReportTracing t( constructor ); mystack = new IntStack(); void push(const int& i) mystack >push(i); int pop() return mystack >pop(); private: IntStack mystack; ; Listing 3: Komposition mit Weiterleitung Die Komposition von Fach- und Aspektcode unter Verwendung von Funktionsaufruf-Weiterleitung hat den Nachteil, dass alle Methoden der Fachklasse implementiert werden müssen. Ausserdem muss der Programmierer jedesmal wenn eine neue Funktion dem Fachcode hinzugefügt wird, auch den Aspektcode entsprechend erweitern. 2.3 Komposition mit Vererbung Um den Nachteilen zu begegnen, die durch Verwendung von Weiterleitung der Funktionsaufrufe entstehen, wird nun Vererbung eingesetzt. Somit kann die Fachklasse nach Belieben erweitert werden und die Aspektklasse muss nicht alle Methoden von ihr implementieren. class IntStackWithTracing: public IntStack IntStackWithTracing() ReportTracing t( constructor ); void push(const int& i) IntStack :: push(i); int pop() Listing 4: Komposition mit Vererbung 3

; return IntStack :: pop(); Der Nachteil dieser Art der Komposition ist, dass es nicht ohne weiteren Aufwand möglich ist Code zu spezifizieren der vor einem Konstruktor ausgeführt wird. Bei der Instanziierung der Klasse IntStackWithTracing aus Listing 4 wird die Ausgabe Before constructor erst erscheinen, wenn der Konstruktor der Eltern-Klasse (hier: IntStack) abgearbeitet ist. Eine Lösung zu diesem Problem wird später noch vorgestellt. 2.4 Verwendung von Namesräumen Um Aspekte anzuwenden musste bisher der Clientcode so angepasst werden, dass er die Klasse instanziiert, die genau diesen Aspekt implementiert; z.b IntStackWithTracing. Diese Verfahrensweise widerspricht jedoch der Voraussetzung, dass kein Quellcode verändert werden darf, also auch nicht der Code der Anwendung. Um den Clientcode nicht ändern zu müssen, muss die Aspektklasse den selben Namen besitzten wie die Fachklasse, wodurch jedoch ein Namenskonflikt auftritt. Gelöst wird dieser Konflikt indem geringfügige Änderungen am Fachcode zugelassen werden, so dass dieser in einem eigenen Namensraum (namespace original) definiert ist. Aspektklassen und dazugehörige Helferklassen befinden analog im namespace aspects. Der Clientcode importiert mittels using namespace composed den Namensraum, in dem die Klasse IntStack definiert ist; entweder mit Aspekten oder ohne. Um einen Aspekt mit hinzuzunehmen, oder wegzulassen, muss man nur im Namensraum composed den Typ IntStack entsprechend definieren. namespace original namespace aspects //Klasse Report Tracing wie in Listing 2 typedef original :: IntStack IntStack ; class IntStackWithTracing: public IntStack IntStackWithTracing() ReportTracing t( constructor ); void push(const int& i) IntStack :: push(i); int pop() return IntStack :: pop(); ; Listing 5: Verwendung von Namensräumen namespace composed // typedef aspects :: IntStack IntStack ; // pure IntStack // typedef aspects :: IntStackWithChecking Stack; // IntStack with Checking (not shown here) typedef aspects :: IntStackWithTracing IntStack ; // Stack with Tracing 4

3 Zwischenstand Die Techniken die bisher gezeigt wurden (Helferklasse, Vererbung, Namensräume) ermöglichen es, einzelne Aspekte auf Klassen (und auch Klassentemplates) anzuwenden. Der momentane Stand erlaubt jedoch noch nicht Aspekte mit mehrere Klassen oder mehrere Aspekte mit einer Klasse zu verküpfen. Ausserdem stellt sich noch die Frage wie man Aspektcode angibt, der vor einem Konstruktor oder nach einem Destruktor ausgeführt werden soll. Techniken für die angesprochnen Probleme werden im Folgenden vorgestellt. 4 Techniken zur Lösung spezieller Probleme 4.1 Ausführung von Code vor einem Konstruktor oder nach einem Destruktor Bei der Kompostition mit Vererbung (siehe Kapitel 2.3) hat man die Einschränkung, keinen Code spezifizieren zu können, der vor dem Konstruktor oder nach dem Destruktor der Fachklasse ausgeführt werden soll. Um diese Limitierung zu beseitigen, werden zwei weitere Hilfsklassen BeforeConstructor und AfterConstructor eingeführt. Die Aspektklasse IntStackWithTracing wird nun von den Klassen BeforeConstructor, AfterConstructor und IntStack abgeleitet; und zwar in genau diese Reihenfolge! So wird bei einer Instanzierung der Aspektklasse zuerst der Konstruktor von BeforeConstructor, dann der Konstruktor der Fachklasse und zuletzt der Konstruktor der Aspektklasse abgearbeitet. Ein ähnliches Verhalten für die Destruktoren findet man bei der Zerstörung einer Instanz der Aspektklasse. namespace original namespace aspects typedef original :: IntStack IntStack ; class BeforeConstructor BeforeConstructor() cout << Before constructor << endl; ; Listing 6: Konstruktoren und Destruktoren struct AfterDestructor AfterDestructor () cout << After destructor << endl; ; class IntStackWithTracing: private BeforeConstructor, private AfterDestructor, public IntStack Tracing() cout << After constructor << endl; void push(const int& i) IntStack :: push(i); valuetype pop() return IntStack :: pop(); 5

; Tracing() cout << Before destructor << endl; Die private-vererbung von BeforeConstructor und AfterConstructor dient dazu, die Verwendung von Methoden der Hilfsklassen in anderen Klassen, die eventuell von IntStack abgeleitet werden, zu verhindern. 4.2 Anwendung von einem Aspekt auf mehrere Klassen Wenn man einen Aspekt mit mehreren Fachklassen verweben will, darf man den Aspektcode nicht abhängig von einer Fachklassen machen, so wie es bisher geschah. Um den Aspektcode flexibler zu gestalten, besteht die Möglichkeit Aspekte in Form von Klassen-Templates zu realisieren. Wie in Listing 7 zu sehen ist, wird die Aspektklasse von der Klasse abgeleitet, die ihr als Template- Parameter übergeben wurde. Probleme treten nun bei den Funktionen auf, deren Paramerter- oder Rückgabetypen von der konkreten Fachklasse abhängen; in unseren Fall double oder int. Mit Traits Templates ist es jedoch möglich den Typ zur Übersetzungszeit herauszufinden. Traits Classes und Member Traits erscheinen hier nicht als geeignet, da man hierfür Code der Fachklasse verändern müsste. Mehr zu Thema Metainformation findet man in [1], [3] und in einschlägiger Fachliteratur. Für jeden verwendeten Stack-Typ wird im Namensraum aspects ein spezialisiertes Klassen-Template von ValueType angelegt, welches die Information über den entsprechenden Stack-Typ beinhaltet. namespace original class DoubleStack // gleicher Aufbau wie IntStack namespace aspects template <class T> struct ValueType ; template<> struct ValueType<original :: IntStack> typedef int RET; ; template<> struct ValueType<original :: DoubleStack> typedef double RET; ; Listing 7: flexibler Aspektcode // ReportTracing wie in Listing 2 template <class Base> class Tracing : public Base typedef typename ValueType<Base>::RET valuetype; void push(const valuetype& i) 6

Base::push(i); valuetype pop() return Base::pop(); namespace composed // typedef original :: IntStack IntStack ; typedef aspects :: Tracing< original :: IntStack> IntStack; typedef original :: DoubleStack DoubleStack; //typedef aspects :: Tracing< original :: DoubleStack> DoubleStack; 4.3 Verweben von mehreren Aspekten mit mehreren Klassen Bisher wurde zu einer Fachklasse immer nur ein Aspekt hinzugefügt. Nun wird eine Möglichkeit vorgestellt, mehrere Aspekte mit einer Klasse zu verküpfen; in unserem Beispiel (Listing 8) werden die Aspekte Checking und/oder Tracing den Fachklassen DoubleStack und/oder IntStack hinzugefügt. Wie in Kapitel 4.2 werden die Aspekte als Klassen-Templates implementiert. Wenn zwei Aspekte auf eine Klasse angewendet werden sollen, hat man bei der zweiten Anwendung das Problem, dass für die übergebene Klasse keine Typ-Information vorhanden ist. Eine Lösung wäre somit für alle vorkommenden Verküpfungs-Kombinationen(IntStack, IntStack+Tracing, IntStack+Checking, IntStack+Checking+Tracing, IntStack+Tracing+Checking, DoubleStack,...) die Typ-Information bereit zu stellen, was jedoch schnell ausufern würde. Eine bessere Möglichkeit bietet der inheritance detector der zur Übersetzungszeit Vererbungsbeziehungen zwischen zwei Klassen feststellt. Mit dem inheritance detector und der Meta-Kontrollstruktur IF kann nun die richtige Typ-Information (ValueTyp) ermittelt werden. Nähere Informationen zum Thema Meta-Funktionen findet man in [3]. Jede Fachklasse, auf die irgendein Aspekt angewendet werden soll, muss mit der Klasse, die der Aspektklasse als Template-Parameter übergeben wurde, bezüglich ihrer Vererbungsbeziehung hin überprüft werden. Fällt dieser Test positiv aus, wird die Typ-Information der positiv getesten Fachklasse zurückgegeben. namespace original class DoubleStack // gleicher Aufbau wie IntStack namespace aspects // ValueType wie in Listing 7 Listing 8: Weben mehrerer Aspekte // inheritance detector : siehe naechsten kommentar template<class Base> struct InheritDetector typedef char (&no)[1]; typedef char (&yes)[2]; static yes test ( Base ); static no test (... ) ; 7

; // inheritance detector : ermittelt ob die Klasse Derived von Base abgeleitet ist template<class Derived, class Base> struct Inherits typedef Derived DP; enum RET = sizeof ( InheritDetector <Base>::test(DP()) ) == sizeof ( InheritDetector <Base>::yes ) ; ; // meta IF struct SelectThen template<class Then, class Else> struct Result typedef Then RET; ; ; //meta IF struct SelectElse template<class Then, class Else> struct Result typedef Else RET; ; ; //meta IF template<bool condition> struct Selector typedef SelectThen RET; ; //meta IF template<> struct Selector <false> typedef SelectElse RET; ; //meta IF template<bool condition, class Then, class Else> struct IF typedef typename Selector<condition>::RET Selector ; typedef typename Selector ::Result<Then,Else>::RET RET; ; // Ermittelt Typ Information der Klasse T template <class T> struct GetType 8

typedef typename IF<( Inherits <T, original :: IntStack>::RET), ValueType<original :: IntStack>::RET, typename IF<(Inherits<T,original::DoubleStack>::RET), ValueType<original :: DoubleStack>::RET, void >::RET >::RET RET; ; // ReportTracing wie in Listing 2 //Tracing Aspekt template <class Base> class Tracing : public Base typedef typename GetType<Base>::RET valuetype; void push(const valuetype& i) Base::push(i); ; valuetype pop() return Base::pop(); // Hilsklasse fuer den Checking Aspekt class CheckingException: public exception CheckingException(const char msg):msg (msg) const char what() const throw() return msg ; private: const char msg ; ; //Checking Aspekt template <class Base> class Checking: public Base typedef typename GetType<Base>::RET valuetype; void push(const valuetype& i) if ( size () == capacity()) throw CheckingException( stack overflow ); Base::push(i); valuetype pop() 9

; if ( size () <= 0) throw CheckingException( stack underrun ); Base::pop(); namespace composed // typedef original :: IntStack IntStack ; // typedef aspects :: Tracing< original :: IntStack> IntStack; // typedef aspects :: Checking<original :: IntStack> IntStack; // typedef aspects :: Checking<aspects::Tracing< original :: IntStack> > IntStack; typedef aspects :: Tracing<aspects::Checking<original :: IntStack> > IntStack; // typedef original :: DoubleStack DoubleStack; // typedef aspects :: Tracing< original :: DoubleStack> DoubleStack; // typedef aspects :: Checking<original :: DoubleStack> DoubleStack; // typedef aspects :: Checking<aspects::Tracing< original :: DoubleStack> > DoubleStack; typedef aspects :: Tracing<aspects::Checking<original :: DoubleStack> > DoubleStack; 5 Zusammenfassung Viele anschauliche Beispiele haben gezeigt, dass Aspekt-orientierte Programmierung mit reinem C++ unter den geforderten Bedingungen durchaus möglich ist. Jedoch ist dieser Ansatz eher zu vergleichen mit Objekt-orientierter Programmierung in der Programmiersprache C, welcher einfach die Sprachmittel fehlen um OO-Konzepte übersichtlich und effizient zu realisieren. Nun muss man sich die Frage stellen welche Sprachmittel nötig sind um effizient AOP zu betreiben. Mit AspectJ ist ja schon ein großer Schritt unternommen worden genau diese Sprachmittel zu identifizieren. Daran müssen sich jetzt andere Konzepte messen, was keine leichte Aufgabe darstellt. Literatur [1] Ulrich W. Eisenecker, Lutz Dominik, Krzysztof Czarnecki, Aspektorierte Programmierung in C++, ix, Ausgabe 8/01 bis 10/01, Heise-Verlag, 2001. [2] Gregor Kiczales, et al., Aspect-Oriented Programming, Springer-Verlag LNCS 1241, 1997. [3] Ulrich W. Eisenecker, Krzysztof Czarnecki, Template-Metaprogrammierung. Eine Einführung, OBJEKTspektrum, Ausgabe Mai/Juni, 1998. 10