12. The Barton & Nackmann Trick

Ähnliche Dokumente
Codingstandard. Softwareentwicklung Praktikum Stand:

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

13. Vererbung. Prof. Dr. François E. Cellier Informatik I für D-ITET (HS 2012)

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

Methoden. von Objekten definiert werden, Methoden,, Zugriffsmethoden und Read-Only

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

C++ Kurs Teil 1. Architektur, Anwendungsspektrum, primitive Datentypen, Funktionsaufrufe, Referenzen, Klassen

Einführung in die Programmierung

Prinzipien Objektorientierter Programmierung

Alltagsnotizen eines Softwareentwicklers

Der C++ Crashkurs v1.0

C++ - Operatoren. Eigene Klassen mit neuen Funktionen

1. Übung zu "Numerik partieller Differentialgleichungen"

11. Klassen. Prof. Dr. François E. Cellier Informatik I für D-ITET (HS 2012)

Java Einführung Abstrakte Klassen und Interfaces

1 Polymorphie (Vielgestaltigkeit)

MATLAB driver for Spectrum boards

Ziel, Inhalt. Programmieren in C++ Wir lernen wie man Funktionen oder Klassen einmal schreibt, so dass sie für verschiedene Datentypen verwendbar sind

Programmieren in C. Macros, Funktionen und modulare Programmstruktur. Prof. Dr. Nikolaus Wulff

Fortgeschrittene C++-Programmierung

Einführung in C++ Operatoren überladen (Klassen)

DOWNLOAD. Englisch in Bewegung. Spiele für den Englischunterricht. Britta Buschmann. Downloadauszug aus dem Originaltitel:

Objects First With Java A Practical Introduction Using BlueJ. Mehr über Vererbung. Exploring polymorphism 1.0

11: Echtzeitbetriebssystem ucos-ii

Programmieren 3 C++ Prof. Peter Sommerlad Fredy Ulmer

HIR Method & Tools for Fit Gap analysis

C++ - Funktionen und mehr. Kerstin Gößner und Ralf Wondratschek

Übung 1 mit C# 6.0 MATTHIAS RONCORONI

Beispiel 2a Die eigenen ersten Schritte mit dem Gnu-Debugger GDB für Remote-Debugging

Technische Infor matik 2 C/C++-Kurs. Datenabstraktion (Klassen) 2005 AG Rechner netze 4.1

Angewandte Mathematik und Programmierung

Programmentwicklung ohne BlueJ

Algorithmen und Datenstrukturen

NEWSLETTER. FileDirector Version 2.5 Novelties. Filing system designer. Filing system in WinClient

Objektorientierte Programmierung mit C++ Vector und List

Probeklausur: Programmierung WS04/05

Klassen in Java. Klassen

Patentrelevante Aspekte der GPLv2/LGPLv2

Vererbung & Schnittstellen in C#

Employment and Salary Verification in the Internet (PA-PA-US)

1. Grundlegende Eigenscha5en 2. Redefini+on 3. Polymophie 4. Mehrfachvererbung

Selbststudium OOP Programmieren 1 - H1103 Felix Rohrer

The Art of Unit Testing

2.0. Jens Weller Joel de Guzman Hartmut Kaiser

Invitation - Benutzerhandbuch. User Manual. User Manual. I. Deutsch Produktübersicht Beschreibung... 2

ROOT Tutorial für D. Liko

Interaktive Simulationen Lektion 1/3: Event-Driven Design und Signals

p^db=`oj===pìééçêíáåñçêã~íáçå=

Meeting C++ C++11 R-Value Referenzen

Programmieren in Java

Der Goopax Compiler GPU-Programmierung in C++ ZKI AK-Supercomputing, Münster, , Ingo Josopait

Objektbasierte Entwicklung

3 Objektorientierte Konzepte in Java

Klassenbeziehungen & Vererbung

Javadoc. Programmiermethodik. Eva Zangerle Universität Innsbruck

Einführung in die Programmierung Wintersemester 2011/12

CORBA Implementierung von Client und Server

Objektorientierung: Klassen und Objekte

Klausur zu Objektorientierter Softwareentwicklung in C++ 4. Februar 2003 (WS 2002/2003) Beispiellösung

Objektorientierte Programmierung mit C++ SS 2007

Praktikum Betriebssysteme 1. Aufgabe (1)

Die LogTrace-Bibliothek

Z:\Informatik 3\Labor_GINF3\Labor_01-Sonstiges.txt

Übung zur Vorlesung Programmieren in C

Objektorientiertes Programmieren für Ingenieure

C++-Zusammenfassung. H. Schaudt. August 18, 2005

Dienstspezifikation nach RFC

WAS IST DER KOMPARATIV: = The comparative

p^db=`oj===pìééçêíáåñçêã~íáçå=

