Adam B. Singer

Practical C++ Design

From Programming to Architecture

2nd ed.
Adam B. Singer
The Woodlands, TX, USA
ISBN 978-1-4842-7406-4e-ISBN 978-1-4842-7407-1
© Adam Singer 2022
This work is subject to copyright. All rights are reserved by the Publisher, whether the whole or part of the material is concerned, specifically the rights of translation, reprinting, reuse of illustrations, recitation, broadcasting, reproduction on microfilms or in any other physical way, and transmission or information storage and retrieval, electronic adaptation, computer software, or by similar or dissimilar methodology now known or hereafter developed.
The use of general descriptive names, registered names, trademarks, service marks, etc. in this publication does not imply, even in the absence of a specific statement, that such names are exempt from the relevant protective laws and regulations and therefore free for general use.
The publisher, the authors and the editors are safe to assume that the advice and information in this book are believed to be true and accurate at the date of publication. Neither the publisher nor the authors or the editors give a warranty, expressed or implied, with respect to the material contained herein or for any errors or omissions that may have been made. The publisher remains neutral with regard to jurisdictional claims in published maps and institutional affiliations.

This Apress imprint is published by the registered company APress Media, LLC part of Springer Nature.

The registered company address is: 1 New York Plaza, New York, NY 10004, U.S.A.

For Terri, Caroline, and Rebecca.

Preface

Motivation of the Author

Throughout my career, I have mentored both students and fellow employees in programming, and many of them have suggested that I write my thoughts down in book form. However, I have typically responded with the rebuttal that I felt I had nothing novel to present. Being a largely self-taught programmer, I have always been able to rattle off a long list of books from which I have derived most of my knowledge. Therefore, what could I write about that has not already been said?

I came to realize, however, that the majority of books that I had encountered tended to focus only on pieces of design or implementation rather than taking a holistic approach. For example, if one wants to learn the C++ language, Stroustrup [30] or Lippman and Lajoie [19] are excellent references. For learning C++ best practices, one need only read the books by Sutter [31, 32, 33], Sutter and Alexandrescu [34], or Meyers [22, 21, 23]. Of course, learning to program extends well beyond C++. For data structures and algorithms, there are always the classics by Knuth [15, 16, 17] or the more accessible and concise book by Cormen et al. [10]. To learn object-oriented analysis and design, the book by Booch et al. [8] is an excellent reference. Of course, design patterns can be learned from Gamma et al. [11], and general programming practices can be learned from many books such as those by McConnell [20], Spinellis [29], or Kernighan and Pike [14].

Certainly, the deeper the specialty one seeks, the more esoteric the book one can find (and should eventually read). This book is not such a book. Rather, I have striven to write a book that operates from the premise that the reader already possesses a working knowledge of the information encased in works such as the aforementioned titles. In this book, I instead attempt to ground the reader’s theoretical knowledge of design through practice using a single case study.

Target Audience

As mentioned previously, the goal of this book is not to present any specific topic but rather to explore the interrelationship of often compartmentalized subjects. The successful combination of these components to form a cohesive, maintainable, elegant piece of software is, in essence, design. As such, this book is intended to target practicing professionals, in particular those who have several years of development experience but who do not yet possess sufficient experience to architect independently a large software project.

Because my intent is to emphasize utilization of one’s existing knowledge effectively to design software, I make little effort to explain individual topics in great depth. I believe too many books classified as intermediate to advanced fail because the author devotes too much content describing prerequisites. The result is a massive tome filled with unnecessary detail for the advanced reader, while the beginner is left with a long and complicated exposition that is still inaccessible because the beginner does not possess sufficient knowledge or experience to grasp the subject regardless of the amount of detail devoted to the description. I have, therefore, aimed for conciseness over completeness. Often, I simply refer the reader to relevant material rather than myself describe a background topic in great detail. While this strategy may indeed make this book difficult for beginners, I hope experienced professionals appreciate both the brevity of the book and its tone, which assumes the reader is competent at their craft.

Structure of the Book

