More C++ Idioms

153
More C++ Idioms/Print Version 1 More C++ Idioms/Print Version Preface C++ has indeed become too "expert friendly" --- Bjarne Stroustrup, The Problem with Programming [1] , Technology Review, Nov 2006. Stroustrup's saying is true because experts are intimately familiar with the idioms in the language. With the increase in the idioms a programmer understands, the language becomes friendlier to him or her. The objective of this open content book is to present modern C++ idioms to programmers who have moderate level of familiarity with C++, and help elevate their knowledge so that C++ feels much friendlier to them. It is designed to be an exhaustive catalog of reusable idioms that expert C++ programmers often use while programming or designing using C++. This is an effort to capture their techniques and vocabulary into a single work. This book describes the idioms in a regular format: Name-Intent-Motivation-Solution-References, which is succinct and helps speed learning. By their nature, idioms tend to have appeared in the C++ community and in published work many times. An effort has been made to refer to the original source(s) where possible; if you find a reference incomplete or incorrect, please feel free to suggest or make improvements. The world is invited to catalog reusable pieces of C++ knowledge (similar to the book on design patterns by GoF). The goal here is to first build an exhaustive catalog of modern C++ idioms and later evolve it into an idiom language, just like a pattern language. Finally, the contents of this book can be redistributed under the terms of the GNU Free Documentation License. Aimed toward: Anyone with an intermediate level of knowledge in C++ and supported language paradigms Authors Sumant Tambe talk -- The initiator and lead contributor since July 2007. Many other C++ aficionados who continuously improve the writeup, examples, and references where necessary. Praise of the Book "Great and valuable work!" -- Bjarne Stroustrup (February, 2009) Table of Contents Note: synonyms for each idiom are listed in parentheses. 1. Adapter Template 2. Address Of 3. Algebraic Hierarchy 4. Attach by Initialization 5. Barton-Nackman trick 6. Base-from-Member 7. Boost mutant 8. Calling Virtuals During Initialization 9. Capability Query 10. Checked delete 11. Clear-and-minimize 12. Coercion by Member Template 13. Compile Time Control Structures 14. Computational Constructor 15. Concrete Data Type

Transcript of More C++ Idioms

More C++ Idioms/Print Version

1

More C++ Idioms/Print VersionPrefaceC++ has indeed become too "expert friendly" --- Bjarne Stroustrup, The Problem with Programming Technology Review, Nov 2006. [1] ,

Stroustrup's saying is true because experts are intimately familiar with the idioms in the language. With the increase in the idioms a programmer understands, the language becomes friendlier to him or her. The objective of this open content book is to present modern C++ idioms to programmers who have moderate level of familiarity with C++, and help elevate their knowledge so that C++ feels much friendlier to them. It is designed to be an exhaustive catalog of reusable idioms that expert C++ programmers often use while programming or designing using C++. This is an effort to capture their techniques and vocabulary into a single work. This book describes the idioms in a regular format: Name-Intent-Motivation-Solution-References, which is succinct and helps speed learning. By their nature, idioms tend to have appeared in the C++ community and in published work many times. An effort has been made to refer to the original source(s) where possible; if you find a reference incomplete or incorrect, please feel free to suggest or make improvements. The world is invited to catalog reusable pieces of C++ knowledge (similar to the book on design patterns by GoF). The goal here is to first build an exhaustive catalog of modern C++ idioms and later evolve it into an idiom language, just like a pattern language. Finally, the contents of this book can be redistributed under the terms of the GNU Free Documentation License. Aimed toward: Anyone with an intermediate level of knowledge in C++ and supported language paradigms

Authors Sumant Tambetalk -- The initiator and lead contributor since July 2007. Many other C++ aficionados who continuously improve the writeup, examples, and references where necessary.

Praise of the Book "Great and valuable work!" -- Bjarne Stroustrup (February, 2009)

Table of ContentsNote: synonyms for each idiom are listed in parentheses. 1. Adapter Template 2. Address Of 3. Algebraic Hierarchy 4. Attach by Initialization 5. Barton-Nackman trick 6. Base-from-Member 7. Boost mutant 8. Calling Virtuals During Initialization 9. Capability Query 10. Checked delete 11. Clear-and-minimize 12. Coercion by Member Template 13. Compile Time Control Structures 14. Computational Constructor 15. Concrete Data Type

