Part 2 - Johan Torp

advertisement
Modern C++ in
practice
Part 2
Today
Generic programming introduction
Learn & code some generic design patterns
Goals
Practical experience of
•
•
•
•
•
C++ best practices, idioms and details
Design patterns in C++
Generic, functional and template programming
Using boost and STL
Utilize APIs, books and mailing lists
Coherent coding style and get to know each other
Past
•
•
•
•
RAII
Boost candy
Higher order programming
Workshops, variant+signals
Future
•
•
•
•
•
•
•
•
Modelling static or dynamic?
”Prepone” error detection
Template metaprogramming introduction
Working with namespaces
Free functions over classes
Exception safe code
Event driven architecture
Dive deeper: boost, STL, template metaprogramming,
generic programming
• Something else?
C++ - a multiparadigm
language
•
•
•
•
•
•
•
Procedural programming
Object oriented programming
Generic programming
Functional programming
Embedded DSLs
Template metaprogramming
Macro metaprogramming
Know and blend these paradigms
Generic programming is especially important
OO is just one paradigm
”I find OOP methodologically wrong”
Alexander Stepanov
”Objects are a poor man’s closures”
Norman Adams
”... what society overwhelmingly asks for is snake oil. Of course,
the snake oil has the most impressive names —otherwise you
would be selling nothing— like ... "Object Orientation" ...”
Edsger W. Dijkstra
What is generic programming
• Orthogonality
• Encapsulate the essence of a problem
• Very high degree of reusability
”Commit to abstractions, not to details: Use the mose generic
and abstract means to implement a piece of functionality”
C++ coding standards #67
Why bother learning
Larger design space => more sweetspots
Encapsulate error prone code
C++ 0x
Different perspective
Will spread to other languages
Very simplified...
OO programming builds on run-time polymorphism
Generic programming builds on compile-time polymorphism
Polymorphism revisited
Run-time polymorphism through explicit interfaces
class FooType { virtual void bar() = 0; };
void foo(FooType& t) {
t.bar(); }
Compile-time polymorphism through ”implicit interfaces”
template<class T>
void foo(T& t) { t.bar(); }
The STL
Laid the foundation of generic programming in C++
Alexander Stepanov first introduced GP in Ada
STL
•
•
•
•
Containers
Iterators
Algorithm
Functors
Concepts
•
•
•
•
Valid expressions
Associated types
Invariants
Complexity guarantees
Types which satifies requirements models the concept
Concepts can be refined
Surf time!
Review iterator concepts at
http://www.sgi.com/tech/stl/table_of_contents.html
Hack time!
Generalize memcpy
void* memcpy(void* region1, const void* region2, size_t n)
{
const char* first = (const char*)region2;
const char* last = ((const char*)region2) + n;
char* result = (char*)region1;
while (first != last) *result++ = *first++;
return result;
}
Memcpy generalized
template <typename InputIterator, typename OutputIterator>
OutputIterator copy(InputIterator first, InputIterator
last,
OutputIterator result)
{
while (first != last) *result++ = *first++;
return result;
}
Hack time!
Implement
www.sgi.com/tech/stl/find_if.html
How to apply GP
Apply at generic layers and architecture
Don’t be overly specific
Spread conventions
Second part
Get familiar with generic design techniques
Introduce, code small example, surf APIs
Time for a break?
Object generators
Use template type deduction to construct unnamed temporaries
std::make_pair
boost::bind
template <class T> foo(T t);
foo(std::pair<unsigned int, std::multimap<unsigned int, std::string> >(0,
it));
foo(std::make_pair(0,it));
Hack time!
Implement make_shared_ptr
Type traits
Associates information with a compile time entity
Non-intrusive templated class
Uses partial template specialization
Never instantiated
Ehh...
What the *beep*?
Example usage
template <class Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
... Some other things
};
template<class Iterator>
void my_algorithm(Iterator begin, Iterator end)
{
...
iterator_traits<Iterator>::value_type copy_of_first_element = *begin;
...
}
To write your own iterator
• Specialize std::iterator_traits
• Or provide the required typedefs
Example implementation
template <typename T>
struct is_void : public false_type{};
template <>
struct is_void<void> : public true_type{};
Surf time!
Let’s have a look at some usages of type traits
www.boost.org/doc/html/boost_typetraits/examples.html
Then let’s look at what boost::type_traits contains
www.boost.org/doc/html/boost_typetraits/category.html
Hack time!
template <class Iterator>
struct iterator_traits
{
typedef typename Iterator::iterator_category iterator_category;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
Implement raw pointer specialization of iterator_traits
Tag dispatching
Dispatch function calls depending of type properties
Uses function overloading to do so
Type traits class often provide tags
Tag dispatching
struct input_iterator_tag{};
struct random_access_iterator_tag{};
namespace detail {
template<class InputIterator, class Distance>
void advance_impl(InputIterator& i, Distance n, input_iterator_tag) {...}
template<class RandomAccessIterator, class Distance>
void advance_impl(RandomAccessIterator & i, Distance n,
random_access_iterator_tag) {...}
}
template<class InputIterator, class Distance>
void advance(InputIterator& i, Distance n)
{
typedef typename iterator_traits<InputIterator>::iterator_category category;
detail::advance_impl(i, n, category);
}
Tag dispatching
Appears in STL & boost – important to know
Curiously Recurring Template
Pattern
class Derived : public Base<Derived>
Example
class Y: public
boost::enable_shared_from_this<Y>
Hack time!
Use CRTP implement an instance counter
CRTP as abstract base class
template <class Derived>
struct Base {
void interface() {
..
static_cast<Derived&>(*this).implementation();
...
}
};
struct Derived : public Base<Derived>
{
void implementation();
};
Derived needn’t implement an interface, only model a concept
Policy based class design
Reminds you of aspect oriented development
Break classes down to small orthogonal pieces
Encapsulate a design space, allow users to do the trade offs
Policy example
struct ThrowError {
void handle_error(int line_nr, const std::string& s) { throw std::exception(s); }
};
struct AssertError {
void handle_error(int line_nr, const std::string& s)
};
{ assert(false);
struct LogError {
void handle_error(int line_nr, const std::string& s) { log(s); }
};
struct IgnoreError {
void handle_error(int line_nr, const std::string& s) {}
};
template <class ErrorHandlingModel=LogError>
class Foo : public ErrorHandlingModel
{
void bar()
{
if (condition_xxx) handle_error(__LINE__, ”Error, XXX occurred”);
}
};
}
Policy example
struct MultiThreadedModel {
protected:
typedef boost::thread::mutex mutex;
mutex m_mutex;
};
struct SingleThreadedModel {
protected:
struct mutex { // Dummy class
struct scoped_lock { scoped_lock(mutex) {} };
};
mutex m_mutex;
};
template <class ErrorHandlingModel, class ThreadingModel>
class Foo : public ErrorHandlingModel
{
void bar()
{
typename ThreadingModel::mutex::scoped_lock lock(m_mutex);
if (condition_xxx) handle_error(__LINE__, ”Error, XXX occurred”);
}
};
Hack time!
template<class ValueType,
class ValueDescription,
class ThreadingModel,
class LifeTimeModel>
class Cache
{
boost::shared_ptr<ValueType> get(const ValueDescription&
description);
};
Use new ValueType(ValueDescription) for creation
Thread safe or not
Destroy or cache not used resources
Policy classes
Policy classes can also be used to extend interfaces
How would you change the following class to allow asynchronous delivery?
class MessageChannel
{
template<class Msg>
void send(const Msg& msg) const;
template<class Msg>
const Signal<void(const Msg&)>& get_signal() const;
};
Hack time!
template<class T> struct ImmediateDeliveryModel {
protected:
template <class Msg> void on_message(const Msg& msg);
};
template<class T> class RequestedDeliveryModel {
public:
void deliver();
protected:
template<class Msg> void on_message(const Msg& msg);
};
template <template <class> DeliveryModel>
class MessageChannel
: public DeliveryModel<MessageChannel> // CRTP
{
public:
template<class Msg> void send(const Msg& msg) {
on_message(msg);
}
protected:
template<class Msg> void deliver_impl(const Msg& msg);
};
Policy based class design
Extend interface, add state, add compile time entites
Need not be entirely orthogonal
Can pass policies on to internal subclasses
Book tip: Modern C++ Design
Break?
Surf time!
Let’s have a look at a policized class
svn.boost.org/svn/boost/sandbox/flyweight/libs/flyweight/doc/
Policies vs traits
Type traits and policies are only instantiated as base-classes
Neither type traits nor policy inheritance model IS-A
Policies can add state, type traits doesn’t
Policies can extend interfaces, type traits has no functions
Type traits is pure, compile time, entity -> information mapping
You can use type traits as policy parameters to model concepts
SFINAE
template<class IntType>
IntType triple(IntType i)
{ return 3*i; }
void foo()
{
triple(”hej!”);
}
SFINAE
Substitution Failure Is Not An Error
template<bool, typename T = void>
struct enable_if {};
template<typename T>
struct enable_if<true, T> { typedef T type;
};
template<class T>
enable_if<is_integral<T>::type, T>::type
triple(T t) { ... }
SFINAE
If you ever need to limit a function template
www.boost.org/libs/utility/enable_if.html
Problem 1
Giving away too much
information
// Models Container
class MyClass
{
private:
typedef std::vector<int> InternalCollection;
public:
typedef InternalCollection::iterator iterator;
...
};
void user_code()
{
MyClass a;
int size = a.end() – a.begin(); // compiles cleanly, violates
promises
}
Problem 2
Requiring user code is
templated
template <class T> class Foo
{
Foo(const T& t);
void bar();
void baz();
void monkey(T t);
};
template <class T>
void user_code(Foo<T>& foo)
{
foo.bar();
foo.baz();
}
Solution: Type Erasure
class Foo
{
virtual void bar() = 0;
virtual void baz() = 0;
};
template <class T>
class FooImpl : public Foo
{
Foo(const T& t);
void bar();
void baz();
void monkey(T t);
};
void user_code(Foo& foo);
Solution to problem 1
template< class Value,
class CategoryOrTraversal,
class Reference = Value&,
class Difference = std::ptrdiff_t
>
class any_iterator;
Hack time!
Implement void_function
• Compatible with free functions
• Compatible with other functors
• Provide uniform type
Erase which type it was constructed from
Problems with concepts
Until C++0x comes, we have the following problems
• Incorrect arguments generate difficult error messages
• The documented requirements might not cover everything
• Code and documentation might drift out of sync
Reminder
The Boost Concept Checking Library provides:
•
•
•
•
A mechanism for inserting compile-time checks
A framework for specifying concept requirements
A mechanism for verifying concept coverage
Concept checking and archetype classes for STL concepts
Concept checks to help code users
Archetype checks to help require correct concepts
Surf time!
Let’s refresh our memory
www.boost.org/libs/concept_check/using_concept_check.htm
A taste of Metaprogramming
template <unsigned long N>
struct binary
{
static const unsigned long value =
binary<N/10>::value*2 + N%10;
};
template <>
struct binary<0>
{
static const unsigned long value = 0;
};
binary<101>::value
Summary first part
C++ is a multiparadigm language
GP+OO can achieve simplified, robust, decoupled systems
•
•
•
•
Concepts consists of
Invariants
Valid expressions
Associated types
Complexity guarantees
Summary second part
Quick taste of some patterns
•
•
•
•
•
•
•
•
Object generators
Type traits
Tag dispatching
CRTP
Policy classes
SFINAE
Type erasure
Concept checking
Get familiar with, not master
Thanks for listening
Slides available at
twiki.gameop.net/twiki/bin/view/Main/ModernC++InPractice
Download