Learning most tasks in programming requires hands-on experience and repetition; design is no exception. My opinion is that design is best learned through a combination of self-exploration and mentoring from an expert. For this reason, I have chosen to organize this book as a study in design through the detailed working of a case study. Instead of discussing design elements in the abstract, we will instead examine the concrete application of design principles as they relate to the design and construction of a simplistic (but not too simplistic) software project. Importantly, we will not only examine successful results that led to the final implementation, but we will also spend time considering alternatives, some of which are viable and others which are not. Where multiple solutions may suffice, choosing one over the other is often either a matter of context or just taste. Regardless, experience is usually the arbiter of such decisions, and hopefully this book will enable the reader to learn from the author’s experiences without having to repeat his mistakes. That is, I hope I have created a book that can serve as a self-contained master class in design.

The Case Study

To this point, I have not yet mentioned the subject of the case study central to this book. In my opinion, an ideal problem would be one that is not too daunting and yet not so trivial that it needs no design. Additionally, the domain of the problem should be readily understandable to any reader. For this reason, I have chosen to implement a stack-based, Reverse Polish Notation (RPN) calculator that I named pdCalc, short for practical design calculator. Functionally, pdCalc was inspired by the HP48S calculator, an old favorite calculator of mine from high school and college (which still sits on my desk today). I cannot imagine any reader being unfamiliar with the basic operations of a calculator, and making the calculator use RPN adds a little twist that I hope will make the project interesting. If your sole objective were to code a simple calculator with basic functionality, admittedly, the design of pdCalc is overkill, and more code is written from scratch than is strictly necessary. Remember, pdCalc’s primary purpose is to serve as an instructional tool – a reduced-size representation of a real project. That pdCalc happens to be a functioning RPN calculator is a secondary objective.

Language Selection

Design as an abstract concept can be taught in the absence of a particular programming language. However, once committed to a concrete example, a specific language must be chosen for the implementation. I decided to write the case study exclusively using C++. While every line of the program does not appear in the text, all of the source code is available for the reader to examine. Despite this book’s primary focus on design, reading the source code is a good way to learn how implementation details in a specific language enable or, at least, facilitate a chosen design. The source code also serves as a high-quality (I hope) exemplar of a modern C++ implementation of a complete user application. I highly recommend reading and modifying the source code in conjunction with reading the text.

The decision to use C++ does not imply that C++ is the best choice for all programs, and, in fact, it may not even be the best choice for the program examined in this book. However, to ground the abstraction, a concrete language had to be selected. I chose C++ because it is standardized, widely deployed, and available at zero cost on many platforms. Selfishly, I also chose C++ because it is my most proficient language. While I could, perhaps, have chosen another language meeting the aforementioned objective criteria (e.g., Python), the resulting code would probably have been functional but nonidiomatic due to my relative lack of expertise.

During the writing of the first edition of this book, C++0x was ratified as C++11 and then updated as C++14. C++11, with smart pointers, move semantics, lambdas, variadic templates, and a host of other new features, fundamentally changed how one could express a design in the C++ language. My objective at the time was to avoid incorporating modern C++ features where they were inappropriate just to demonstrate usage. Instead, I sought to highlight how these new language elements could be used effectively in the design of a large-scale program. Hopefully, I achieved my objective.

Design and architecture have not changed significantly since the publication of the first edition of this book, so why write a second edition? If this were an abstract design book, such an update would be unnecessary. However, since the publication of the first edition, C++17 and C++20 were both ratified. Because the book’s case study is accompanied by a complete implementation, expressing a concrete design using C++ has evolved with new language developments. While C++17 added several new implementation features, none of these features substantially impacted the ability to express designs in the language. C++20, on the other hand, is different.

C++20, maybe even more so than C++11, fundamentally changes how designs may be expressed in C++. In particular, four new major language features were added: ranges, coroutines, concepts, and modules, all of which make, at least, a brief appearance in the case study. The ranges library does not fundamentally alter program design. It does, however, fundamentally change the way algorithms can be implemented, especially with the better integration of the functional programming paradigm as enabled through views. Coroutines add cooperative multitasking to the C++ language. We will see that even in a single-threaded program such as the one described in this book, coroutines can be used to express an idea from the first edition differently. Concepts change the way generic programs are constructed. This book’s design does not make extensive use of templates; therefore, concepts make only the briefest of appearances. Lastly, C++20 finally brings modularization to C++ as a formal language construct. This change drastically impacts how code can be organized, including, in many instances, completely eliminating the need for header files. We will explore the usage of C++ modules, and their interaction with dynamically linked libraries, in some depth.

