📄 Page
1
(This page has no text content)
📄 Page
2
Beautiful C++ 30 Core Guidelines for Writing Clean, Safe and Fast Code J. Guy Davidson Kate Gregory
📄 Page
3
Selected C++ Core Guidelines This content is currently in development.
📄 Page
4
Contents Selected C++ Core Guidelines Foreword Preface Acknowledgments About the Authors Section 1 Bikeshedding is bad Chapter 1.1. P.2: Write in ISO Standard C++ Chapter 1.2. F.51: Where there is a choice, prefer default arguments over overloading Chapter 1.3. C.45: Don’t define a default constructor that only initializes data members; use in-class member initializers instead Chapter 1.4. C.131: Avoid trivial getters and setters Chapter 1.5. ES.10: Declare one name (only) per declaration Chapter 1.6. NR.2: Don’t insist to have only a single return-statement in a function Section 2 Don’t hurt yourself Chapter 2.1. P.11: Encapsulate messy constructs, rather than spreading through the code Chapter 2.2. I.23: Keep the number of function arguments low Chapter 2.3. I.26: If you want a cross-compiler ABI, use a C-style subset Chapter 2.4. C.47: Define and initialize member variables in the order of member declaration
📄 Page
5
Chapter 2.5. CP.3: Minimize explicit sharing of writable data Chapter 2.6. T.120: Use template metaprogramming only when you really need to Section 3 Stop using that Chapter 3.1. I.11: Never transfer ownership by a raw pointer (T*) or reference (T&) Chapter 3.2. I.3: Avoid singletons Chapter 3.3. C.90: Rely on constructors and assignment operators, not memset and memcpy Chapter 3.4. ES.50: Don’t cast away const Chapter 3.5. E.28: Avoid error handling based on global state (e.g. errno) Chapter 3.6. SF.7: Don’t write using namespace at global scope in a header file Section 4 Use this new thing properly Chapter 4.1. F.21: To return multiple “out” values, prefer returning a struct or tuple Chapter 4.2. Enum.3: Prefer class enums over “plain” enums Chapter 4.3. ES.5: Keep scopes small Chapter 4.4. Con.5: Use constexpr for values that can be computed at compile time Chapter 4.5. T.1: Use templates to raise the level of abstraction of code Chapter 4.6. T.10: Specify concepts for all template arguments Section 5 Write code well by default
📄 Page
6
Chapter 5.1. P.4: Ideally, a program should be statically type safe Chapter 5.2. P.10: Prefer immutable data to mutable data Chapter 5.3. I.30: Encapsulate rule violations Chapter 5.4. ES.22: Don’t declare a variable until you have a value to initialize it with Chapter 5.5. Per.7: Design to enable optimization Chapter 5.6. E.6: Use RAII to prevent leaks Envoi Afterword
📄 Page
7
Table of Contents Selected C++ Core Guidelines Foreword Preface Acknowledgments About the Authors Section 1 Bikeshedding is bad Chapter 1.1. P.2: Write in ISO Standard C++ What is ISO Standard C++? Encapsulating variations Learning the old ways Staying on top of developments to the standard Chapter 1.2. F.51: Where there is a choice, prefer default arguments over overloading Introduction Refining your abstraction: Additional arguments h3r overloading? The subtleties of overload resolution Back to the example The unambiguous nature of default arguments Alternatives to overloading Sometimes you must overload Summary Chapter 1.3. C.45: Don’t define a default constructor that only initializes data members; use in-class member initializers instead
📄 Page
8
Why have default constructors anyway? How do you initialize a data member? What happens when two people maintain a class? Summary Chapter 1.4. C.131: Avoid trivial getters and setters An archaic idiom Abstraction Mere Encapsulation Class Invariants Nouns and Verbs Summary Chapter 1.5. ES.10: Declare one name (only) per declaration Let me introduce you Backward compatibility Writing clearer declarations Structured binding Summary Chapter 1.6. NR.2: Don’t insist to have only a single return-statement in a function Rules evolve Ensuring cleanup Using RAII Writing good functions Summary Section 2 Don’t hurt yourself
📄 Page
9
Chapter 2.1. P.11: Encapsulate messy constructs, rather than spreading through the code All in one gulp What it means to encapsulate a messy construct The purpose of language and the nature of abstraction Levels of abstraction Abstraction by refactoring and drawing the line Summary Chapter 2.2. I.23: Keep the number of function arguments low How much should they earn? Simplifying matters through abstraction Do as little as possible, but no less Real-life examples Summary Chapter 2.3. I.26: If you want a cross-compiler ABI, use a C-style subset Creating libraries What is an ABI? Paring back to the absolute minimum Exception propagation Summary Chapter 2.4. C.47: Define and initialize member variables in the order of member declaration Summary Chapter 2.5. CP.3: Minimize explicit sharing of writable data Traditional execution model Wait, there’s more
📄 Page
10
Avoiding deadlocks and data races Setting aside locks and mutexes Summary Chapter 2.6. T.120: Use template metaprogramming only when you really need to std::enable_if => requires Summary Section 3 Stop using that Chapter 3.1. I.11: Never transfer ownership by a raw pointer (T*) or reference (T&) Using the free store The performance cost of smart pointers Using unadorned reference semantics gsl::owner Summary Chapter 3.2. I.3: Avoid singletons Global objects are bad Singleton Design Pattern Static initialization order fiasco How to hide a singleton But only one of these should ever exist Wait a moment… Summary Chapter 3.3. C.90: Rely on constructors and assignment operators, not memset and memcpy Chasing maximum performance
📄 Page
11
The horrendous overhead of the constructor The simplest possible class What is the standard talking about anyway? But what about memcpy? Never underestimate the compiler Summary Chapter 3.4. ES.50: Don’t cast away const Story time Dealing with rather more data The const firewall Implementing a dual interface Caching and lazy evaluation Two types of const Surprises with const Summary Chapter 3.5. E.28: Avoid error handling based on global state (e.g. errno) Error handling is hard C and errno Return codes Exceptions <system_error> Boost.Outcome Why is error handling so hard? Light at the end of the tunnel Summary Chapter 3.6. SF.7: Don’t write using namespace at global scope in a header file
📄 Page
12
Don’t do this Disambiguation Using using Where do the symbols go? An altogether more insidious problem Solving the problem of cluttered scope resolution operators The temptation and The Fall Summary Section 4 Use this new thing properly Chapter 4.1. F.21: To return multiple “out” values, prefer returning a struct or tuple The shape of a function signature Documenting and annotating Now you can return an object You can also return a tuple Passing and returning by non-const reference Summary Chapter 4.2. Enum.3: Prefer class enums over “plain” enums Constants Scoped enumerations Underlying type Implicit conversion Summary Postscript Chapter 4.3. ES.5: Keep scopes small The nature of scope
📄 Page
13
Block scope Namespace scope Class scope Function parameter scope Enumeration scope Template parameter scope Scope as context Summary Chapter 4.4. Con.5: Use constexpr for values that can be computed at compile time From const to constexpr Default C++ Using constexpr inline consteval constinit Summary Chapter 4.5. T.1: Use templates to raise the level of abstraction of code Story time Raising the level of abstraction Function templates and abstraction Class templates and abstraction Naming is hard Summary Chapter 4.6. T.10: Specify concepts for all template arguments How did we get here?
📄 Page
14
Constraining your parameters How to abstract your concepts Factoring via concepts Summary Section 5 Write code well by default Chapter 5.1. P.4: Ideally, a program should be statically type safe Type safety is a security feature of C++ Union Casting Unsigned Buffers and sizes Summary Chapter 5.2. P.10: Prefer immutable data to mutable data The wrong defaults const ness in function declarations Summary Chapter 5.3. I.30: Encapsulate rule violations Hiding the unsightly things in life Keeping up appearances Summary Chapter 5.4. ES.22: Don’t declare a variable until you have a value to initialize it with The importance of expressions and statements C-style declaration Declare-then-initialize
📄 Page
15
Maximally delayed declaration Localization of context-specific functionality Eliminating state Summary Chapter 5.5. Per.7: Design to enable optimization Maximizing the frame rate Working further from the metal Optimization through abstraction Summary Chapter 5.6. E.6: Use RAII to prevent leaks Deterministic destruction Leaking away files Why are we bothering? This all seems a bit much: Future possibilities Where can I get this? Envoi Afterword
📄 Page
16
Foreword I enjoyed reading this book. I enjoyed it especially because it presents the C++ Core Guidelines (CG) very differently from how the CG itself does it. The CG presents its rules relatively tersely in a fixed format. The CG rules are often expressed in language-technical terms with an emphasis on enforcement through static analysis. This book tells stories, many coming from the games industry based on the evolution of code and techniques over decades. It presents the rules from a developer’s point of view with an emphasis on what benefits can be obtained from following the rules and what nightmares can result from ignoring them. There are more extensive discussions of the motivation for rules than the CG themselves can offer. The CG aims for a degree of completeness. Naturally, a set of rules for writing good code in general cannot be complete, but the necessary degree of completeness implies that the CG are not meant for a systematic read. I recommend the introduction and the philosophy section to get an impression of the aims of the CG and its conceptual framework. However, for a selective tour of the CG guided by taste, perspective, and experience, read the book. For true geeks, it is an easy and entertaining read. For most software developers, it offers something new and useful. —Bjarne Stroustrup, June 2021
📄 Page
17
Preface The complexity of writing C++ is diminishing with each new standard and each new piece of teaching literature. Conferences, blogs, and books abound, and this is a good thing. The world does not have enough engineers of sufficient quality to solve the very real problems we face. Despite the continuing simplification of the language, there is still much to learn about how to write good C++. Bjarne Stroustrup, the inventor of C++, and Herb Sutter, the convenor of the standards body that maintains C++, have devoted considerable resources to creating teaching materials for both learning C++ and writing better C++. These volumes include The C++ Programming Language1 and A Tour of C++,2 as well as Exceptional C++3 and C++ Coding Standards.4 1. Stroustrup, B, 2013. The C++ Programming Language, Fourth Edition. Boston: Addison- Wesley. 2. Stroustrup, B, 2018. A Tour of C++, Second Edition. Boston: Addison-Wesley. 3. Sutter, H, 1999. Exceptional C++. Reading, MA: Addison-Wesley. 4. Sutter, H, and Alexandrescu, A, 2004. C++ Coding Standards. Boston: Addison-Wesley. The problem with books, even this modest volume, is that they represent a snapshot in time of the state of affairs, yet C++ is a continuously evolving language. What was good advice in 1998 may no longer be such a smart idea. An evolving language needs an evolving guide. The guidelines provide excellent, simple advice for improving your C++ style such that you can write correct, performant, and efficient code at your first attempt. An online resource, C++ Core Guidelines,5 was launched at the CppCon Conference in 2015 by Bjarne Stroustrup and Herb Sutter during their two6 keynote7 talks. The guidelines provide excellent, simple advice for improving your C++ style such that you can write correct, performant, and
📄 Page
18
efficient code at your first attempt. It is the evolving guide that C++ practitioners need, and the authors will be delighted to review pull requests with corrections and improvements. Everyone, from beginners to veterans, should be able to follow its advisories. 5. Isocpp.github.io. 2021. C++ Core Guidelines. [online] Available at: <https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines> [Accessed 16 July 2021]. 6. Youtube.com. 2021. CppCon 2015: Bjarne Stroustrup “Writing Good C++14”. [online] Available at: <https://www.youtube.com/watch?v=1OEu9C51K2A> [Accessed 16 July 2021]. 7. Youtube.com. 2021. CppCon 2015: Herb Sutter “Writing Good C++14... By Default.” [online] Available at: <https://www.youtube.com/watch?v=hEx5DNLWGgA> [Accessed 16 July 2021]. At the end of February 2020, on the #include discord,8 Kate Gregory canvassed interest in producing a book about the Core Guidelines and I cautiously jumped at the chance. Kate gave a talk at CppCon 20179 where she looked at just 10 of the Core Guidelines. I share her enthusiasm for promoting better programming. I am the Head of Engineering Practice at Creative Assembly, Britain’s oldest and largest game development studio, where I have spent a lot of the past 20-plus years helping to turn our fine engineers into even greater engineers. It is our observation that, despite the accessibility and simplicity of the Core Guidelines, many developers are not especially familiar with them. We want to promote their use, and we decided to write this book because there is not enough literature about them. 8. #include <C++>. 2021. #include <C++>. [online] Available at: <https://www.includecpp.org/> [Accessed 16 July 2021]. 9. Youtube.com. 2021. CppCon 2017: Kate Gregory “10 Core Guidelines You Need to Start Using Now.” [online] Available at: <https://www.youtube.com/watch?v=XkDEzfpdcSg> [Accessed 16 July 2021]. The Core Guidelines can be found at https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines. They are absolutely jam-packed with excellent advice: indeed, it is hard to know where to start. Reading from the top to the bottom is feasible, but it is a tall order to grasp the entire set of advisories without repeated reading. They are organized into 22 major sections with titles like “Interfaces,” “Functions,” “Concurrency,” and so on. Each section is composed of individual guidelines, sometimes a few, sometimes dozens. The guidelines are identified by their major section letter, then their number within the section, separated by a period. For example, “F.3: Keep functions short and simple” is the third guideline in section F, “Functions.”
📄 Page
19
Each guideline is ordered in a similar way. It will start with the title of the guideline, which is presented as an action (do this, don’t do this, avoid this, prefer this) followed by a reason and some examples, and possibly an exception to the guideline. Finally, there will be a note on how to enforce the guideline. Enforcement notes range from advice to authors of static analysis tools to hints on how to conduct a code review. There is a skill to reading them, it turns out; deciding which ones to prioritize in your own code is a matter of personal discovery. Let us show you how to start taking advantage of their wisdom. There are some sharp edges in C++ as well as some dusty corners that are not visited so often in modern C++. We want to steer you away from these. We want to show you that C++ does not have to be difficult, complex, or something that most developers cannot be trusted with. About This Book In this book we offer what we consider to be 30 of the best C++ Core Guidelines. By thoroughly explaining these guidelines we hope that you will at least abide by them, even if you decide against investigating the remainder. The set that we have chosen are not necessarily the most important. However, they are certainly the set that will change your code for the better immediately. Of course, we hope that you will also see that there are many other good guidelines you could also follow. We hope that you will read the remainder and try them out in your code. Just as the Core Guidelines are aimed at all C++ developers with all levels of experience, so is this book aimed at the same set of people. The material does not increase in complexity as the book progresses, nor is there a required order in which to read the chapters. They are independent of one another, although they may explicitly refer to other chapters. We kept each chapter to about three thousand words, so you may decide that this is a bedside volume rather than a textbook. The purpose is not to teach you C++, but to advise you how to improve your style. We divided the guidelines into five sections of six chapters, following Kate’s original presentation to CppCon in 2017. In Section 1, “Bikeshedding is bad,” we present guidelines that allow you to simply
📄 Page
20
make a decision about when to do A or B, for some particular set of As and Bs, and move on with the minimum of fuss and argument. “Bikeshedding”10 derives from C. Northcote Parkinson’s “law of triviality,” an argument that organization members typically give disproportionate weight to trivial issues, such as the color to paint a bikeshed compared to the testing criteria for the nuclear power station to which it is attached, because it is the one thing everyone knows something about. 10. 2021. [online] Available at: <https://exceptionnotfound.net/bikeshedding-the-daily-software- anti-pattern/> [Accessed 16 July 2021]. In Section 2, “Don’t hurt yourself,” we present guidelines for preventing personal injury while writing code. One of the problems with the residual complexity of C++ is that there are several places where you can shoot yourself in the foot with ease. For example, while it is legal to populate a constructor initialization list in any order, it is never wise to do so. Section 3 is named “Stop using that” and deals with parts of the language that are retained for backward compatibility reasons, along with pieces of advice that used to be valuable, but which have been superseded by developments in the language. As C++ evolves, things that seemed like a good idea at the time occasionally reveal themselves as rather less valuable than was originally expected. The standardization process fixes these things, but everyone needs to stay informed about them because you may come across examples if you find yourself working with a legacy codebase. C++ offers a guarantee of backward compatibility: code written 50 years ago in C should still compile today. Section 4 follows on from this with the title “Use this new thing properly.” Things like concepts, constexpr, structured binding, and so on need care when being deployed. Again, C++ is an evolving standard and new things appear with each release, all of which require some teaching to back them up. Although this text does not aim to teach you the new features of C++20, these guidelines do give you a flavor of how to apprehend novel features. Section 5, the final section, is titled “Write code well by default.” These are simple guidelines that, if followed, will result in you generating good code without having to think too hard about what is going on. They lead to the production of good idiomatic C++ which will be understood and appreciated by your colleagues.