2. Topics Covered
▪ Introduction
▪ RAII
▪ Attorney client
▪ Virtual friend functions
▪ Base from member
▪ Address of
▪ PIMPL
▪ COW
▪ SFINAE and Type Traits
▪ Tag dispatching
4. RAII (Resource Acquisition Is Initialization)
▪ A fundamental programming idiom used
to automate resource management.
5. RAII
▪ Resources are acquired during initialization, when there is no
chance of them being used before they are available, and
released with the destruction of the same objects, which is
guaranteed to take place even in case of errors.
Typical uses :
▪ Obtaining memory from new, malloc, IMalloc::Alloc, etc. Which
must be matched with a call to delete, free, IMalloc::Free, etc.
▪ Opening a file, which must be closed.
▪ Synchronization primitives like monitors, critical sections, etc.
Which must be released to allow other threads to obtain them.
▪ Opening a network connection, database connection, Creating
a Windows GDI object, Opening a Registry handle, etc.
▪ Example : STL’s std::auto_ptr
6. Attorney-Client - The limitation
▪ Control the granularity of access to the implementation
details of a class.
▪ Friends in C++ is an all-or-nothing proposition.
A friend declaration in C++ gives complete access to the
internals of a class.
class Foo
{
private:
void A(int a);
void B(float b);
void C(double c);
friend class Bar;
};
class Bar
{
// This class needs access to Foo::A and Foo::B only.
// C++ friendship rules, however, give access to all the private members
of Foo.
};
7. Attorney-Client - The workaround
class Attorney;
class Client
{
private:
void A(int a);
void B(float b);
void C(double c);
friend class Attorney;
};
class Attorney
{
private:
Client c;
static void callA(int a) { c.A(a); }
static void callB(float b) { c.B(b); }
friend class Bar;
};
class Bar
{// Bar now has access to only Client::A and Client::B
through the Attorney.
};
8. Virtual friend functions
▪ Simulate a virtual friend function.
class Base
{
public:
friend ostream& operator << (ostream& o, const Base& b);
// ...
protected:
virtual void print(ostream& o) const
{ ... }
};
inline std::ostream& operator<< (std::ostream& o, const Base& b)
{
b.print(o); // delegate the work to a polymorphic member
function.
return o;
}
class Derived : public Base
{
protected:
virtual void print(ostream& o) const
{ ... }
};
9. Base from member
▪ To initialize a base class from a data-member of the
derived class.
▪ Data member cannot be initialized before initializing the
base class.
▪ Ron Klatchko and Dietmar Kühl developed a way around.
▪ Base classes are initialized in the order they are declared.
10. Base from member - The limitation
namespace std {
class streambuf;
class ostream {
explicit ostream(std::streambuf * buf);
//...
};
}
class fdoutbuf // A customization of streambuf
: public std::streambuf
{
public:
explicit fdoutbuf( int fd );
//...
};
class fdostream
: public std::ostream
{
protected:
fdoutbuf buf;
public:
explicit fdostream( int fd )
: buf( fd ), std::ostream( &buf )
// This is not allowed: buf can't be initialized before std::ostream.
// std::ostream needs a std::streambuf object defined inside fdoutbuf.
{}
};
11. Base from member - The workaround
class fdoutbuf_pbase // A newly introduced class
{
public:
fdoutbuf sbuffer; // The member moved 'up' the hierarchy.
explicit fdoutbuf_pbase(int fd) : sbuffer(fd)
{}
};
class fdostream : protected virtual fdoutbuf_pbase
, public std::ostream
{
public:
explicit fdostream(int fd) : fdoutbuf_pbase(fd), //
Initialize the newly added base before std::ostream.
std::ostream(&sbuffer) // Now safe to pass the pointer
{}
//...
};
12. Address of - The limitation
▪ Find address of an object of a class that has an
overloaded unary ampersand (&) operator.
▪ This function is already in the <memory> header of the
new C++ standard C++11.
14. PIMPL (Private Implementation)
▪ Described by Herb Sutter.
▪ To hide the implementation details of an interface from
ordinary clients.
▪ Technique to minimize coupling via the separation of
interface and implementation.
▪ Also known as Bridge pattern, Cheshire Cat, Handle/
Body.class Add
{
int x;
int y;
public:
Add() { }
int add();
};
15. #include “AddDetails.h"
#include " Add.h"
Add ::Add()
{
ptr = new AddDetails();
}
Add::~ Add()
{
delete ptr;
}
int Add::add()
{
return ptr -> add();
}
Add.cpp
class AddDetails;
class Add
{
AddDetails* ptr;
public:
Add();
~Add();
int add();
};
Add.h
#include " AddDetails.h";
int AddDetails::add()
{
return x+y;
}
AddDetails.cpp
class AddDetails
{
int x;
int y;
public:
AddDetails() { }
int add();
};
AddDetails.h
#include “Add.h"
int _tmain(int argc, _TCHAR* argv[])
{
Add obj;
int result = obj.add();
return 0;
}
Main.cpp
PIMPL
16. COW (Copy On Write)
▪ Copying an object can sometimes cause a performance
penalty.
▪ Achieve lazy copy optimization, do the work just when you
need because of efficiency.
▪ If objects are frequently copied but infrequently modified
later.
template <class T>
class CowPtr
{
public:
typedef boost::shared_ptr<T> RefPtr;
private:
RefPtr m_sp;
void detach()
{
T* tmp = m_sp.get();
if( !( tmp == 0 || m_sp.unique() ) ) {
m_sp = RefPtr( new T( *tmp ) );
}
}
public:
18. COW (Copy On Write)
char & String::operator[](int)
CowPtr<String> s1 = "Hello";
char &c = s1->operator[](4); // Non-const detachment does
nothing here
CowPtr<String> s2(s1); // Lazy-copy, shared state
c = '!'; // Uh-oh
19. SFINAE (Substitution Failure Is Not An Error)
▪ David Vandevoorde first introduced the acronym SFINAE.
▪ It allows a template to determine certain properties of its
template arguments at instantiation time.
▪ Many developers found the behavior useful for compile-
time introspection.
int negate(int& i) { return -i; }
template <class F>
typename F::result_type negate(const F& f) { return -f(); }
negate(1);
▪ Type Traits are classes to obtain type information on
compile-time.
20. SFINAE
#include <type_traits>
#include <iostream>
template<class T> // foo1 overloads are enabled via the return type
typename std::enable_if<std::is_floating_point<T>::value, T>::type
foo1(T t)
{
std::cout << "foo1: floatn"; return t;
}
//Using helper type
template<class T> std::enable_if_t<std::is_integral<T>::value, T> foo1(T t)
{
std::cout << "foo1: intn"; return t;
}
template<class T>// foo2 overload is enabled via a parameter
T foo2(T t, typename std::enable_if<std::is_integral<T>::value >::type* =0)
{
return t;
}
template<class T , // foo3 overload is enabled via a template parameter
class = typename std::enable_if<std::is_integral<T>::value>::type >
T foo3(T t) // note, function signature is unmodified
{
return t;
}
21. int main()
{
foo1(1.2); // OK, calls the first version of foo1()
foo1(10); // OK, calls the second version of foo1()
// foo2(0.1); // compile-time error
foo2(7); // OK
// foo3(1.2); // compile-time error
foo3(34); // OK
}
SFINAE
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T>
{ typedef T type; };
22. Tag Dispatching
▪ Tag dispatching is a technique for compile time
dispatching between a few overloaded functions by using
the properties of a type.
void work_fast(int i) { /*fast implementation*/ }
void work_fast(long l) { /*fast implementation*/ }
void work_slow(float f) { /*slow implementation*/ }
void work_slow(double d) { /*slow implementation*/ }
int main ()
{
int w; long x;
float y; double z;
work_fast(w); work_fast(x);
work_slow(y); work_slow(z);
return 0;
}
23. Tag Dispatching
▪ Tag dispatching is a technique for compile time
dispatching between a few overloaded functions by using
the properties of a type.
struct fast_speed_tag {};
struct slow_speed_tag {};
template <typename T>
void work_dispatch (const T &val, const slow_speed_tag&)
{
std::cout << "slow" << std::endl;
}
template <typename T>
void work_dispatch (const T &val, const fast_speed_tag&)
{
std::cout << "fast" << std::endl;
}
24. template <typename T>
struct traits // default
{
typedef slow_speed_tag speed;
};
template <>
struct traits<int> { // we would do the same for long etc
typedef fast_speed_tag speed;
};
template <typename T>
void work (const T &val)
{
work_dispatch(val, typename traits<T>::speed());
}
int main ()
{
int x;
float y;
work(x); // fast
work(y); // slow
return 0;
}
Tag Dispatching
25. ▪ One of the best examples of the Tag dispatch technique is
the implementation of std::advance() within STL iterators.
▪ The tag types are empty structs that are never used. As
such, the compiler will usually be able to optimize and
make them totally disappear from the compiled code –
which makes this technique even more appealing.
Tag Dispatching