Download - Advanced C++ Programming assembler using … · Advanced C++ Programming ... Fortran, Cobol as formular translators C/C++ as general purpose languages Lisp/Java as interpreter languages

Transcript

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

int main()

{ C c,d;

d = c;

C::f();

C::f(0);

c.m();

}

gcc -c symbols.cc -o symbols.o

3

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

{

for(uint4 i=1; i<sizeM; i++)

(&tM+i)->~T();

}

};

26

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

Disadvantages:

• No polymorphy

• Hard initialisation and destruction

56

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

69

70

/************************* 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

void multInv(double *x, const double *y) const

{ *x = *y / d; }

};

80

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