Einkommensaufbau mit FFI:

VIII: Vererbung. Unterklassen einer Klasse. Vererbung von Methoden und Instanzvariablen. Überschreiben von Methoden

Klassendefinitionen verstehen

Objektorientierte Programmierung mit C++ SS 2007

Funktionen und Parameter

Programmieren - Vererbung & Polymorphie

Vorkurs C++ Programmierung

Distributed Computing Group

Aber genau deshalb möchte ich Ihre Aufmehrsamkeit darauf lenken und Sie dazu animieren, der Eventualität durch geeignete Gegenmaßnahmen zu begegnen.

Algorithms for graph visualization

Einführung in C++ mit Microsoft VS

Algorithms & Datastructures Midterm Test 1

1. General information Login Home Current applications... 3

Besonderheiten von C#

Grundlagen C und C++ Einheit 03: Grundlagen in C++ Lorenz Schauer Lehrstuhl für Mobile und Verteilte Systeme

Kapitel 6. Vererbung

CABLE TESTER. Manual DN-14003

Einführung in die C-Programmierung

Session 1: Classes and Applets

Wählen Sie das MySQL Symbol und erstellen Sie eine Datenbank und einen dazugehörigen User.

ONLINE LICENCE GENERATOR

Programmieren in C. Operatoren, Variablen und deren Sichtbarkeit. Prof. Dr. Nikolaus Wulff

Fakultät Angewandte Informatik Lehrprofessur für Informatik

Kapitel 6. Vererbung

C A R L V O N O S S I E T Z K Y. Boost C++ Libraries. Johannes Diemke. Department of Computer Science Learning and Cognitive Systems

Javakurs 2013 Objektorientierung

39 Object Request Brokers. 40 Components of an ORB Stubs and Skeletons Stub

Vererbung. Vererbung von Methoden und Instanzvariablen. Vererbung als Realisierung einer is-a Beziehung.

C++ Programmierung. Uwe Naumann. LuFG Informatik 12, RWTH Aachen University, Germany

GetName(), GetName(), GetGeschlecht() und AelterWerden().

Java Kurs für Anfänger Einheit 5 Methoden

Transkript:

