Generic Programming Johan Torp. Agenda GP introduction GP in perspective Questions * GP = Generic...

Post on 31-Dec-2015

228 views 4 download

Transcript of Generic Programming Johan Torp. Agenda GP introduction GP in perspective Questions * GP = Generic...

Generic Programming

Johan Torp

Agenda

• GP introduction• GP in perspective• Questions

          * GP = Generic programming in C++

What is GP?

• Algorithmic GP • GP techniques & patterns in C++

          

Polymorphism

class BarInterface { virtual void bar() = 0; };void polymorphicFunction(BarInterface& t) {  ...  t.bar();  ...}

template<class T> void polymorphicFunction(T& t) {  ...  t.bar();  ...} 

GP interface is called concept

// MyBarConcept// // Has a memberfunction called bar()// ... more assumptions ...

// T must fulfill MyBarConcepttemplate<class T>void polymorphicFunction1(T& bar); 

// T must fulfill MyBarConcepttemplate<class T>void polymorphicFunction200(T& bar);   class MyBar {   void bar(); };  

Example concepts

Concept DefaultConstructible Copyable  Assignable  Addable Convertible to OtherType OutputStreamableBarConcept

Valid expressions T t; T t2(t1);t1 = t2t1+t2; static_cast<OtherType>(t); stream << t;t.bar();

   Require as little as possible for maximal genericity

Concepts are type requirements

• Valid expressions, pre/post conditions & semantics• Additionally: Invariants, associated types, complexity

guarantees, etc

Concepts in C++ are expressed in documentationTypes which fulfill concept C are said to model CRefine new concepts from old ones by adding additional requirements

Input Iterator

Description  An Input Iterator is an iterator that may be dereferenced to refer to ...Refinement of  Trivial IteratorAssociated Types   Value type - The type obtained by dereferencing ...  Distance type - A signed integral type used to ... Valid expressions  Dereference                         *i   Pre-increment                    ++iExpression semantics  Dereference                       Will return a reference to the accessed value type   Pre-increment                    Will ...Complexity guarantees  All operations are amortized constant time

Template instantiation

class OneBar { void bar() };class AnotherBar { void bar() }; Source1.cpp:   polymorphicFunction(int(123)); Source2.cpp:   polymorphicFunction(OneBar());  polymorphicFunction(OneBar());  polymorphicFunction(AnotherBar()); Source3.cpp:  polymorphicFunction(OneBar());

Generalize memcpy

void* memcpy(void* region1,  const void* region2,size_t n); 

• Only reads contigous memory• Only writes to contigous memory • Only byte-wise copying  

Minimal requirements of copying

• traverse through source sequence • access source elements• copy elements to destination• know when to stop

 

STL version of copy

template <class InputIterator, class OutputIterator>OutputIteratorcopy(InputIterator first, InputIterator pastLast, OutputIterator result){   while (!(first == pastLast)) // Don't require != operator       *result++ = *first++;   return result;} 

How pointless?!

• Real std::copy is optimized for different types• Solves optimized generic copying once and for all

C++ language features supporting GP

Dispatching features • Inheritance• Templates• Namespaces & argument dependent lookup (ADL)• Function & operator overloading• Implicit type conversion• SFINAE• [Partial] template specialization

 Other useful language features • Dependent types• Template type deduction• Template non-type arguments (compile-time integers, bools, etc)• Template template arguments

GP techniques & patterns

• type traits• mix-in classes• policy based design• object generators • enable if • tag dispatching • type erasure• lazy data views  • concept and archetype checking

GP related programming models:• template metaprogramming• expression templates to create DSELs• macro metaprogramming

GP in practice

• Generalize patterns / boiler plate into generic classes • Library based programming models

GP problems

• Compilation & link time• Code size• Language flaws

  

How much GP can we afford?

Maybe we can use more GP because: • Compilers have improved • Well modularized => recompile less• Master/unity builds lessens template bloat• Stability & higher abstraction level allows faster iteration  too• Explicit template instantiation can lessen template bloat• Replacing boilerplate interfaces with type erasure gives

similar run-time performance 

GP flaws fixed in C++0x

• Concept code support• True variadic templates • Type deduction and type inference of named variables• Rvalue references• Preventing template instantiation• Axioms• Object construction improvements• ... and lots more!

    

Compilers are continously adding C++0x features

GP vs OOP: Efficiency and compilation

GP more efficient run-timeOO code requires less compilation

GP vs OOP: Dependencies

• Concrete OO classes and polymorphic code are coupled via the interface 

• A type might fulfill/model a concept. Polymorphic code only depends on concept via documentation

    A.k.a. : ad-hoc polymorphism vs parametric polymorphism

GP vs OOP: Customization points

Mix-in classes change interface, state, data layout, etc  template <class MixIn>  class Foo : public MixIn {...};   Type traits & tags can non-intrusively add static information   template<class T> struct iterator_traits<T*> { typedef random_access_iterator_tag iterator_category;};

GP vs OOP: Orthogonality

template<class Aspect1, class Aspect2, class Aspect3>class TweakableGPClass{ ... };

class TweakableOOClass{   TweakableOOClass(Aspect1&, Aspect2&, Aspect3&);private:  Aspect1& m_a1; ...}; Composition problems• Boiler-plate• Run-time costs (vtable calls, method forwarding, extra state)

GP vs OOP: Dispatch

• OOP is only polymorphic on one type - the object type• GP is polymorphic on all parameter types• GP also allow complex dispatch logic!

// Default fallback, unless no other function matches bettertemplate<class Object1, class Object2>void handleCollision(Object1&, Object2&); // Any object going into a black hole disappearstemplate<Object1>void handleCollision(Object1&, BlackHole&);// Unless it's another black hole -- then we get // a super nova explosion instead. Yay!template<>void handleCollision(BlackHole&, BlackHole&);// Missiles which hit anything damagable inflicts damage template<class Object2>enable_if<void, isDamagable<Object2>::value>handleCollision(Missile&, Object2&);

GP vs OOP: Simplicity

• More power = more ways to shoot ourselves in the foot• OO is less abstract

  GP is best suited to build generic libraries - which in turn provide simple and limited programming models

C++ multiparadigm programming

• Object orientation• Generic programming• Procedural programming

         

GP and dynamically typed languages

def foo(t):  """t must model bar"""  ...  t.bar()  ...

• Compiled separately, extremely fast iteration time• Late binding can provide poor runtime performance • Nothing checked statically, rely on interpreters & unit tests

A reflection on reflection

Reflection allows a program to observe and modify its own structure and behavior at runtime • GP dispatch techniques are similar to run-time reflection• GP allows static analysis => understandable behaviour

GP is generative

Macro > Template instantiation > Compilation  • Work on multiple "compilation levels" in the same file at the

same time 

Code generation

• Raise abstraction level without runtime penalty• Pay in terms of build system complexity• Identify boiler plate & patterns and generate them

Summary

• Concepts are type requirements• Template instantiation • Algorithmic GP • GP techniques• GP problems• GP classes are highly customizable and reusable• GP vs OOP • GP is useful for core libraries & algorithms• Dynamic programming languages & reflection• Code generation

 

Questions?