1Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
C++ Coding Techniques
A collection of techniques for writing C++ code
2Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Outline for the session
• Building large systems– Why on earth do we bother?
• Programming Guidelines– Getting your code organized
• C++ Coding Idioms– Proven recepies
• C++ Gotcha's– Mistakes already made by others
3Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Goals when building large systems
The system should promote:• Reusability• Extensibility• Flexibility
4Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Reusability
• The system should consist of components that can be reused.
• Reusability is necessary to:– reduce the development time (longterm)– improve reliability of the system
5Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Extensibility
Extensibility is necessary to:• Support extensions to the original system• Support addition of new features
6Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Flexibility
• The system should not be rigid– A change in one component should not necessitate changes in other
components
• Note:– Dependencies are not necessarily explicit– Problems not caught by compiler– Problems not caught by testing
7Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Abstraction is everything!
• Encapsulation hides the internal implementation of a component
• Interfaces are used to provide methods for users to work with the component
• Inheritance is used to specialize an abstraction
8Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Encapsulation
• The most important concept in OOP– No encapsuling... no objectorientation
• Hides the internals of a component• Provides an abstract interface for component users
– All operations are through the interface
• Allow the implementation to change– No requirement on component users to change their code
9Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Encapsuling: example
class Writer {
public:
...
void write(int i);
void write(float f);
void write(const char* c_str);
...
private:
...
};
10Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Encapsuling: example of usage
class Point {
public:
void write(Writer *writer) const {
writer>write(x);
writer>write(y);
}
private:
int x, y;
};
11Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Encapsuling issues
• Encapsuling goals:– Maintain the object in a consistent state– Separate implementation and abstraction– Allow implementation to change
• Define methods for the abstraction• Do not define methods for the implementation
– I will demonstrate some examples in a few slides
12Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Encapsuling issues, contd.
• Do not expose the internal state– Keep private parts to yourself!– Do not expose internals even through accessor functions– Make users rely on implementation– May allow users to (accidentally) change internal state:
• Impossible to maintain consistentency• Here be dragons!
13Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
class Writer {public: ... void write(int i); void write(float f); void write(const char* c_str); ...private: WriteBuf m_buf;};
Encapsuling: example
Writer represents how data is written
WriteBuf represents where data is
written
14Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Encapsuling: advantages of this code
• Interface completely hides internal implementation– WriteBuf and Writer can has completely different interfaces
• Implementation can be changed freely– Without affecting the interface– Hence without changing any code that uses the interface
15Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Exposing internals: examples
• Expr::lhs() and Expr::rhs()– Method not defined for the abstraction– What if the expression is unary?
• Table::get_cache() and Table::clear_cache()
– Cache not part of the abstraction• Not a clearcut case: depending on definition
– Object users will rely on its existance– What if you want to (re)move it?
16Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
A digression into mutable
• Expr represents an expression
• Hold a cache to avoid recomputations
• Cache represents a value cache
• Cache is inside Expr class
class Expr { int compute () const { if (cache.invalid()) cache.set(...); return cache.val(); }
private: ... Cache cache;};
17Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
A digression into mutability
• Consider an Expr class with a cache inside and consider the following code:
int eval(const Expr& expr) { return expr.compute();}
$ g++ Wall ansi pedantic mutable.cc o mutablemutable.cc: In function 'int eval(const Expr&)':mutable.cc:33: error: no matching function for call to 'Expr::compute() const'mutable.cc:21: note: candidates are: int Expr::compute() <near match>
18Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
A digression into mutability
• Instance e is const
• Cache inside the object?
• Fields inside const objects are immutable
• Code will not compile
• Use mutable
Valueeval(const Expr& e){ return e.compute();}
19Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
A digression into mutable
• Cache or no cache is an implementation issue
• Implementation issues should not be visible in the interface
• Hence: use mutable
class Expr { int compute () const { if (cache.invalid()) cache.set(...); return cache.val(); }
private: ... mutable Cache cache;};
20Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Interfaces: what's it really about?
• Hiding the internals of a component• Policies for operating on an abstraction• Welldesigned intefaces promote:
– Reusability by abstracting the implementation– Flexibility by isolating independent parts from each other– Robustness by not allowing uncontrolled changes to the internals
21Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Interface Design Guidelines
• Interfaces should be designed from the perspective of the caller– Interfaces are called many times, but implemented just a few
• Document before implementation– Write uses cases!– The more the better– No distraction from implementation issues
22Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Usecase Driven Design
• Promotes complete interfaces– By writing use cases you put yourself in the role of the interface user
• Spot limitations in the interface• Encourages minimalistic interfaces
– Discourages creation of monolithic interfaces
23Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Inheritance
• Specialization of an class• Implement an abstraction
– A special form of specialization
• Strategic extensibility– Possible to extend the system according to the policies set by the
designer– Here policies are represented as classes with virtual functions
24Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Inheritance
WriteBuf
FileWriteBuf
SocketWriteBuf
StringWriteBuf
WriterwriteTo
25Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Inheritance: example
• A write buffer that data can be written to• Data is written as a block of bytes• If the buffer is in a “bad” state, no data will be written
class WriteBuf {public: virtual size_t write(void *buf, size_t sz) = 0; virtual bool is_good() const = 0;};
26Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Inheritance: example
class FileWriteBuf : public WriteBuf { FILE *m_file; // is private by defaultpublic: FileWriteBuf(char *name) : m_file(fopen(name, “a+”)) { }
size_t write(void *buf, size_t size) { if (m_file) return fwrite(buf, 1, size, m_file); return 0; }
bool is_good() const { return m_file != 0; }};
27Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
C++ Programming Techniques
A smörgåsbord of various C++ coding techniques and guidelines
28Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Trust the compiler
• The compiler is your friend. Trust the compiler.– Trust... but verify– Check assembler output if necessary
• Compilers good at straightforward code– Use constants– Avoid dark corners of the standard
• Easy to understand means easy to optimize
29Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Clever coding: an example
uchar foo_1(ulong a){ return !!(a << 11);}
uchar foo_2(ulong a) { if (a & 0x1FFFFF) return 1; return 0;}
testl $2097151,8(%ebp)popl %ebpsetne %alret
movl 8(%ebp), %eaxpopl %ebpsall $11, %eaxtestl %eax, %eaxsetne %almovzbl %al, %eaxret
30Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
No gratuitous writes to memory
• Writing to memory cost– Bus speed vs. CPU speed
• Write cachefriendly code• Global variables cost• Local variables are cheap
• Branches cost• Write pipeline friendly code• Recompute value?•
31Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Gratuitous writes to memory: example
• Function comp() does not touch memory
• First version forces global variable to be written to memory before call
• Second version loads global variable into auto variable and writes back after computation
int func_g() { g_var += comp(g_var); g_var += comp(g_var); g_var += comp(g_var); return g_var;}
Memory accesses: 10 to 13
int func_l() { int l_var = g_var; l_var += comp(l_var); l_var += comp(l_var); l_var += comp(l_var); return (g_var = l_var);}
Memory accesses: 5
32Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Include file handling
• Always use include guards!• Never ever allow any circular includes!
– Include guards do not protect against circular dependencies– Check this on a regular basis– Symptom: weird error messages about incomplete or undefined
structures and/or classes
• Organize module as:– Header file holding basic (nonclass) definitions– Header file holding class definitions– Code file holding implementations
33Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Module structure
• Header file– Include guard– Include what is used– Forward declare classes
• Source file– Module header file first– Include what is used– Standard header files last
– Forward declare classes
#ifndef FOO_H_INCLUDED#define FOO_H_INCLUDED
#include <cstdlib>
class Foo { ... };...#endif
#include “foo.h”#include <cstdlib>
Foo::Foo() { ... }
...
34Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
RAII Idiom: Resource allocation is initialization
• Resources are aquired on construction• Resources are released on destruction
– Instance is destroyed automatically when leaving scope– No risk of forgetting to release resource
• Example:– Writing two items that should be written atomically.
bool packet(Writer& out, const char* str, int val){ Writer::Mutex mutex(out); out.write(str); out.write(val); return out.is_good();}
35Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
The Law of the Big Three
• If a class defines a copy constructor, a copy assignment operator, or a destructor, it probably needs to define all three.
• All three manipulate ownership of the resources used by an object
• If some operation is not allowed, make the function private– Declaration is sufficient, no definition necessary
36Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Missing: Copy Constructor
class String {public: String(const char *c_str) : m_str(new char [strlen(c_str)]) { ... }
~String() { delete [] m_str; }
private: const char *m_str;};
String make_string() { String b(“Hello world”); return b;}
37Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Missing: assignment operator
class String {public: ... String(const String& that) : m_str(new char [strlen(that.str)]) { ... }
private: const char *m_str;};
String make_string() { String a(“Hello ”); String b; b = a; return b;}
38Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Assignment operator: standard idiom
class String {public: ... String& operator=(const String& that) { String copy(that); swap(copy); return *this; }
void swap(String& str) { std::swap(m_str, str.m_str); } ...};
39Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Assignment operator: idiom variations
// Slightly terser version of the previous oneString& String::operator(const String& that) { String(that).swap(*this); return *this;}
// More optimizer friendlyString& String::operator=(String that) { swap(that); return *this;}
40Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Accessibility vs. visibility
class MyClass {private: void store(int i) { ... }public: void store(double f) { ... }};
void foo() { MyClass c; c.store(0);}
error: 'void Cell::store(int)' is private
error: within this context
char *
NULL
41Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Virtual function handling
• Virtual functions should be private– Occationally protected, but never public
• Why?– Separate interface from implementation– Virtual functions are part of implementation– Implementation details should be hidden– Hence, virtual functions should be private
• Note:– Private functions can be overridden
42Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Virtual Function Usage: example
class Base {public: void tweak() { do_tweak(); }private: virtual void do_tweak() = 0;};
class Derived : public Base {private: virtual void do_tweak() { std::cout << “Hello World!” << std::endl; }};
43Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Gotcha's
Some pieces of code that does not work as you probably expect
44Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
#1: Constructor Calling
• Calling virtual functions from constructors or destructors
• Object doesn't exist until after constructor completes
• Not an implementation issue!– Language design issue
class A {public: A() { init(); }private: virtual init();};
class B : public A { ...private: virtual init() { ... }};
45Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
#2: Arrays of Objects
class A { ... };
class B : public A { ... int x; };
void foo(A a[]) { ... a[i] ... };
void bar() {
B b[20];
foo(b); // ?!?!?
}
46Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
#3: Initialization or... what?
class String {
public:
String(const char *str) { ... }
};
class Concat : public Expr {
public:
Concat(const String& a,
const String& b) { ... }
String value() const { ... }
};
47Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
#3: Initialization or... what?
• If something looks like a declaration, it is• Parantheses are allowed around parameter names• str is declared as function accepting two String (instances)
and returning a Concat (instance)
error: request for member 'value' in 'str', which is of nonclass type 'Concat ()(String, String)'
int concat(char *a, char *b) { Concat str(String(a), String(b)); return str.value();}
48Copyright 2005 MySQL ABThe World’s Most Popular Open Source Database
Recommended reading
• Guru of the Week by Herb Sutterhttp://www.gotw.ca/
• Exceptional C++ Style by Herb Sutter• Modern C++ Programming by Andrei Alexandrescu• C++ Templates: The Complete Guide by David
Vandevoorde and Nicolai M. Josuttis– Not for the fainthearted