12. The Barton & Nackmann Trick Mit der Verwendung parametrisierter Basisklassen wird (Policy-) Funktionalität in der Ableitung verfügbar. Kann auch die Template- Basisklasse etwas (alles?) über die Ableitung erfahren? YES: The Curiously Recurring Template Pattern (erstmals veröffentlicht im Buch Scientific and Engineering in C++ der beiden Autoren) template <typename Derived> class CuriousBase {... class Curious: public CuriousBase<Curious> { //! 98

12. The Barton & Nackmann Trick Beispiel: universelle Objektzählung #ifndef OBJECTCOUNTER_H #define OBJECTCOUNTER_H template <typename CountedType> class ObjectCounter { static size_t count; // number of existing objects protected: ObjectCounter() { ++count; } ObjectCounter (ObjectCounter<CountedType> const&) { ++count; } ~ObjectCounter() { --count; } public: // return number of existing objects: static size_t live() { return count; } 99

12. The Barton & Nackmann Trick // initialize counter with zero template <typename CountedType> size_t ObjectCounter<CountedType>::count = 0; #endif Was ist zu zählen? // Anwendungsbeispiel: #include "ObjectCounter.h" #include <iostream> template <typename CharT> class MyString : public ObjectCounter<MyString<CharT> > { void count() { usind std::cout; using std::endl; cout<<"number of MyString<char>: "<<MyString<char>::live()<<endl; cout<<"number of MyString<wchar_t>: "<<MyString<wchar_t>::live()<<endl; } 100

12. The Barton & Nackmann Trick int main() { { MyString<char> s1, s2; MyString<wchar_t> ws; } count(); 101

Ziel: flexible Implementation des Command-Patterns Generalized functor: any processing invocation that C++ allows, encapsulated as a typesafe first-class object. Entkopplung von Command-Invocator und Command-Receiver: (interface separation, time separation) Lokal: der Invoker kennt u.u. den Receiver gar nicht und umgekehrt Temporal: die Konfiguration der Aktion kann (lange) vor dem Aufruf stattfinden Modal: es ist dem Invoker nicht bekannt, wie die Aktion erbracht wird 102

// feste Kopplung: window.resize(0, 0, 200, 100); // Command pattern: Command resizecmd( window, // the object &Window::Resize, // the action 0, 0, 200, 100); // arguments... // later on: resizecmd.execute(); 103

C++ callable Entities was man alles in C++ aufrufen kann ( quasi operator () ): a. C like Funktionen b. C-like Zeiger auf Funktionen c. Referenzen auf Funktionen (de facto const Zeiger auf F.) d. Functors (Objekte von Klassen mit operator () ) e. Das Ergebnis der Anwendung von operator.* bzw. ->* mit einem Zeiger auf eine Memberfunktion als rechte Seite Wunsch: das Command-Pattern soll alle Fälle abdecken Übliche Idee (Basisklasse mit Ableitungen für Varianten) scheitert 104

C++ callable Entities void foo(); struct X { static void foo(); void operator()(); void bar(); } f; void (*pf)() = & foo; void (&rf)() = foo; void (*psf)() = &X::foo; void (X::*pM)() = &X::bar; X* p = &f; foo(); // a. (*pf)(); // b. (*psf)(); // b.! rf(); // c. f(); // d. (f.*pm)(); // e. (p->*pm)(); // e. 105

Einschub: const T& oder T const &? [http://stackoverflow.com/questions/2640446/why-do-some-people-prefer-t-const-over-const-t]: Eine Frage des Geschmacks? one reason for choosing between the two is whether you want to read it like the compiler (right-to-left) or like English (left-to-right). If one reads it like the compiler, then "T const&" reads as "& (reference) const (to a constant) T (of type T)". If one reads it like English, from left-to-right, then "const T&" is read as "a constant object of type T in the form of a reference. Nicht nur! This will make a difference when you have more then one const/volatile modifiers. Then putting it to the left of the type is still valid but will break the consistency of the whole declaratiion. For example: T const * const *p; means that p is a pointer to const pointer to const T and you consistenly read from right to left. const T * const *p; means the same but the consistency is lost and you have to remember that leftmost const/ volatile is bound to T alone and not T *. 106

Interessant: es wäre falsch zu sagen, Callable Entities sind Objekte von Typen, für die operator() definiert ist! f.*pm und p->*pm haben keinen C++ Typ! Sofern das Command-Pattern selbst operator() definiert (als Execute() ), kann man Kommandos in Kommandos schachteln! (GoF: Makro-Command-Pattern) Problem: sämtliche Zeiger haben keine first-class Semantik: beim Kopieren muss man Eigentümer-Belange und Polymorphie beachten Lösungsidee: handle-body-idiom, [piml-idiom]: Functor + FunctorImpl 107

// 1. Versuch: class Functor { public: void operator()(); // only void??? // other members private: // implementation goes here // 2. Versuch: variables Resultat: template <typename ResultType> class Functor { public: ResultType operator()(); // any result!!! // other members private: // implementation goes here 108

Argumente können in beliebiger Anzahl und mit beliebigen Typen übergeben werden --- es gibt aber keine (Anzahl-)variablen Template- Parameter in C++98 (wohl aber in C++0x ) // 2 ½. Versuch: variable Argumente template <typename ResultType> class Functor {... template <typename ResultType, typename Param1> class Functor {......Param1, Param2,... ParamN // N ~ 12 15 // is not allowed in C++98 109

Typlisten lösen das Problem: // 3. Versuch: variable Argumente template <typename ResultType, class TList> class Functor {... // zum Beispiel: Functor<double, TYPELIST_2(int, double)> myfunctor; Die Implementation muss alle Fälle explizit behandeln, bis 15 in loki fertig vorbereitet 110

Wieder mal Template specialization template <typename R, class TList> class FunctorImpl; template <typename R> class FunctorImpl<R, NullType> { public: virtual R operator ()() = 0; virtual FunctorImpl* Clone() const = 0; virtual ~FunctorImpl() {} 111

template <typename R, typename P1> class FunctorImpl<R, TYPELIST_1(P1)> { public: virtual R operator ()(P1) = 0; virtual FunctorImpl* Clone() const = 0; virtual ~FunctorImpl() {} template <typename R, typename P1, typename P2> class FunctorImpl<R, TYPELIST_2(P1, P2)> { public: virtual R operator ()(P1, P2) = 0; virtual FunctorImpl* Clone() const = 0; virtual ~FunctorImpl() {}... 112

Functor ist dann klassisches handle-body idiom: template <typename R, class TList> class Functor { public: Functor(); // artifacts that Functor(const Functor&); // prove value Functor& operator= (const Functor&); // semantic explicit Functor(std::auto_ptr<Impl> spimpl);... // extension constructor private: typedef FunctorImpl<R, TList> Impl; std::auto_ptr<impl> spimpl_; 113

Parametertypnamen: template <typename R, class TList> class Functor { // as above + typedef TList ParamList; typedef typename TypeAtNonStrict<TList, 0, EmptyType>::Result Parm1; typedef typename TypeAtNonStrict<TList, 1, EmptyType>::Result Parm2;... // TypeAtNonStrict == TypeAt mit explizitem Typ // (3. Parameter) falls index out of range 114

Muss Functor ebenfalls für alle Parameteranzahlen spezialisiert werden (wie FunctorImpl)? NEIN: genialer Trick implementiere alle, aber nur eine Version wird verwendet und auch nur diese ist überhaupt korrekt! template <typename R, class TList = NullType> class Functor {// as above + public: R operator()() { return (*spimpl_)(); } R operator()(parm1 p1) { return (*spimpl_)(p1); } R operator()(parm1 p1, Parm2 p2) { return (*spimpl_)(p1, p2); }... // up to 15 prepared 115

Ein Beispiel: // define a Functor for int x double -> double Functor<double, TYPELIST_2(int, double)> func; // use it correctly: double result = func(4, 5.6); // wrong usage: double result = func(); // operator ()() not found, weil // FunctorImpl<double, TYPELIST_2(int, double)> // diesen nicht implementiert, sondern? 116

Konkrete FunctorImpl Implementationen: (wobei wir beliebige callable entities hinterlegen wollen!) Zuerst functors (callables type d.-- Instanzen von Klassen mit op() ) (Functor ist selbst ein functor!): template <typename R, class TList> class Functor { // as above + public: template <class Fun> // member template Functor(const Fun& fun); 117

Dazu wird eine Ableitung von FunctorImpl<R, Tlist> benutzt, die ein Fun aufbewahrt und op() an diesen weiterleitet, der Trick von oben wird erneut benutzt, um alle Signaturen abzudecken: template <class ParentFunctor, typename Fun> class FunctorHandler: public FunctorImpl < typename ParentFunctor::ResultType, typename ParentFunctor::ParmList > { public: typedef typename ParentFunctor::ResultType ResultType; FunctorHandler(const Fun& fun): fun_(fun){} FunctorHandler* Clone() const { return new FunctorHandler(*this); }... 118

ResultType operator()() { return fun_(); } ResultType operator() ( typename ParentFunctor::Parm1 p1 ) { return fun_(p1); } ResultType operator() ( typename ParentFunctor::Parm1 p1, typename ParentFunctor::Parm2 p2, ) { return fun_(p1, p2); }... private: Fun fun_; 119

Nun kann der member template Konstruktor trivial implementiert werden: template <typename R, class TList> template <class Fun> // member template Functor<R, Tlist>::Functor(const Fun& fun) : spimpl_(new FunctorHandler<Functor, Fun>(fun) {} 120

// Test drive: #include Functor.h #include <iostream> using namespace std; using namespace loki; struct TestFunctor { void operator()(int i, double d) { cout << TestFunctor::operator()( << i <<, << d << ) called.\n ; } int main() { TestFunctor f; Functor<void, TYPELIST_2(int, double)> cmd(f); cmd(4, 4.5); } 121

Weitere konkrete FunctorImpl Implementationen: (wobei wir weitere callable entities hinterlegen wollen!) Warum haben wir mit den functors begonnen und nicht mit den (vermeintlich) leichteren callable entities type a., b., c.??? Überraschung: Alles ist schon implementiert! void TestFunction(int i, double d) { cout << TestFunction::( << i <<, << d << ) called.\n ; } int main() { Functor<void, TYPELIST_2(int, double)> cmd(testfunction); cmd(4, 4.5); } 122

Template parameter deduction macht s möglich: TestFunction als Parameter passt nur auf member template Konstruktor, also wird dieser mit void (&)(int, double) instantiiert Der Typ von fun_ im FunctorHandler<Functor< >, void(&)(int, double)> ist demzufolge ebenfalls void (&)(int, double) 123

Der Aufruf von operator() am FunctorHandler<Functor< >, void(&)(int, double)> Wird an fun_() weitergereicht, which is legal syntax for invoking a function through a pointer [reference] to function. Es bleibt ein Problem ( It couldn t be perfect, could it? ): Wenn TestFunction überladen wird, kann man den Typ des Symbols nicht mehr (ohne Aufruf) ermitteln. Auswege: (typisierte) Initialisierung oder Cast 124

// as above TestFunction overloaded with: void TestFunction(int); // another one int main() { // ambiguous: // Functor<void, TYPELIST_2(int, double)> cmd(testfunction); // but: typedef void (*TpFun)(int, double); TpFun pf = TestFunction; // better &TestFunction Functor<void, TYPELIST_2(int, double)> cmd1(pf); cmd1(4, 5.6); Functor<void, TYPELIST_2(int, double)> cmd2(static_cast<tpfun>(testfunction)); cmd2(4, 5.6); } 125

Was, wenn Argumente bzw. Ergebnis nicht exakt passen, sondern nur umwandelbar sind? Noch eine Überraschung: Alles ist schon implementiert! const char * TestFunction (double, double) { static const char buffer [] = Hello, world! ; return buffer; // perfectly safe! } int main() { Functor<std::string, TYPELIST_2(int, int)> cmd(testfunction); cout << cmd(10, 11).substr(7); } 126