More C++ Idioms/Print Version 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. Const auto_ptr Construct On First Use Construction Tracker Copy-and-swap Copy-on-write Counted Body (intrusive reference counting) Curiously Recurring Template Pattern Detached Counted Body (non-intrusive reference counting) Empty Base Optimization Emulated Exception enable-if Envelope Letter Erase-Remove Examplar Execute-Around Pointer Export Guard Macro Expression-template Fake Vtable Fast Pimpl Final Class Free Function Allocators Friendship and the Attorney-Client Function Object Generic Container Idioms Hierarchy Generation Include Guard Macro Inline Guard Macro Inner Class Int-To-Type Interface Class Iterator Pair Making New Friends Metafunction Move Constructor Multi-statement Macro Multiple Member Initialization Member Detector Named Constructor Named External Argument Named Loop (labeled loop) Named Parameter Named Template Parameters Nifty Counter (Schwarz Counter) Non-copyable Mixin Non-member get

2

61. Non-member Non-friend Function 62. Non-throwing swap

More C++ Idioms/Print Version 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. Non-Virtual Interface (Public Overloaded Non-Virtuals Call Protected Non-Overloaded Virtuals) nullptr Object Generator Object Template Overload Set Creation Parameterized Base Class (Parameterized Inheritance) Pimpl (Handle Body, Compilation Firewall, Cheshire Cat) Policy Clone (Metafunction wrapper) Policy-based Design Polymorphic Exception Recursive Type Composition Requiring or Prohibiting Heap-based Objects Resource Acquisition Is Initialization (RAII, Execute-Around Object, Scoped Locking) Resource Return Return Type Resolver Runtime Static Initialization Order Idioms Safe bool Scope Guard Substitution Failure Is Not An Error (SFINAE) Shortening Long Template Names Shrink-to-fit Small Object Optimization Smart Pointer Storage Class Tracker Tag Dispatching Temporary Base Class Temporary Proxy The result_of technique Thin Template Traits Type Erasure Type Generator (Templated Typedef) Type Safe Enum Type Selection Virtual Constructor Virtual Friend Function

3

Address OfIntentFind address of a object of a class that has an overloaded unary ampersand (&) operator.

MotivationC++ allows overloading of unary ampersand (&) operator for class types. The return type of such an operator need not be the actual address of the object. Intentions of such a class are highly debatable but the language allows it nevertheless. Address-of idiom is way to find the real address of an object irrespective of the overloaded unary