In the second edition, I again endeavor to illustrate the interplay between implementation and design and hence aim to demonstrate how language features enable the more natural expression of design paradigms. Where the design usage of modern C++ language or library features are generalizable beyond the case study, I have chosen to highlight these points in a sidebar. Sidebars are inclusive of all new language features since C++11.

GUI Framework, Tooling, External Dependencies, and Compilers

While graphical user interfaces (GUI) are not the primary focus of this book, our program will eventually acquire one; hence, selecting a particular GUI framework was necessary. I selected Qt for this purpose and successfully tested the code against Qt versions 5 and 6. As with C++, Qt is widely deployed, cross platform, and free. As with C++, again, selfishly, I chose Qt because I am experienced using desktop Qt and personally enjoy using it. A big advantage of using Qt is that it makes nearly all of the source code for the project platform independent. Where platform independence was not achievable, I encapsulated the platform-dependent code behind an abstract interface. We’ll explore this design pattern, in depth, in Chapter 7.

In the first edition of this book, pdCalc was mostly self-contained, from both a code and tooling standpoint; Qt was the only external dependency necessary to compile, build, and test the code. For the second edition, however, a few external dependencies were necessary. First, I changed the build system from using Qt’s own qmake to Kitware’s CMake [1]. CMake is cross platform, free of charge, easy to obtain, and practically a de facto standard for building C++ applications. For my purposes, CMake made experimentation with different C++20 compilers easier and more efficient. Of note, CMake’s C++20 tooling for modules was still immature at the time of writing, so some parts of the build scripts are more imperative than I expect will be necessary in the future. Second, while C++20 added coroutines as a new low-level language feature, the standard did not incorporate any high-level supporting libraries. In order to focus on the design aspects of using coroutines rather than their implementation mechanics, I increased the level of abstraction by using Lewis Baker’s cppcoro [7] library to implement a generator (see Chapter 5). For convenience, I included the single necessary file from cppcoro into a third-party directory included with pdCalc’s source code.

In general, I always strive to write standards-conforming, platform-independent code. Since any known platform-specific code is compartmentalized and encapsulated, extension to other platforms should be straightforward if you can tolerate the pain of customizing the build system. Therefore, the source code in this book should compile with any C++20-compliant compiler and on any platform where Qt is supported. Unfortunately, over a year after finalization of the standard, only one compiler, Microsoft’s Visual C++ (MSVC), is sufficiently standards conforming to actually compile the full source code presented in this book. (To be fair, 2020 was a very strange year.) GCC is close, but it lacks two key features. First, GCC’s C++ standard library implementation is missing the C++20 formatting library. This problem can be easily remedied by substituting the MIT licensed fmt library [2] in its place. The bigger problem is the immaturity of C++ modules support in GCC. Specifically, pdCalc’s source code, at the time of writing, causes GCC to produce internal compiler errors; I have submitted multiple bug reports. Clang’s support of C++20, while laudable, is behind both GCC and MSVC. At the current pace of development, I expect GCC to reach sufficient standards conformance to compile this book’s source code ahead of clang.

The Source Code

