Advanced C++ ProgrammingProgramming is giving new functionality to a computer or computer network.Each programming language is it’s own a compromis between runtime efficiency and humanreadibility.Examples:
• assembler using mnemonics
asm {
pushad
cpuid ; serial command
_emit 0x0F ; it is...
_emit 0x31 ; ... rdtsc
lea ecx, t
mov dword ptr [ecx], eax ; # of processor clocks
mov dword ptr [ecx+4], edx ; if __int64 is used
popad }
• Fortran, Cobol as formular translators
• C/C++ as general purpose languages
• Lisp/Java as interpreter languages
The C++ Compiler is just one instrument amoung others to say a computer what’s to do,and it’s a very complex one. The Compiler tries to translate your program text, you should try tounderstand the compiler!!
1
Code symbols.cc// overloading
int f() {}
int f(int) {}
int f(float) {}
char f(float*, char) {}
class C
{ public:
// static methods/functions
C() {}
C(int) {}
static int f() {}
static int f(char) {}
// methods
~C() {}
int m() const {}
virtual int vm() {}
C &operator=(const C&) {}
C &operator+(const C&) {}
};
C operator*(const C&, int) {}
2
symbol names: nm -C symbols.o
00000000 W C::~C(void)
00000000 W C::C(void)
00000000 W C::operator=(C const &)
00000028 T operator*(C const &, int)
00000000 V C virtual table
00000000 W C::f(void)
00000000 W C::f(char)
00000018 T f(float *, char)
00000010 T f(float)
00000008 T f(int)
00000000 T f(void)
00000000 W C::m(void) const
00000034 T main
00000000 W C::vm(void)
4
symbol names: nm symbols.o
00000000 W _._1C
00000000 W __1C
00000000 W __as__1CRC1C
00000028 T __ml__FRC1Ci
00000000 W __tf1C
00000008 C __ti1C
00000000 V __vt_1C
00000000 W f__1C
00000000 W f__1Cc
00000018 T f__FPfc
00000010 T f__Ff
00000008 T f__Fi
00000000 T f__Fv
00000000 W m__C1C
00000034 T main
00000000 W vm__1C
The C++ compiler resolves overloading by introducing unique symbol names
5
symbol names (gcc 3.4)
C::~C(void) _._1CC::C(void) __1C
C::operator=(C const &) __as__1CRC1Coperator*(C const &, int) __ml__FRC1Ci
C virtual table __vt_1CC::f(void) f__1CC::f(char) f__1Cc
C::m(void) const m__C1CC::vm(void) vm__1C
f(float *, char) f__FPfcf(float) f__Fff(int) f__Fi
f(void) f__Fv
6
Function namingno return types in symbol name!code
int f(float);
char f(float);
gcc -c symbols.cc -o symbols.o
symbols.cc:
In function `char f (float)': symbols.cc:4: new declaration `char f (float)'
symbols.cc:3:
ambiguates old
declaration `int f (float)'
7
Changes: C to C++Philosophy of C: general purpose and near to maschineinherited from C:all fundamental types, pointer types, array types
• void, [unsigned] char, [unsigned] int,..., float, double
• T *, T **, ...
• T[n] (n constant integer)
• pointer to function
• volatile qualifier
• struct
• typedef
all operators
• unary operators:~, !, -, *, &, [],++, –, -> , ., sizeof()
• binary operators: +, - *, /, &, |, ^, &&, ||, ,
• ternary operator: ? :
8
all control structures like
• if() {} [else {}]
• for ( ; ; ) {}
• while() {}
• do {} while();
• asm {}
standard libraries stdlib, stdio, ...The C preprocessor for #include of declaration header and #define of macros
9
Philosophy of C++ =philosophy of C + o.onew types
• bool
• const qualifier
• reference to type T&
• pointer to member X::*memb
• pointer to method e.g. void (X::*meth(<args>) [const]
classes
• with private, protected and public part, friend declarations
• methods, constructors, destructor, static methods
The operators new and delete (substituting the typefree malloc and free functions)Inline declarations of functions, overloading of functions
10
The C++ TypesTypes are
• fundamentals (bool, [signed/unsigned]{ char, short int }[long long], float, double [long dou-ble], void, enums
• classes
• pointer to type: Type*
• pointer to function y (*pfType)(<args>)
• pointer to method y (C::*pmType)(<args>) [const]
• reference to type: Type &
• constant type: const Type == Type const
11
Further Features of C++
• Global functions
– function overloading
– operator overloading
• classes
– constructors
– methods not having the this pointer∗ static methods
– methods having the this pointer∗ virtual methods∗ abstract (virtual) methods –> abstract class∗ destructors∗ constant methods
– inheritance∗ multiple inheritance∗ virtual inheritance
– attributes∗ the qualifier mutable
• namespaces
– the using directive
12
Inheritance of classes
• public, private inheritance
• virtual inheritance
• virtual methods
• abstract (virtual methods) and classes
The standard template library !!Exceptions (as an alternative result) for error handlingFunction- and Operator Overloadingtemplate functions (assumes overloading) and classes
13
The C++ Types
const Type * == Type const *
is a (non constant) pointer to constant Type
Type *const
is a constant pointer to Type
const Type *const == Type const*const
is a constant pointer to a constant Type
A reference is semantically a constant pointer, syntactically a value.C uses pointer for call by reference, C++ most usually references.For the compiler a (non-virtual) method of the typey (C::*pmType)(<args>) [const]is the same as y (*pmType)([const] C* this, args).
14
Why Inheritance ?:
• re-usage of code (of the derived class)
• virtual methods for polymorphy
• interface classes
15
Some Features of the Standard Template LibraryContainer classes in the STL are
• vector<T, Allocator>
• list<T, Allocator>
• set<T, Compare, Allocator>
• map<Key, T, Compare, Allocator>
• queue<T, Allocator>
A Compare type has to implement the “less than” policy concerning the type T (Key int the caseof a map), i.e. for a Compare object cmp and T objects t0, t1 the callbool lt = cmp(t0, t1);must be syntactically correct and the implied order must be a total one.The Allocator type need not to be given explicitly. This type has to implement the allocator policy,i.e. the static methods
• static void* allocate(size_t __n)
• static void deallocate(void *, size_t __n)
must be defined by the Allocator.
16
Iterator Conceptall of these container template classes contain typedefs to iterator and const_iterator and methodswith the signature (i.e. they implement the iterator policy).
• iterator begin()
• const iterator begin() const
• const_iterator end()
The iterator types implement the pointer policy, i.d. they define:
• iterator operator++ () // post increment
• iterator operator– () // post increment
• iterator operator++ (int) // pre increment
• iterator operator– (int) // pre decrement
• T *operator -> ()
• T &operator *()
(The type T* implements automatically the pointer concept!)
17
The std::map implements a mapping from the Key type onto the T type.
template<class S, class T> struct pair
{
S first;
T second;
};
A map<Key, T, Compare, Allocator> is like a set<pair<Key,T>, Compare, Allocator > whereCompare acts only on the first part of pair.
The STL also implements the auto_ptr, which implements the owner-ship concept.
18
Resource Management:B. Stroustrup: “resource aquisition is initialisation”This means: Put each allocation in it’s own constructor!Example (by B. Stroustrup)
class X
{
FILE *fp;
char *mem;
X(const char * fname, size_t size)
{
fp = fopen(fname, �r�);
mem = new char[size];
}
};
better:
class X
{
FilePtr fp;
charPtr memp;
X(const char *fname, size_t size): fp(fname), memp(size)
{}
};
19
In large applications memory leaks are a common problem. This can be handled in a centralmanner using garbage collection schemes or in a distributed manner using “smart” pointer types.
• smartpointer implementations
– intrinsic (see exercise)
– extrinsic (auto_ptr implemented in the STL and example below)
(Intrinsic means here: affects the pointed type, extrinsic = non intrinsic)
20
struct reflist
{
mutable const reflist *prevM,*succM;
reflist()
{ prevM = succM = this; }
reflist(const reflist &p)
{ ref(p); }
void ref(const reflist &p) const
{ prevM=&p; succM=p.succM;
succM->prevM = prevM->succM = this;
}
void link(const reflist &p) const
{ const reflist *tmp = p.succM;
succM->prevM = &p; p.succM = succM;
succM = tmp; tmp->prevM = this;
}
bool unref()
{ if(succM!=this)
{ prevM->succM = succM;
succM->prevM = prevM;
prevM = succM = this;
return false;
}
return true;
21
}
const reflist *next() const
{ return succM; }
bool isShared() const
{ return succM!=this; }
};
template <class T> class pointerL : public reflist
{
void replace(const T* obj)
{ delete(soM);
soM = (T*)obj;
for(smartPointerL<T>*i=next(); i!=this; i=i->next())
i->soM = soM;
}
template<class S>
void assign(const smartPointerL<S> &p)
{ if(soM)==p.soM)
return;
bool d=unref();
ref(p);
if(d) delete(soM);
soM = p.soM;
}
22
public:
template<class S>
smartPointerL(const
smartPointerL<S> &p):
reflist(p),soM(p.soM()) {}
smartPointerL (const smartPointerL<T> &p):
reflist(p),soM(p.soM) {}
template<class S>
smartPointerL<T> &operator=
(const smartPointerL<S> &p)
{ assign(p); return *this; }
smartPointerL<T> &operator=(const smartPointerL<T> &p)
{ assign(p); return *this;
template<class S>
const smartPointerL<T> &operator< <=(const smartPointerL<S> &p)
{ replace(p.get());
link(p);
return *this;
}
void destruct()
{ replace(0); }
void clear()
{ if(unref())
destroy();
23
soM = 0;
}
const T &operator*() const
{ return *soM;}
T &operator*()
{ return *soM; }
const T* operator->() const
{ return soM;}
T* operator->()
{ return (T*)soM; }
};
24
The Late Copy Concept
template<class T> class vectorRep
{ friend class vector<T>;
uint4 sizeM;
int4 refCountM;
void operator delete(void *p)
{ delete[] (char*)p; }
T tM;
void *operator new(size_t mysize,size_t size)
{ return new char[mysize + (size-1) * sizeof(T) ]; }
template<class X> vectorRep(uint4 size, const X &x):
sizeM(size), tM(x)
{ uFill(&tM+1, &tM+sizeM, x); }
template<class X> vectorRep(uint4 size,const X *x,int):
sizeM(size), tM(*x)
{ uFillP(&tM+1, &tM+sizeM, x+1); }
vectorRep(uint4 size, const vectorRep<T> &rep,const T &t):
sizeM(size), tM(rep.tM)
{
uint4 m=sizeM<rep.sizeM ? sizeM : rep.sizeM;
uFillP(&tM+1, &tM+m, &hv.tM+1);
uFill(&tM+m, &tM+sizeM, t);
}
~vectorRep()
25
template functions, inlining of functions;Example sorting:
template<class T> struct stdLess
{ bool operator()(T a, T b) const
{ return a<b; } };
template<class T, bool (*less)(T, T) > struct inlineLess
{
bool operator()(T a, T b) const
{ return (*less)(a,b); }
};
template<class T> class callLess
{
bool (*lessM)(T, T);
public:
callLess( bool (*less)(T, T) ): lessM(less) {}
bool operator()(T a, T b) const
{ return (*lessM)(a,b); }
};
27
template <class T, class less> inline
void qSort(T *a, T *b, less lt)
{
T *i=a, *j=b, *m=a+(b-a)/2, v=*m;
*m = *a;
for(;;) {
while(!lt(*--j, v))
if(i==j) goto L;
*i = *j;
while(!lt(v, *++i))
if(i==j) goto L;
*j = *i;
}
L:*j++ = v;
if(i-a>1)
qSort(a, i, lt);
if(b-j>1)
qSort(j, b, lt);
}
template <class T> inline
void qSort(T *a, T *b) { qSort(a,b,stdLess<const T>()); }
28
inline and non inline compareusage:
struct point
{
double x,y;
bool operator <(const point &q)const
{ return (y==q.y) ? (x<q.x) : (y<q.y); }
};
bool cmp(const point &p,const point &q)
{ return (p.x==q.x) ? (p.y<q.y) : (p.x<q.x); }
int main()
{
point *a, *o;
qSort(a, o);
qSort(a, o, cmp);
qSort(a, o, inlineLess<const point&,cmp>());
qSort(a, o, callLess<const point&>(cmp));
}
29
Binary Searchassuming the elements between a0, a1 are sorted with respect to less
template<typename less,class T> inline int lowerBound
(const T *a0,const T *a1,const T &t,less lt)
{
if(a1<=a0) return -1;
const T *a=a0;
while(a1-a0>1) {
const T* m=a0+((a1-a0)> >1);
if(lt(t,*m)) a1=m;
else a0=m;
}
if(a0==a && lt(t, *a) )
return -1;
return a0-a;
}
30
Meta-Programming ?
Example: gcd: Z x Z -> Z
gcd(a,a) = a,a > b⇒ gcd(a,b) = gcd(a−b,b)
template<unsigned int a, unsigned int b> struct gcd
{
enum { na = a<b ? a : a-b,
nb = a<b ? b-a : b };
enum { value = gcd<na, nb>::value };
};
template<unsigned int a> struct gcd<a,a>
{ enum { value = a }; };
template<unsigned int a, unsigned int b> struct kgV
{ enum { value = (a/gcd<a, b>::value)*b }; };
template<unsigned int a,unsigned int b> class C
{ double x[gcd<a,b>::value];
// ...
};
31
The C++ -Compiler operates on following Objects:Names Id := {identi f ier}Types X := {types}Templates T := {templates}Numbers Z := {integer}(bool := { f alse = 0, true = 1} ⊂ Z)Functions F := { f unctions}A := X
⋃Z⋃
T⋃
F
Definitions D := Id×A
Where a type ∈ Id×Dn anda Template is of the Form template : Xk×Zm×Fn−> X ∪F .
class C // (′C′ ∈ Id,C ∈ X) ∈ D{
typedef type result;// (′result ′ ∈ Id, type ∈ X) ∈ Dtemplate<class T> class SubC {};
// (′SubC′ ∈ Id,subC ∈ T ) ∈ Denum { value = 33 };// (′value′ ∈ Id,33 ∈ Z) ∈ D
};
32
Mapping C -> Meta C++C-Code; Meta C++int x= y; enum {x=y };
arithmetic expression,x=b%y enum {x= b % y};object; constant integer or type;
special object e.g. 0 special type, e.g. struct Nil;function template
int f(double x, int y); template<class X,int Y> struct Frecursive function call recursive instantiation
Construction template instantiationconst myType obj(x,y); typedef myTemplate<X,Y> Obj;
struct myType { int x; }; struct Obj { typedef int X; };obj.x; Obj::X;
if(); else; template specialisationassignment -
loop - (needs assignment)
33
int2type: Z -> X
template<int n> struct int2type
{ enum { value = n }; };
type2type: X -> X
template<class T> struct type2type
{ typedef T result; };
select: bool x X x X -> X
template<bool b,class X,class Y> struct select
{ typedef X result; };
template<class X,class Y> struct select<false,X,Y>
{ typedef Y result; };
reference: X -> X
template<class U> struct reference
{ typedef U& result; enum { is = false }; };
template<class U> struct reference<U &>
{ typedef U& result; enum { is = true }; };
34
Example conversion: X x X -> bool
struct nil
{
typedef nil value;
enum { size = 0 };
template<class T> nil(const T&);
template<int n> struct sub
{ typedef nil result; };
};
template<class X,class Y> struct conversionHelper
{
static char f(X x, int);
static int f(nil, float);
static const typename reference<Y>::result Y_();
};
template<class X,class Y> struct conversion
{
typedef conversionHelper<X, Y> H;
enum { value = sizeof((H::f)((H::Y_)(), 0))==1 };
};
template<class B, class D> struct inherits
{ enum { value = conversion<B*,D*>::value }; };
35
Example property: X -> bool
template<class T = void> struct property
{
enum { value = conversion<property<>, T>::value};
typedef int2type<value> result;
};
struct property<X>
{
enum { value = true };
typedef int2type<value> result;
};// this property is not inherited
class X
{ ...
operator property<>()const;
// this property is inherited
};
36
typical usage of properties
template<class X> void f(const X &x, int2type<true>)
{
// use: X has property
}
template<class X> void f(const X &x, int2type<false>)
{
// take care of X has not property !!
}
template<class X> void caller(const X &x)
{ f(x, property<X>::result()); }
37
algorithms acting on types:
template<class V, class L = nil, class R = nil> struct typeTree
{
typedef L left; typedef R right; typedef V value;
typedef typeTree<value, left, right> tree;
enum {leftSize=left::size, rightSize=right::size, size=leftSize+rightSize+1};
template<int i> struct sub
{
enum { r = i>leftSize ? i-leftSize : 0 };
typedef typename select< (i+1==size),
tree,
typename right::sub<r>::result>::result rightSub;
typedef typename select< (i<leftSize),
typename left::sub<i>::result,
rightSub>::result result;
typedef typename result::value value;
};
template<int i> struct at
{ typedef typename sub<i>::value result; };
};
38
Example type maps
template< class Map, class Tree1, class Tree2 = nil> struct map
{
typedef typeTree<
typename Map::map<
typename Tree1::value, typename Tree2::value>::result,
typename map<Map,typename Tree1::left,typename Tree2::left>::result,
typename map<Map,typename Tree1::right, typename Tree2::right>::result> result;
};
template< class Map, class Tree> struct map<Map,Tree,nil>
{
typedef typeTree<
typename Map::map<typename Tree::value>::result,
typename map<Map,typename Tree::left>::result,
typename map<Map,typename Tree::right>::result> result;
};
template< class Map > struct map<Map,nil,nil>
{ typedef nil result;};
39
Examples of reasonable type mappings:
struct lowerType
{
template<class T1, class T2> struct map
{ typedef typename select<conversion<T2,T1>::value, T2, T1>::result result; };
};
struct referenceType
{
template<class T> struct map
{ typedef typename reference<T>::result result;};
};
40
Example typeTree usage: field: Xn×Z−> X
template< class Tree, int n=0> struct field :
public field<typename Tree::left,n+Tree::rightSize+1>,
public field<typename Tree::right,n+1>
{
typedef Tree tree;
typedef field<typename tree::left, n+tree::rightSize+1> leftBase;
typedef field<typename tree::right, n+1> rightBase;
typedef typename tree::value value;
enum { size = tree::size };
value valM;
field(): valM(value()) {}
template<class T> field(const field<T, n> &f):
leftBase(f), rightBase(f), valM(f.valM) {}
template<class T> field &operator=(const field<T, n> &f)
{ leftBase::operator=(f);
rightBase::operator=(f);
valM = f.valM;
return *this;
}
void print(std::ostream &os) const
{ leftBase::print(os); rightBase::print(os);os< <valM< <"; "; }
};
41
Alexandrescu: Small Object AllocationThe Standard Allocator
• standard allocation/deallocation by new/delete• most often thin wrapper of malloc/free• general purpose implementation• good for medium and large sized blocks but slow for small object (<64 byte)• 4 - 32 additional bytes needed for book holding• different strategies: first match, best match, worst match, ...• difficulty: multiple allocation of small objects, dealloation in same order or
deallocation in inverse order or random allocation/deallocation
42
//Platform independend
struct MemControlBlock
{
size_t size;
bool available;
};
//Platform dependend
struct MemControlBlock
{
size_t size :31;
bool available: 1;
};
allocation and dealloction: linear search
struct MemControlBlock
{
MemControlBlock *prev, *next;
bool available;
};
dealloction: no search
43
Small Object Allocator HierarchyGoal: allocation and dealloction (most often) without search
SmallObject: Service on object level, usage by inheritanceSmallObjectAllocator: Allocates small objects of different sizes, configurableFixedAllocator: Allocates small objects of fixed sizeChunk: Handles fixed number of small objects of fixed size
44
////////////////////////////////////////////////////////////////////////////////
// class Chunk
// Offers services for allocating fixed-sized objects
////////////////////////////////////////////////////////////////////////////////
struct Chunk
{ // No Constructor
void Init(size_t blockSize, unsigned char blocks);
void* Allocate(size_t blockSize);
void Deallocate(void* p, size_t blockSize);
// blockSize not hold
unsigned char* pData_;
unsigned char firstAvailableBlock_,
blocksAvailable_;
};
45
////////////////////////////////////////////////////////////////////////////////
// Chunk::Init
// Initialise a chunk
////////////////////////////////////////////////////////////////////////////////
void Chunk::Init(size_t blockSize, unsigned char blocks)
{
assert(blockSize > 0);
assert(blocks > 0);
// Overflow check
assert((blockSize * blocks) / blockSize == blocks);
pData_ = new unsigned char[blockSize * blocks];
firstAvailableBlock_ = 0;
blocksAvailable_ = blocks;
unsigned char i = 0;
unsigned char* p = pData_;
for (; i != blocks; p += blockSize)
{ *p = ++i; }
}
46
////////////////////////////////////////////////////////////////////////////////
// Chunk::Allocate
// Allocates a block from a chunk
////////////////////////////////////////////////////////////////////////////////
void* Chunk::Allocate(size_t blockSize)
{
if (!blocksAvailable_)
return 0;
unsigned char* pResult = pData_ + (firstAvailableBlock_ * blockSize);
// Set firstAvailableBlock_ to the next available block
firstAvailableBlock_ = *pResult;
--blocksAvailable_;
return pResult;
}
47
////////////////////////////////////////////////////////////////////////////////
// Chunk::Deallocate
// Deallocates a block from a chunk
////////////////////////////////////////////////////////////////////////////////
void Chunk::Deallocate(void* p, size_t blockSize)
{
assert(p >= pData_);
unsigned char* toRelease = static_cast<unsigned char*>(p);
// Alignment check
assert((toRelease - pData_) % blockSize == 0);
*toRelease = firstAvailableBlock_;
// Set firstAvailableBlock_ to position of toRelease
firstAvailableBlock_ = static_cast<unsigned char>((toRelease - pData_) / blockSize);
// Truncation check (position can be hold by char?)
assert(firstAvailableBlock_ == (toRelease - pData_) / blockSize);
++blocksAvailable_;
}
48
////////////////////////////////////////////////////////////////////////////////
// class FixedAllocator
// Offers services for allocating fixed-sized objects
////////////////////////////////////////////////////////////////////////////////
class FixedAllocator
{
size_t blockSize_;
unsigned char numBlocks_;
typedef std::vector<Chunk> Chunks;
Chunks chunks_;
Chunk* allocChunk_;
Chunk* deallocChunk_;
// For ensuring proper copy semantics
mutable const FixedAllocator* prev_;
mutable const FixedAllocator* next_;
...
public:
// Allocate a memory block
void* Allocate();
// Deallocate a memory block previously allocated with Allocate()
void Deallocate(void* p);
};
49
////////////////////////////////////////////////////////////////////////////////
// FixedAllocator::Allocate // Allocates a block of fixed size
////////////////////////////////////////////////////////////////////////////////
void* FixedAllocator::Allocate()
{
if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0)
{
Chunks::iterator i = chunks_.begin();
for (;; ++i)
{
if (i == chunks_.end())
{ // Initialize
chunks_.reserve(chunks_.size() + 1);
Chunk newChunk;
newChunk.Init(blockSize_, numBlocks_);
chunks_.push_back(newChunk);
allocChunk_ = &chunks_.back();
deallocChunk_ = &chunks_.front();
break;
}
if (i->blocksAvailable_ > 0)
{
allocChunk_ = &*i;
50
break;
}
}
}
assert(allocChunk_ != 0); assert(allocChunk_->blocksAvailable_ > 0);
return allocChunk_->Allocate(blockSize_);
}
51
////////////////////////////////////////////////////////////////////////////////
// class SmallObjAllocator
// Offers services for allocating small-sized objects
////////////////////////////////////////////////////////////////////////////////
class SmallObjAllocator
{
public:
SmallObjAllocator(size_t chunkSize, size_t maxObjectSize);
void* Allocate(size_t numBytes);
void Deallocate(void* p, size_t size);
private:
SmallObjAllocator(const SmallObjAllocator&);
SmallObjAllocator& operator=(const SmallObjAllocator&);
typedef std::vector<FixedAllocator> Pool;
Pool pool_;
FixedAllocator* pLastAlloc_;
FixedAllocator* pLastDealloc_;
size_t chunkSize_;
size_t maxObjectSize_;
};
52
////////////////////////////////////////////////////////////////////////////////
// class SmallObject
// Base class for polymorphic small objects, offers fast allocations/deallocations
////////////////////////////////////////////////////////////////////////////////
class SmallObject
{
static void* operator new(size_t size)
// Trick: Let size be given by compiler
static void operator delete(void* p, size_t size)
virtual ~SmallObject() {}
};
The compiler is able to get size of destroyed objects by asking the destructor or by adding thissize in virtual function table.
53
Alexandrescu: Singleton Objectsclass Font {...};
class PrinterPort {...};
class PrintJob {...};
class MyOnlyPrinter
{
static void AddPrintJob(PrintJob &newJob)
{
if(printQueue_.empty() && printingPort_.available())
{ printgPort_.send(newJob.Data());
else
{ printQueue_.push_back(newJob); }
}
private:
static std::queue<PrintJob> printQueue_;
static PrinterPort printingPort_;
static Font defaultFont_;
};
54
class Singleton
{
public:
static Singleton *Instance()
{
if(!pInstance_)
pInstance_ = new Singleton;
return pInstance_;
}
private:
Singleton() {}
Singleton(const Singleton&);
Singleton *pInstance_;
};
// Sinleton.cpp
Singleton* Singleton::pInstance_ = 0;
55
Singleton sneaky(*Singleton::Instance() );
// -> Compile error
class Singleton
{
public:
static Singleton &Instance();
private:
Singleton() {}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
~Singleton();
};
57
Problem Destruction(Singleton constructor may allocate persistant ressources)
static Singleton &Instance()
{
static Singleton Instance;
return Instance;
}
(Meyers singleton, 1996 )Destruction after termination of mainThe compiler generates a function which destructs Singleton::instance.This function is registered by atexit-
58
class Singleton
{
public:
static Singleton *Instance()
{
if(!pInstance_)
{
if(destroyed_)
std::runtime_error(�bad singleton recognised�);
else
Create();
}
return *pInstance_;
}
private:
static void Create()
{ static Singleton instance;
pInstance_ = &instance;
};
~Singleton()
{ pInstance_ = 0; destroyed_ = true; }
};
59
The Phoenix-Singleton: change in last listing
if(destroyed_)
std::runtime_error(�bad singleton recognised�);
by
if(destroyed_)
{
Create();
new(pInstance_) Singleton;
atexit(KillPhoenixSingleton);
destroyed_ = false;
}
and add
static void KillPhoenixSingleton();
60
Visitor-PatternEnlargement of class hierarchy
• adding class– just write new class which inherits one class of hierarchy– compile new class
• adding virtual method– add virtual method in base– if necessary add virtual method to derived classes– recompile all of class hierarchy.
61
class shapeI
{
public:
virtual void visit(dispatcherI&) = 0;
};
struct rect: public shapeI
{
void visit(dispatcherI &V)
{ V.dispatch(*this); }
static std::string type()
{ return "rect"; }
};
struct circle: public shapeI
{
void visit(dispatcherI &V)
{ V.dispatch(*this); }
static std::string type()
{ return "circle"; }
};
struct typePrinter: public dispatcherI
{ virtual void dispatch(rect &);
virtual void dispatch(circle &); };
62
Virtual method call = partial evaluationf (class, param)−→ fclass(param)
class Base
{
...
virtual Y method(X x);
};
class Object: public Base
{
...
virtual Y method(X x);
};
Object obj;
Base &b=obj;
b.method(y);
translated by compiler to set of functions likeY method(Base *this, X x);
Y method(Object *this, X x);
The virtual function call mechanism performs the partial evaluation.
63
MultiMethod-Pattern:partial evaluation of multiple classesf (class,class, ..., param)−→ fclass,class,...(param)
is not directly supported by C++.But this pattern can be imlemented for example by map:
<typeinfo<class1>,typeinfo<class1>,...>--> special impl. of method<class 1, class2, ..., param)
All specialisations most be stored in a multidimensional array.This leads to a lot of technical code and some runtime overhead.
64
struct dispatcherI;
class shapeI
{
public:
virtual void visit(dispatcherI&) = 0;
};
struct rect: public shapeI
{
void visit(dispatcherI &V)
{ V.dispatch(*this); }
static std::string type()
{ return "rect"; }
};
struct circle: public shapeI
{
void visit(dispatcherI &V)
{ V.dispatch(*this); }
static std::string type()
{ return "circle"; }
};
65
struct dispatcherI
{ virtual void dispatch(rect &) =0;
virtual void dispatch(circle &) =0; };
template<class F, class T> struct dispatcherT: dispatcherI
{ T &t;
dispatcherT(T &s) : t(s) {}
void dispatch(rect &s)
{ F::call(t, s); }
void dispatch(circle &s)
{ F::call(t, s); }
};
template<class F> struct dispatcher: dispatcherI
{ shapeI &shp;
dispatcher(shapeI &s) : shp(s) {}
void dispatch(rect &r)
{ dispatcherT<F, rect> dsp(r);
shp.visit(dsp);
}
void dispatch(circle &c)
{ dispatcherT<F, circle> dsp(c);
shp.visit(dsp);
}
};
66
struct intersector
{
template<class T0, class T1> static void call(T0&, T1&)
{ std::cout< <"dispatch("< <T0::type()< <","< <T1::type()< <")\n"; }
};
void intersect(shapeI &s0, shapeI &s1)
{
dispatcher<intersector> dsp(s1);
s0.visit(dsp);
};
int main()
{
rect r;
circle c;
intersect(r, c);
return 0;
}
67
//IDispatchPart.h
template< typename TItem > struct IDispatchPart_
{ virtual void Dispatch( TItem& _item ) = 0; };
//IDispatch.h
template< typename TItems > struct IDispatcher_ :
public IDispatchPart_< typename TItems::item > ,
public IDispatcher_< typename TItems::next > {};
//Deferred dispatcher interface stub.
template<> struct IDispatcher_< ListEnd_ > {};
//IShapesDsp.h
class Rect_;
class Square_;
class Triangle_;
class RightTriangle_;
typedef TypeList_< Rect_,
TypeList_< Square_,
TypeList_< Triangle_,
TypeList_< RightTriangle_ > > > > Shapes_;
class IShapesDsp_ : public IDispatcher_< Shapes_ > {};
68
/************************* pair' and lists *********************/
struct nil {};
template<class L, class R=nil> struct pair
{ typedef pair<L,R> p;
typedef L car;
typedef R cdr;
};
template<class T, class X0=nil, class X1=nil, class X2=nil> struct listH
{ typedef pair<T, typename listH<X0, X1, X2>::final> final; };
template<> struct listH<nil> { typedef nil final; };
template<class T, class X0=nil, class X1=nil, class X2=nil> struct list: listH<T, X0, X1, X2>::final
{ typedef typename listH<T, X0, X1, X2>::final p; };
71
/************************* Eval ************************/
template<class P, class Syms> struct eval
{ typedef P final; };
template<class sym, class val, class Syms> struct eval<bind<sym, val>, Syms >
{ typedef val final; };
template<class L, class R, class Syms> struct eval<pair<L,R>, Syms >
{ typedef typename eval<L, Syms>::final fun;
typedef typename instance<fun::n, typename fun::more, fun, R, Syms>::final Fun;
typedef typename eval<Fun, Syms>::final final;
};
template<class T> struct eval<quote<T> >
{ typedef T final; };
72
/************************* macros ************************/
template<class Fun, class P> struct instance<0, $t, Fun, P>
{ typedef typename Fun:: template fun<size_<P>::value, P>::final final; };
template<class Fun, class P> struct instance<1, $f, Fun, P>
{ typedef typename eval<typename P::car>::final X1;
typedef typename Fun:: template fun<X1>::final final;
};
template<class Fun, class P> struct instance<1, $t, Fun, P>
{ typedef typename eval<typename P::car>::final X1;
typedef typename Fun:: template fun<X1, size_<P>::value-1,
typenameP::cdr>::final final;
};
template<class Fun, class P> struct instance<2, $f, Fun, P>
{ typedef typename eval<typename P::car>::final X1;
typedef typename eval<typename P::cdr::car>::final X2;
typedef typename Fun:: template fun<X1, X2>::final final;
};
template<class Fun, class P> struct instance<2, $t, Fun, P>
{ typedef typename eval<typename P::car>::final X1;
typedef typename eval<typename P::cdr::car>::final X2;
typedef typename Fun:: template fun<X1, X2, size_<P>::value-2,
typename P::cdr::cdr>::final final;};
73
/************************* REVERSE + Helper ************************/
template<class P, class Tail> struct reverseH
{ typedef typename reverseH<typename P::cdr, pair<typename P::car, Tail> >::final final; };
template<class Tail> struct reverseH<nil, Tail>
{ typedef Tail final; };
struct Reverse: macro<1, $f, 'r','e','v','e','r','s','e'>
{
template<class P> struct fun
{ typedef quote<typename reverseH<P, nil>::final> final; };
};
74
/************************* MAP ************************/
template<class F, class P> struct mapH
{ typedef transposer<P> split;
typedef typename instance<F::n, typename F::more, F, typename split::carP>::final fun;
typedef typename eval<fun>::final res;
typedef pair<res, typename mapH<F, typename split::cdrP>::final > final; };
template<class F> struct mapH<F, nil> { typedef nil final; };
struct Map: macro<1, $t, 'm','a','p'>
{
template<class F, int size, class P> struct fun
{typedef quote<typename mapH<F, typename evalR<P>::final>::final> final; }; };
75
Advanced C++ Programming 11.01.05
keywords:
• solver for systems of equations
• parallelisation
• synchron distributed programming model
Matrix free solver for nonlinear systems of equations
f (x∗) = 0
The Newton iteration
∇ f|xnδxn = f (xn)
xn+1 := xn +δxn
reduces this problem to solve a sequence of linear systems.
76
Small linear systems:direct solver (not matrix free!): e.g. reduction by rendering(
α rT
c S
)(ξ
x
)=(
β
b
)⇒
x = S−1b−ξ S−1c
ξ = β−rT S−1bα−rT S−1c
template<int n> struct matrix
{
matrix<n-1> sub;
double col[n-1], row[n-1], d, invCol[n-1], invD;
double dotRow(const double *z) const
{
double s=0;
for(int i=0; i<n-1; i++)
s += row[i] * z[i];
return s;
}
matrix(const double *val, int off = n): sub(val+off+1,n)
{
d = *val++;
for(int i=0; i<n-1; i++)
row[i] = *val++;
77
for(int j=0; j<n-1; j++, val += off)
col[j] = *val;
sub.multInv(invCol, col);
invD = d-dotRow(invCol);
}
78
void mult(double *y, const double *x) const
{
*y = dotRow(x+1) + d * *x;
sub.mult(y+1, x+1);
for(int i=1; i<n; i++)
y[i] += col[i-1] * *x;
}
void multInv(double *x, const double *y) const
{
sub.multInv(x+1, y+1);
*x = (*y - dotRow(x+1) ) / invD ;
for(int i=1; i<n; i++)
x[i] -= invCol[i-1] * *x;
}
void invert(matrix<n> &I) const;
};
struct matrix<1>
{
double d;
matrix(const double *val, int): d(*val)
{}
void mult(double *y, const double *x) const
{ *y = d * *x; }
79
Large linear systems:iterative solver (matrix free!): example Krylov space methods
Ax = b
Kn(A,b) := span({
b,Ab,A2b, . . . ,An−1b})
=
span({
Akb : k = 0 . . .n−1})
Krylov space methods minimise in the kth iteration step ‖Ax−b‖ for x ∈ Kk(A,b).Their implementation need
• the matrix-vector product Ax,
• vector-vector product < x,y >,
• vector-scalar product α x,
• vector-sum x+ y.
Examples of Krylov space methods areConjugate Gradient (CG), Lanczos for symmetric positive definite matrices.Generalized Minimal Residual (GMRes), Transposed free Quasi Minimal Residual (tfqmr) andBiCGStab for non symmetric indefinite matrices.
81
Example CG iteration:
vector<double> Cg::solve(const vector<double> &b,
const vector<double> &x0, double eps )
{
vector<double> r(dim), p(dim), a(dim), x(x0);
r = p = b - A(x0);
double rho = Skal_mult(r, r);
for(int k = 0; rho > eps && k < dim; k++)
{
a = A(p);
double alpha = rho/Skal_mult(a, p);
x += alpha * p;
r -= alpha * a;
double beta = 1.0/rho;
beta *= (rho = Skal_mult(r, r));
(p *= beta) += r;
}
return x;
}
82
Why matrix free?Let in the context of the Newton iteration
A := ∇ f|xn.
Then it holds:
Aδx = ∇ f|xnδx =
∂ f∂δx|xn = lim
ε→0
f (xn + ε δx)− f (xn)ε
This term can be computed either numerically or analytically as the directional derivative.
What must be done for parallelisation?
• distribution of data
• communication
• parallelisation of all needed operations
– evaluation of f(x)
– vector-vector-product
83
// generic type representation
double d=3.14;
string s=�3.14�;
// using pointer to void
void *gen0=&d, *gen1=&s;
...
double *pd0=(double*)gen0, *pd1=(double*)gen1;
// using any
any gen0=d, gen1=s;
...
double *pd0=gen0.cast<double>(), *pd1=gen1.cast<double>();
84
//! class any stores any type and allows safe casts
class any
{ mutable smartPointer<generic> repM;
void unique()
{ if(repM.isShared())
repM=repM->clone();
}
public:
any() {}
template<class T> any (const T &t)
{ repM = generic::create(t); }
any(const any &obj): repM(obj.repM) {}
template<class T> any &operator= (const T &t)
{ repM = generic::create(t); return *this; }
any &operator = (const any &obj)
{ repM = obj.repM; return *this; }
template<class T> T *cast(const T * =0)
{ unique(); return !repM ? (T*)0 : repM->get((T*)0); }
template<class T> const T *cast(const T * =0) const
{ return !repM ? (T*)0 : repM->get((T*)0); }
};
85
//! generic representation of arbitrary types
class generic: public shared
{ ...
public:
generic(const std::type_info *id): idM(id) {}
virtual ~generic() {}
virtual generic* clone() const=0;
template<class T> static generic* create(const T&t)
{ return create_(t, int2type<fundamental<T>::is>() ); }
template<class T> T *cast(const T *)
{ return cast_<T>(int2type<fundamental<T>::is>(), (const T*)0); }
};
86
//! generic representation of arbitrary types
class generic: public shared
{
private:
const std::type_info *idM;
virtual const void *obj() const =0;
template<class T> T* cast_(trueT, const T*)
{ return equal(typeid(T), *getTypeId()) ? (T*)obj() : 0; }
template<class T> T* cast_(falseT, const T*)
{ return equal(typeid(T), *getTypeId() ? (T*)() : dynamic_cast<T*>(this); }
template<class T> static generic* create_(const T&t, trueT)
{ return SP(new genRepHas<T>(t) ); }
template<class T> static generic* create_(const T&t, falseT)
{ return SP(new genRepIs<T>(t) ); }
public:
...
};
87
template<class T> struct genRepT: public generic
{ genRepT():generic(&typeid(T)) {} };
template<class T> struct genRepIs :public genRepT<T>, public T
{
genRepIs(const T &t): T(t) {}
genRepIs(const genRepIs<T> &t): genRepT<T>(), T((const T&)t) {}
generic* clone() const
{ return generic*(new genRepIs<T>(*this) ); }
const void *obj() const
{ return static_cast<const T*>(this); }
};
template<class T> struct genRepHas : public genRepT<T>{
T tM;
genRepHas(const T &t): tM(t) {}
generic* clone() const
{ return SP(new genRepHas<T>(tM) ); }
const void *obj() const
{ return &tM; }
};
88
Asynchron algorithmsdata serialisationIf two software-components have to exchange structured data types without sharing any data typedeclaration an abstraction concerning binary representation is needed. The mean ideas are
• any structured type is a composition of simpler types
• there are only a few compositions
• to read a structured data only its binary representation is needed
fundamentalsThe fundamental types defined in the CTL are:the integral types
char = bool with values in {0,1}
char=int1, int2, int4, int8
unsigned char =uchar=uint1, uint2, uint4, uint8
and the float types
real4, real8
89
array
template<class T> class array;
serialisation as
os< <int8(vec.size());
for(int8 i=0; i<vec.size; i++)
os< <vec[i];
examples
std::vector<T,Alloc>: array<T>
std::set<T, Cmp, Alloc>: array<T>
std::list<T, Alloc>: array<T>
empty
struct empty {};
oStream &operator< <(oStream &os,const empty&)
{ return os; }
90
tupel
template<class T0, class T1=empty,..., class Tmax=empty>
class tupel;
T0 t0;
T1 t1;
...
Tmax tmax;
os< <t0< <t1< <...< <tmax;
examples
std::complex<T> : tupel<T,T>
std::pair<S,T> : tupel<S,T>
struct info
{
int n;
float x,y;
CTL_Type(info, tupel, (n,x,y), 3)
}; : tupel<int,float,float>
map<key,val,Cmp,Alloc> : array<tupel<key,val> >
91
cstring
template<class T> class cstring;
while(!!str[i])
os< <str[i++];
os< <T();
examples
char *
std::string
92
reference
template<class T> class reference;
if(!t)
return os < < true < < int4(-1);
int4 log = os.getStreamId(t);
if(log>0) // t is already in the stream
return os < < true < < log;
os.addReference(t);
os< <false;
const char *typeName=typeName<T>(*t);
if(!typeName)
return os< <std::string();
os < < std::string(typeName);
os < < binarySize(*t) < < *t;
examples
std::auto_ptr<T>: reference<T>
T*: reference<T>
93
To enable the reader of an reference of type T or a specific any object holding a T,the function ctl::connect<T>() must be called before the reading is invoked, in order to insert thecorresponding reader function into a factory.
struct polymorphIOBase
{
virtual ~polymorphIOBase() {}
virtual iStream &read(void *&, iStream &) const=0;
};
template<class T,class Base=T> struct polymorphIO:
public Base, public polymorphIOBase
{
iStream &read(void *&obj, iStream &is) const
{ obj = new T; return is> >*(T*)obj; }
};
typedef std::string typeId;
94
class factory
{
map<typeId, const polymorphIOBase*> ioM;
public:
template<class T> bool add(T* =0)
{ typedef typename select<fundamental<T>::is,
type2type<T>, T>::result base;
const typeId type=typeName<T>();
if(!type)
return false;
ioM[type] = new polymorphIO<T, base>;
return true;
}
template<class T> void read(const T *&t,
const typeId &type,iStream &is)
{ polymorphIOBase *io= ioM[type];
if(dynamic_cast<const T*>(io))
io->read((void*&)t,is);
else
t = 0;
}
};
95
ExampleThe following class defines two constructors accepting a sparse matrix int the ijv and int the rowby row formats.The ijv format is an array of entries of the form (i,j, value) which has the binary representation:
array<tupel<int4, int4, real8> >
The row by row format is an array of rows, where each row has an row i index and a vector ofentries of the form (j, val). Its representation is
array<tupel<int4,array<tupel<int4,real8> > > >
96
Top Related