More C++ Idioms/Print Version ampersand operator and its access protection. In the example below, main function fails to compile because operator & of nonaddressable class is private. Even if it were accessible, a conversion from its return type double to a pointer would not been possible or meaningful. class nonaddressable { public: typedef double useless_type; private: useless_type operator&() const; }; int main(void) { nonaddressable na; nonaddressable * naptr = &na; // Compiler error here. return 0; }

4

Solution and Sample CodeThe Address-of idiom retrieves the address of an object using a series of casts.template T * addressof(T & v) { return reinterpret_cast(& const_cast(reinterpret_cast(v))); } int main(void) { nonaddressable na; nonaddressable * naptr = addressof(na); // No more compiler error.

return 0; }

More C++ Idioms/Print Version

5

Known Uses Boost addressof utility [2]

Algebraic HierarchyIntentTo hide multiple closely related algebraic abstractions (numbers) behind a single generic abstraction and provide a generic interface to it.

Also Known As State (Gamma et al.)

MotivationIn pure object-oriented languages like Smalltalk, variables are run-time bindings to objects that act like labels. Binding a variable to an object is like sticking a label on it. Assignment in these languages is analogous to peeling a label off one object and putting it on another. On the other hand, in C and C++, variables are synonyms for addresses or offsets instead of being labels for objects. Assignment does not mean re-labelling, it means overwriting old contents with new one. Algebraic Hierarchy idiom uses delegated polymorphism to simulate weak variable to object binding in C++. Algebraic Hierarchy uses Envelope Letter idiom in its implementation. The motivation behind this idiom is to be able write code like the one below. Number Number Number Number n1 n2 n3 n2 = = = = Complex (1, 2); // Label n1 for a complex number Real (10); // Label n2 for a real number n1 + n2; // Result of addition is labelled n3 n3; // Re-labelling

Solution and Sample CodeComplete code showing implementation of Algebraic Hierarchy idiom is shown below. #include using namespace std; struct BaseConstructor { BaseConstructor(int=0) {} }; class RealNumber; class Complex; class Number; class Number { friend class RealNumber; friend class Complex; public: Number (); Number & operator = (const Number &n);

More C++ Idioms/Print Version Number (const Number &n); virtual ~Number(); virtual Number operator + (Number const &n) const; void swap (Number &n) throw (); static Number makeReal (double r); static Number makeComplex (double rpart, double ipart); protected: Number (BaseConstructor); private: void redefine (Number *n); virtual Number complexAdd (Number const &n) const; virtual Number realAdd (Number const &n) const; Number *rep; short referenceCount; }; class Complex : public Number { friend class RealNumber; friend class Number; Complex (double d, double e); Complex (const Complex &c); virtual ~Complex (); virtual Number operator + (Number const &n) const; virtual Number realAdd (Number const &n) const; virtual Number complexAdd (Number const &n) const; double rpart, ipart; }; class RealNumber : public Number { friend class Complex; friend class Number; RealNumber (double r); RealNumber (const RealNumber &r); virtual ~RealNumber (); virtual Number operator + (Number const &n) const;

6

More C++ Idioms/Print Version virtual Number realAdd (Number const &n) const; virtual Number complexAdd (Number const &n) const; double val; }; /// Used only by the letters. Number::Number (BaseConstructor) : rep (0), referenceCount (1) {} /// Used by user and static factory functions. Number::Number () : rep (0), referenceCount (0) {} /// Used by user and static factory functions. Number::Number (const Number &n) : rep (n.rep), referenceCount (0) { cout referenceCount++; } Number Number::makeReal (double r) { Number n; n.redefine (new RealNumber (r)); return n; } Number Number::makeComplex (double rpart, double ipart) { Number n; n.redefine (new Complex (rpart, ipart)); return n; } Number::~Number() { if (rep && --rep->referenceCount == 0) delete rep; }

7

More C++ Idioms/Print Version

8

Number & Number::operator = (const Number &n) { cout swap (temp); return *this; } void Number::swap (Number &n) throw () { std::swap (this->rep, n.rep); } Number Number::operator + (Number const &n) const { return rep->operator + (n); } Number Number::complexAdd (Number const &n) const { return rep->complexAdd (n); } Number Number::realAdd (Number const &n) const { return rep->realAdd (n); } void Number::redefine (Number *n) { if (rep && --rep->referenceCount == 0) delete rep; rep = n; } Complex::Complex (double d, double e) : Number (BaseConstructor()), rpart (d), ipart (e) { cout rpart, this->ipart + cn->ipart); } RealNumber::RealNumber (double r) : Number (BaseConstructor()), val (r) { cout ipart); } namespace std { template void swap (Number & n1, Number & n2) { n1.swap (n2); } } int main (void) { Number n1 = Number::makeComplex (1, 2); Number n2 = Number::makeReal (10); Number n3 = n1 + n2; cout (p); }

int main(void) { std::pair p(1.34, 5);

std::cout init (); return ptr; } A non-template version of this approach can be found here. The factory function can be moved inside base class but it has to be static. class Base { public: template static std::auto_ptr Create (Parameter p) { std::auto_ptr ptr (new D (p)); ptr->init (); return ptr; } }; int main () { std::auto_ptr b = Base::Create ("para"); } Constructors of class Derived should be made private to prevent users from accidentally using them. Interfaces should be easy to use correctly and hard to use incorrectly - remember? The factory function should then be friend of the derived class. In case of member create function, Base class can be friend of Derived. Without using two phase initialization Achieving desired effect using a helper hierarchy is described here but an extra class hierarchy has to be maintained, which is undesirable. Passing pointers to static member functions is C'ish. Curiously Recurring Template Pattern idiom can be useful in this situation. class Base { }; template class InitTimeCaller : public Base { protected:

17

More C++ Idioms/Print Version InitTimeCaller () { D::foo (); D::bar (); } }; class Derived : public InitTimeCaller { public: Derived () : InitTimeCaller () { cout