The source code (and supporting unit tests) for pdCalc is available, in its entirety, from Apress’s GitHub repository (https://github.com/Apress/practical-cplusplus-design-2e). Appendix A describes, in detail, how to download and build the source code. Although most readers will likely prefer to download the source using a git client, the entire source code is also available from GitHub as a single zip file.

The source code itself is licensed under the GNU Public License (GPL) version 3. I hope you find the source code useful for learning about design and implementation. Beyond its use as an educational aid for this book, you are, of course, free to do anything you like with the source code within the rights granted to you by the GPL.

For the first edition of this book, I wrote the source code for pdCalc from scratch specifically targeting new language features of C++11; the source code was updated for C++14 shortly before publication. For the second edition of this book, I updated and refactored the source code to use C++17 and C++20 features instead of performing a complete rewrite. This strategy has two implications. First, not every line of code was touched, although more lines were touched than I originally anticipated. Nevertheless, I likely missed places where newer features of the language could have been used but weren’t. In other instances, newer features were intentionally not used because of compiler or language support. For example, while most of pdCalc was upgraded to extensively use modules, the GUI was not because, at the time of writing, modules and Qt’s meta object compiler (MOC) did not interoperate well. Second, because I was literally refactoring the source code for the sole purpose of incorporating new language features, I was able to identify specifically where new language features enabled different design paradigms. Where these changes were significant, I contrasted the original version of the source code and its design to the updated version directly in the book’s text.

This book uses two distinct fonts: the standard font you are reading now and a fixed width font used for source code. The fixed width font is demonstrated in the following list of standard library containers: vector, list, and map. When clear from context, to save space, I often omit namespaces and template arguments. When the discussion requires a block of source code, it will be offset from the rest of the text as follows:
class ExampleClass
{
public:
  // your implementation here
};

In general, I tried to reproduce the code in the text identically to the code in the repository. However, in some cases, parts of the source were deliberately omitted in the text either for brevity or clarity. I tried to note instances where the differences are significant. Where the two differ, assume the code in the repository is the more correct and complete version.

Contacting the Author

I suspect that as you read this book and explore the source code, you will invariably have questions. Please feel free to contact me with questions about the content of the book, questions about the source code, errors, or improvements to my design or implementation. I can be contacted at PracticalDesignB​ook@gmail.​com.​ I will make my best effort to reply to all reasonable email related to pdCalc, but without knowing the volume of email I’ll receive, I can make no ironclad guarantee that I will be able to respond to every request. With respect to the first edition of this book, I can proudly state that I responded to each and every email that I received in great detail. Unfortunately, the previous statement is true by vacuous argument as no one emailed me. Nonetheless, I reiterate that anyone should feel free to ask me questions, and I will make my best effort to respond in a timely manner.

Parting Advice

Finally, in addition to learning something, I hope that you, the reader, have fun with the subject. My personal suggestion is to try to think about the design decisions yourself before reading my solutions. If you are truly industrious, you may even want to implement your own calculator using a completely different, possibly better design. After all, as I said before, design is ultimately about both experience and taste, and your experience and taste may differ significantly from mine. Enjoy!

Table of Contents
About the Author
Adam B. Singer

graduated first in his class at the Georgia Institute of Technology in 1999 with a bachelor’s degree in chemical engineering. He subsequently attended the Massachusetts Institute of Technology (MIT) on a National Defense Science and Engineering Graduate Fellowship. He graduated from MIT with a Ph.D. in chemical engineering in 2004 after defending his thesis titled “Global Dynamic Optimization.”

Since graduation, Adam has been employed by ExxonMobil1 in various Upstream organizations, including research, information technology, and digital transformation. He has held both technical and managerial roles in software development, design, and project management in areas such as mathematical optimization, reservoir modeling and simulation, decision support under uncertainty, basin modeling, well log modeling, process stratigraphy, geoscience platform architecture, and data science. He has also served on and chaired committees designing in-house training in the areas of technical software development and computational and applied mathematics. He is currently the Senior Principal Digital Architect for ExxonMobil.

Adam additionally held the title of adjunct assistant professor in the Department of Computational and Applied Mathematics at Rice University from 2007 to 2012. In 2006 and 2007, he taught a graduate-level course, CAAM 520, on computational science. The course focused on the design and implementation of high-performance parallel programs.

 
About the Technical Reviewer
German Gonzalez-Morris

is a software architect/engineer working with C/C++, Java, and different application containers, in particular with WebLogic Server. He has developed various applications using JEE, Spring, and Python. His areas of expertise also include OOP, Java/JEE, Python, design patterns, algorithms, Spring Core/MVC/security, and microservices. German has worked with performance messaging, RESTful API, and transactional systems. For more information about him, visit www.linkedin.com/in/german-gonzalez-morris.

 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.63.5