Advanced C++ Programming Cookbook - Dr. Rian Quinn - E-Book

Advanced C++ Programming Cookbook E-Book

Dr. Rian Quinn

0,0
36,59 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.
Mehr erfahren.
Beschreibung

A recipe-based guide to refining your C++ programming skills with the help of coding best practices, advanced programming concepts, and the latest features of C++17 and C++20




Key Features



  • Learn how to develop and design your own libraries


  • Find solutions to your app development problems and implement them in a highly reusable manner, following library development best practices


  • Explore advanced C++ features such as containers, coroutines, and modules



Book Description



If you think you've mastered C++ and know everything it takes to write robust applications, you'll be in for a surprise. With this book, you'll gain comprehensive insights into C++, covering exclusive tips and interesting techniques to enhance your app development process.






You'll kick off with the basic principles of library design and development, which will help you understand how to write reusable and maintainable code. You'll then discover the importance of exception safety, and how you can avoid unexpected errors or bugs in your code. The book will take you through the modern elements of C++, such as move semantics, type deductions, and coroutines. As you advance, you'll delve into template programming - the standard tool for most library developers looking to achieve high code reusability. You'll explore the STL and learn how to avoid common pitfalls while implementing templates. Later, you'll learn about the problems of multithreaded programming such as data races, deadlocks, and thread starvation. You'll also learn high-performance programming by using benchmarking tools and libraries. Finally, you'll discover advanced techniques for debugging and testing to ensure code reliability.






By the end of this book, you'll have become an expert at C++ programming and will have gained the skills to solve complex development problems with ease.




What you will learn



  • Solve common C++ development problems by implementing solutions in a more generic and reusable way


  • Achieve different levels of exception safety guarantees by introducing precise declarations


  • Write library-quality code that meets professional standards


  • Practice writing reliable, performant code that exposes consistent behavior in programs


  • Understand why you need to implement design patterns and how it's done


  • Work with complex examples to understand various aspects of good library design



Who this book is for



This book is for intermediate and expert-level C++ developers who are looking to explore the lesser known functionalities of the language to improve the efficiency of their code and the way they develop applications. Basic knowledge of object-oriented programming concepts and the Standard Template Library (STL) is assumed.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB

Seitenzahl: 465

Veröffentlichungsjahr: 2020

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Advanced C++ Programming Cookbook

 

 

Become an expert C++ programmer by mastering concepts like templates, concurrency, and type deduction

 

 

 

 

 

 

 

 

 

 

 

Dr. Rian Quinn

 

 

 

 

 

 

 

 

 

 

 

BIRMINGHAM - MUMBAI

Advanced C++ Programming Cookbook

Copyright © 2020 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Commissioning Editor: Richa TripathiAcquisition Editor:Shriram ShekharContent Development Editor:Tiksha SarangSenior Editor: Storm MannTechnical Editor:Romy DiasCopy Editor: Safis EditingProject Coordinator:Francy PuthiryProofreader: Safis EditingIndexer:Rekha NairProduction Designer:Jyoti Chauhan

First published: January 2020

Production reference: 1290120

Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.

ISBN 978-1-83855-991-5

www.packt.com

 

To my kids, embrace the challenges that others deem too hard to take on; be patient, strong, and confident, never letting anyone stand in your way.
– Dad
 

Packt.com

Subscribe to our online digital library for full access to over 7,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.

Why subscribe?

Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals

Improve your learning with Skill Plans built especially for you

Get a free eBook or video every month

Fully searchable for easy access to vital information

Copy and paste, print, and bookmark content

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.packt.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

At www.packt.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks. 

Contributors

About the author

Dr. Rian Quinn is the Chief Technology Officer (CTO) in the Advanced Technologies Business Unit at Assured Information Security, Inc., having focused on trusted computing, hypervisor-related technologies, machine learning/artificial intelligence, and cybersecurity for more than 10 years, and has 9 years of technical management and business development experience. He holds a Ph.D. in computer engineering, with specializations in information assurance and computer architectures, from Binghamton University. He is the cofounder and lead developer of the Bareflank hypervisor, and is an active member of several open source projects, including Microsoft's Guideline Support Library (GSL) and OpenXT. Rian previously wrote Hands-On System Programming with C++.

About the reviewers

Sergey Gomon started his journey in IT 12 years ago at Belarus State University of Informatics and Radioelectronics, in the Artificial Intelligence department. He has about 8 years of industrial programming experience using C++ in several fields, such as network programming, information security, and image processing. He currently works at SolarWinds MSP and is an activist in the CoreHard C++ community.

 

 

Zhuo Qingliang (also known as KDr2 online) is presently working at paodingai.com, a start-up FinTech company in China that is dedicated to improving the financial industry by using artificial intelligence technologies. He has over 10 years of experience in Linux, C, C++, Python, Perl, and Java development. He is interested in programming, doing consulting work, and participating in and contributing to the open source community (including the Julia community, of course).

 

 

 

 

 

 

 

Packt is searching for authors like you

If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.

Table of Contents

Title Page

Copyright and Credits

Advanced C++ Programming Cookbook

Dedication

About Packt

Why subscribe?

Contributors

About the author

About the reviewers

Packt is searching for authors like you

Preface

Who this book is for

What this book covers

To get the most out of this book

Download the example code files

Code in Action

Conventions used

Sections

Getting ready

How to do it…

How it works…

There's more…

See also

Get in touch

Reviews

Getting Started with Library Development

Technical requirements

Understanding the principle of least surprise

Getting ready

How to do it...

How it works...

Example 1

Example 2

Example 3

Example 4

Example 5

Example 6

Example 7

Example 8

Example 9

How to namespace everything

Getting ready

How to do it...

How it works...

Example 1

Example 2

Header-only libraries

Getting ready

How to do it...

How it works...

How to handle includes

Global variables

Issues with C-style macros

How to implement a large library as header-only

Learning library development best practices

Getting ready

How to do it...

How it works... 

What about warnings?

Static and dynamic analysis

Documentation

CII Best Practices

Learning how to use the boost APIs

Getting ready

How to do it...

How it works... 

Example 1

Example 2

See also

Using Exceptions for Error Handling

Technical requirements

Using the noexcept specifier

Getting ready

How to do it...

How it works...

Using the noexcept operator

Getting ready

How to do it...

How it works...

Using RAII

Getting ready

How to do it...

How it works...

Learning why to never throw exceptions in destructors

Getting ready

How to do it...

How it works...

Easily creating your own exception classes

Getting ready

How to do it...

How it works...

Implementing Move Semantics

Technical requirements

Using compiler-generated special class member functions and the Big Five

Getting ready

How to do it...

How it works...

Making your class movable

Getting ready

How to do it...

How it works...

Move-only types

Getting ready

How to do it...

How it works...

Implementing the noexcept move constructor

Getting ready

How to do it...

How it works...

Learning to be wary of const&&

Getting ready

How to do it...

How it works...

Referencing qualified member functions

Getting ready

How to do it...

How it works...

Exploring objects that cannot be moved or copied

Getting ready

How to do it...

How it works...

Using Templates for Generic Programming

Technical requirements

Implementing SFINAE

Getting ready

How to do it...

How it works...

Learning perfect forwarding

Getting ready

How to do it...

How it works...

Using if constexpr

Getting ready

How to do it...

How it works...

Using tuples to work with parameter packs

Getting ready

How to do it...

How it works...

Using type traits to overload functions and objects

Getting ready

How to do it...

How it works...

Learning how to implement template<auto>

Getting ready

How to do it...

How it works...

Working with explicit template declarations

Getting ready

How to do it...

How it works...

Concurrency and Synchronization

Technical requirements

Working with mutexes

Getting ready

How to do it...

How it works...

std::mutex

std::lock_guard

std::recursive_mutex

std::shared_mutex

std::timed_mutex

Using atomic data types

Getting ready

How to do it...

How it works...

Understanding what const & mutable mean in the context of multiple threads

Getting ready

How to do it...

How it works...

Making a class thread-safe

Getting ready

How to do it...

How it works...

Synchronization wrappers and how to implement them

Getting ready

How to do it...

How it works...

Blocking operations versus asynchronous programming

Getting ready

How to do it...

How it works...

Working with promises and futures

Getting ready

How to do it...

How it works...

Optimizing Your Code for Performance

Technical requirements

Benchmarking your code

Getting ready

How to do it...

How it works...

Looking at assembly code

Getting ready

How to do it...

How it works...

Reducing the number of memory allocations

Getting ready

How to do it...

How it works...

Declaring noexcept

Getting ready

How to do it...

How it works...

Debugging and Testing

Technical requirements

Getting to grips with unit testing

Getting ready

How to do it...

How it works...

Working with ASAN, the address sanitizer

Getting ready

How to do it...

How it works...

Memory leak error

Memory deleted twice

Accessing invalid memory 

Using memory after deleting it

Deleting memory that was never allocated

Working with UBSAN, the undefined behavior sanitizer

Getting ready

How to do it...

How it works...

Divide-by-zero errors

Null-pointer dereferences

Out-of-bounds errors

Overflow errors

Using #ifndef NDEBUG to conditionally execute additional checks

Getting ready

How to do it...

How it works...

Creating and Implementing Your Own Container

Technical requirements

Using a simple wrapper around std::vector

Getting ready

How to do it...

How it works...

Default constructor

Custom allocator constructor

Count constructors

Copy/move constructors

Initializer list constructor

Usage

Adding elements to our container

Usage of push/emplace

Adding the relevant parts of the std::set API

Getting ready

How to do it...

How it works...

Working with iterators

Getting ready

How to do it...

How it works...

Adding the relevant parts of the std::vector API

Getting ready

How to do it...

How it works...

Exploring Type Erasure

Technical requirements

How to erase a type with inheritance

Getting ready

How to do it...

How it works...

Using C++ templates to write generic functions

Getting ready

How to do it...

How it works...

There's more...

See also

Learning the C++ type eraser pattern

Getting ready

How to do it...

How it works...

Implementing delegates with type erasing

Getting ready

How to do it...

How it works...

Adding a function signature to our delegate

Adding const support to our delegate

Adding support for one-to-many to our delegate

Adding support for non-member functions to our delegate

An In-Depth Look at Dynamic Allocation

Technical requirements

Comparing std::shared_ptr and std::unique_ptr

Getting ready

How to do it...

How it works...

Converting from a std::unique_ptr into a std::shared_ptr

Getting ready

How to do it...

How it works...

Working with circular references

Getting ready

How to do it...

How it works...

Typecasting with smart pointers

Getting ready

How to do it...

How it works...

The heap under a microscope

 Getting ready

How to do it...

How it works...

Common Patterns in C++

Technical requirements

Learning the factory pattern

Getting ready

How to do it...

How it works...

Using the singleton pattern properly

Getting ready

How to do it...

How it works...

Extending your objects with the decorator pattern

Getting ready

How to do it...

How it works...

Adding communication with the observer pattern

Getting ready

How to do it...

How it works...

Improving performance with static polymorphism

Getting ready

How to do it...

How it works...

A Closer Look at Type Deduction

Technical requirements

Using auto and type deduction

Getting ready

How to do it...

How it works...

Learning how decltype type deduction rules work

Getting ready

How to do it...

How it works...

Working with template function type deduction

Getting ready

How to do it...

How it works...

Leveraging template class type deduction in C++17

Getting ready

How to do it...

How it works...

Working with user-defined type deduction in C++17

Getting ready

How to do it...

How it works...

Bonus - Using C++20 Features

Technical requirements

Looking at Concepts in C++20

Getting ready

How to do it...

How it works...

Working with Modules in C++20

Getting ready

How to do it...

How it works...

Introducing std::span, a new view on arrays

Getting ready

How to do it...

How it works...

Working with Ranges in C++20

Getting ready

How to do it...

How it works...

Learning how to use Coroutines in C++20

Getting ready

How to do it...

How it works...

Other Books You May Enjoy

Leave a review - let other readers know what you think

Preface

In this book, you will learn advanced C++ techniques that you can use on your own C++ projects. This book teaches C++ using a recipe-style approach, complete with examples and screenshots for each recipe that you can download from GitHub and work with yourself. This book teaches C++ using the C++17 specification, with a sneak peek at the new features being added to C++20 at the end. In some recipes, we will even use a disassembler to better understand how C++ is compiled, and the impact certain decisions have on your applications. By the end of this book, you will have mastered the advanced concepts of C++ and will be able to solve everyday problems, which will take your C++ programming to the next level. 

Who this book is for

This book is for intermediate C++ developers who are familiar with C++ and want to obtain expert skills and become a proficient C++ developer. A good understanding of the language is assumed, including a basic understanding of assembly.

What this book covers

Chapter 1, Getting Started with Library Development, teaches you how to develop your own libraries, including an explanation of the principle of least surprise, how to namespace everything, how to write header-only libraries, and how to ensure others will continue to use your libraries.

Chapter 2, Using Exceptions for Error Handling, covers more advanced topics of C++ exception and error handling, including a detailed explanation of the noexcept specifier and operator, how RAII supports resource management in the presence of exceptions, why throwing from a destructor should be avoided, and how to write your own exceptions.

Chapter 3, Implementing Move Semantics, provides a detailed explanation of C++ move semantics, including an explanation of the Big Five, how to make your class movable, how to write move-only (and non-move) non-copy style classes, how to properly implement a move constructor, why const && makes no sense, and how to use reference qualification.

 Chapter 4, Using Templates for Generic Programming, teaches you how to write template functions like an expert, including how to implement your own SFINAE, how to perform perfect forwarding, how to use constexpr-if statements, how to leverage tuples with parameter packs, how to loop over parameter packs at compile time, how to use type traits to implement different versions of the same function, how to use template<auto>, and how to leverage explicit type declarations in your own applications.

Chapter 5, Concurrency and Synchronization, teaches you how to use std::mutex (and friends), when to use atomic types, how to handleconst classes with thread-safety using the mutable keyword, how to write a thread-safe class, how to write a thread-safe wrapper, as well as how to write asynchronous C++ including promises and futures.

Chapter 6, Optimizing Your Code for Performance, covers how to profile and benchmark your C++, how to disassemble your C++ to better understand how to optimize your code, how to locate and remove unneeded memory allocations, and why noexcept helps with optimizations.

Chapter 7, Debugging and Testing, walks you through how to use Catch2 to unit test C++, how to use Google's ASAN and UBSAN sanitizers to dynamically analyze your code for memory corruption and undefined behavior, as well as how to use NDEBUG.

Chapter 8, Creating and Implementing Your Own Container, teaches you how to write your own container wrapper by creating a std::vector that is always sorted.

Chapter 9, Exploring Type Erasure, teaches you everything you need to know about type erasure, including how to erase types through inheritance and using template, how to implement the type erasure pattern, and how to implement the delegate pattern.

Chapter 10, An In-Depth Look at Dynamic Allocation, teaches you advanced topics in dynamic memory allocation, including how to properly use std::unique_ptr and std::shared_ptr, how to handle circular references, how to type cast smart pointers, and how the heap works behind the scenes to provide your application with dynamic memory.

Chapter 11, Common Patterns in C++, explains how different patterns in computer science are implemented in C++, including the factory pattern, the singleton pattern, the decorator pattern, and the observer pattern, as well as how to implement static polymorphism to write your own static interfaces without the need for virtual inheritance.

 Chapter 12, A Closer Look at Type Deduction, provides a deep dive into how type deduction is performed in C++17, including howauto,decltype, and template deduce their types automatically. This chapter concludes with examples of how to write your own C++17 user-defined deduction guides.

Chapter 13, Bonus: Using C++20 Features, provides a sneak peek at the new features coming with C++20, including concepts, modules, ranges, and coroutines.

To get the most out of this book

We assume that you have written C++ before and are already familiar with some modern C++ features.

This book uses Ubuntu to provide examples that you can compile and run yourself as you read the book. We assume you have some basic knowledge of Ubuntu, how to install it, and how to use a Linux terminal.

We use a disassembler in some of the recipes to better understand what the compiler is doing under the hood. Although you do not need to know how to read the assembly to understand what is being taught, a basic understanding of x86_64 assembly will help.

Download the example code files

You can download the example code files for this book from your account at www.packt.com. If you purchased this book elsewhere, you can visit www.packtpub.com/support and register to have the files emailed directly to you.

You can download the code files by following these steps:

Log in or register at

www.packt.com

.

Select the

Support

tab.

Click on

Code Downloads

.

Enter the name of the book in the

Search

box and follow the onscreen instructions.

Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:

WinRAR/7-Zip for Windows

Zipeg/iZip/UnRarX for Mac

7-Zip/PeaZip for Linux

The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Advanced-CPP-Programming-CookBook. In case there's an update to the code, it will be updated on the existing GitHub repository.

We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

Code in Action

Visit the following link to check out videos of the code being run: https://bit.ly/2tQoZyW

Sections

In this book, you will find several headings that appear frequently (Getting ready, How to do it..., How it works..., There's more..., and See also).

To give clear instructions on how to complete a recipe, use these sections as follows:

Getting ready

This section tells you what to expect in the recipe and describes how to set up any software or any preliminary settings required for the recipe.

How to do it…

This section contains the steps required to follow the recipe.

How it works…

This section usually consists of a detailed explanation of what happened in the previous section.

There's more…

This section consists of additional information about the recipe in order to make you more knowledgeable about the recipe.

See also

This section provides helpful links to other useful information for the recipe.

Get in touch

Feedback from our readers is always welcome.

General feedback: If you have questions about any aspect of this book, mention the book title in the subject of your message and email us at [email protected].

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.

Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

Reviews

Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!

For more information about Packt, please visit packt.com.

Getting Started with Library Development

In this chapter, we will cover some useful recipes for creating our own libraries, including an explanation of the principle of least surprise, which encourages us to implement libraries using semantics that our users are already familiar with. We will also look at how to namespace everything to ensure our custom libraries don't conflict with others. In addition, we will look at how to create header-only libraries, as well as some best practices associated with library development. Finally, we will conclude this chapter with a demonstration of the boost libraries to show you what a large library looks like and how it can be used by users in their own projects.

In this chapter, we will cover the following recipes:

Understanding the principle of least surprise

How to namespace everything

Header-only libraries

Learning library development best practices

Learning how to use the boost APIs

Let's get started!

Technical requirements

To compile and run the examples in this chapter, you must have administrative access to a computer running Ubuntu 18.04 with a functional internet connection. Prior to running these examples, you must install the following packages using the following command:

> sudo apt-get install build-essential git cmake

If this is installed on any operating system other than Ubuntu 18.04, then GCC 7.4 or higher and CMake 3.6 or higher will be required.

Understanding the principle of least surprise

When either using existing C++ libraries or creating your own, understanding the principle of least surprise (also called the principle of least astonishment) is critical to developing source code efficiently and effectively. This principle simply states that any feature that a C++ library provides should be intuitive and should operate as the developer expects. Another way of saying this is that a library's APIs should be self-documenting. Although this principle is critically important when designing libraries, it can and should be applied to all forms of software development. In this recipe, we will explore this principle in depth. 

Getting ready

As with all of the recipes in this chapter, ensure that all of the technical requirements have been met, including installing Ubuntu 18.04 or higher and running the following in a Terminal window:

> sudo apt-get install build-essential git cmake

This will ensure your operating system has the proper tools to compile and execute the examples in this recipe. Once you've done this, open a new Terminal. We will use this Terminal to download, compile, and run our examples.

How to do it...

Perform the following steps to complete this recipe:

From a new Terminal, run the following code to download the source code:

> cd ~/

> git clone https://github.com/PacktPublishing/Advanced-CPP-CookBook.git

> cd Advanced-CPP-CookBook/chapter01

To compile the source code, run the following code:

> mkdir build && cd build

> cmake ..

> make recipe01_examples

Once the source code has been compiled, you can execute each example in this recipe by running the following commands:

> ./recipe01_example01

The answer is: 42

> ./recipe01_example02

The answer is: 42

> ./recipe01_example03

The answer is: 42

> ./recipe01_example04

The answer is: 42

The answer is: 42

> ./recipe01_example05

The answer is: 42

The answer is: 42

> ./recipe01_example06

The answer is: 42

The answer is: 42

> ./recipe01_example07

The answer is: 42

> ./recipe01_example08

The answer is: 42

> ./recipe01_example09

The answer is: 42

In the next section, we will step through each of these examples and explain what each example program does and how it relates to the lessons being taught in this recipe.

How it works...

As stated in the previous section, the principle of least surprise states that a library's APIs should be intuitive and self-documenting and this principle generally applies to all forms of software development and not just library design. To understand this, we'll look at some examples.

Example 1

Example 1 demonstrates the principle of least surprise as follows:

#include <iostream>int sub(int a, int b){ return a + b; }int main(void){ std::cout << "The answer is: " << sub(41, 1) << '\n'; return 0;}

As shown in the preceding example, we have implemented a library API that adds two integers and returns the results. The problem is that we named the function sub, which most developers would associate with subtraction and not addition; although the API functions as designed, it breaks the principle of least surprise because the API's name is not intuitive. 

Example 3

Example 3 demonstrates the principle of least surprise as follows:

#include <iostream>int add(int a, int b){ return a + b; }int main(void){ std::cout << "The answer is: " << add(41, 1) << '\n'; return 0;}

As shown in the preceding example, we're adhering to the principle of least surprise here. The API is designed to add two integers and return the result, and the API intuitively performs this action as expected. 

Example 4

Example 4 demonstrates the principle of least surprise as follows:

#include <stdio.h>#include <iostream>int main(void){ printf("The answer is: %d\n", 42); std::cout << "The answer is: " << 42 << '\n'; return 0;}

As shown in the preceding example, another great example of the principle of least surprise is the difference between printf() and std::cout. The printf() function requires the addition of format specifiers to output integers to stdout. There are many reasons why printf() is not intuitive:

To a beginner, the

printf()

function's name, which stands for print formatted, is not intuitive (or in other words, the function's name is not self-documenting). Other languages avoid this issue by picking more intuitive names for a print function, such as 

print()

or

console()

, which do a better job of adhering to the principle of least surprise.  

The format specifier symbol for an integer is

d

. Once again, to a beginner this is unintuitive. In this specific case, 

d

stands for decimal, which is another way of saying

signed integer

. A better format specifier might have been

i

to match the language's use of

int

Contrast this with std::cout, which stands for character output. Although this is less intuitive compared to print() or console(), it is more intuitive than printf(). Furthermore, to output an integer to stdout, the user doesn't have to memorize a table of format specifiers to complete their task. Instead, they can simply use the << operator. Then, the APIs handle formatting for you, which is not only more intuitive but also safer (especially when working with std::cin as opposed to scanf()). 

Example 6

Example 6 demonstrates the principle of least surprise as follows:

#include <iostream>int add(int a, int b){ return a + b; }int Sub(int a, int b){ return a - b; }int main(void){ std::cout << "The answer is: " << add(41, 1) << '\n'; std::cout << "The answer is: " << Sub(43, 1) << '\n'; return 0;}

As shown in the preceding code, we have implemented two different APIs. The first adds two integers and returns the results while the second subtracts two integers and returns the results. The issue with the subtract function is two-fold:

The addition function is in lowercase while the subtraction function is in uppercase. This is not intuitive and users of the APIs would have to learn which APIs are in lowercase and which are in uppercase. 

The C++ standard APIs are all in snake case, meaning they leverage lowercase words with the use of

_

to denote a space. In general, it is better to design C++ library APIs with snake case as a beginner is more likely to find this intuitive. It should be noted that, although this is generally the case, the use of snake case is highly subjective and there are several languages that do not adhere to this guidance. The most important thing is to pick a convention and stick to it. 

Once again, ensuring your APIs mimic existing semantics ensures the user can quickly and easily learn to use your APIs, while reducing the probability of the user writing your APIs incorrectly, leading to compile errors. 

Example 7

Example 7 demonstrates the principle of least surprise as follows:

#include <queue>#include <iostream>int main(void){ std::queue<int> my_queue; my_queue.emplace(42); std::cout << "The answer is: " << my_queue.front() << '\n'; my_queue.pop(); return 0;}

As shown in the preceding example, we are showing you how a std::queue can be used to add integers to a queue, output the queue to stdout, and remove elements from the queue. The point of this example is to highlight the fact that C++ already has a standard set of naming conventions that should be leveraged during C++ library development.

If you are designing a new library, it is helpful to the user of your library to use the same naming conventions that C++ has already defined. Doing so will lower the barrier to entry and provide a more intuitive API. 

Example 8

Example 8 demonstrates the principle of least surprise as follows:

#include <iostream>auto add(int a, int b){ return a + b; }int main(void){ std::cout << "The answer is: " << add(41, 1) << '\n'; return 0;}

As shown in the preceding example, we are demonstrating how the use of auto, which tells the compiler to figure out what the return type of the function is automatically, does not uphold the principle of least surprise. Although auto is extremely helpful for writing generic code, its use should be avoided as much as possible when designing a library API. Specifically, for the user of the API to understand what the inputs and outputs of the API are, the user must read the API's implementation as auto does not specify the output type. 

Example 9

Example 9 demonstrates the principle of least surprise as follows:

#include <iostream>template <typename T>T add(T a, T b){ return a + b; }int main(void){ std::cout << "The answer is: " << add(41, 1) << '\n'; return 0;}

As shown in the preceding example, we are demonstrating a more appropriate way to uphold the principle of least surprise while simultaneously supporting generic programming. Generic programming (also called template meta-programming or programming with C++ templates) provides the programmer with a way to create an algorithm without stating the types that are being used in the algorithm. In this case, the add function doesn't dictate the input type, allowing the user to add two values of any type (in this case, the type is called T, which can take on any type that supports the add operator). Instead of returning an auto, which would not state the output type, we return a type T. Although T is not defined here as it represents any type, it does tell the user of the API that any type we input into this function will also be returned by the function. This same logic is used heavily in the C++ standard library. 

How to namespace everything

When creating a library, it is important to namespace everything. Doing so ensures that of the APIs provided by the library cause name collisions with the user's code or with facilities provided by other libraries. In this recipe, we will demonstrate how to do this in our own libraries.  

Getting ready

As with all of the recipes in this chapter, ensure that all of the technical requirements have been met, including installing Ubuntu 18.04 or higher and running the following in a Terminal window:

> sudo apt-get install build-essential git cmake

This will ensure your operating system has the proper tools to compile and execute the examples in this recipe. Once you have done this, open a new Terminal. We will use this Terminal to download, compile, and run our examples.

How to do it...

You need to perform the following steps to complete this recipe:

From a new Terminal, run the following to download the source code:

> cd ~/

> git clone https://github.com/PacktPublishing/Advanced-CPP-CookBook.git

> cd Advanced-CPP-CookBook/chapter01

To compile the source code, run the following code:

> mkdir build && cd build

> cmake ..

> make recipe02_examples

Once the source code has been compiled, you can execute each example in this recipe by running the following commands:

> ./recipe02_example01

The answer is: 42

> ./recipe02_example02

The answer is: 42

In the next section, we will step through each of these examples and explain what each example program does and how it relates to the lessons being taught in this recipe.

How it works...

C++ provides us with the ability to wrap code in a namespace, which simply adds the namespace name to all functions and variables inside the namespace code (it should be noted that C style macros are not included in the namespace and should be used with care because C macros are a preprocessor feature that does not contribute to the code's compiled syntax). To explain why we should namespace everything when creating our own libraries, we'll look at some examples.

Example 1

Example 1 demonstrates how to wrap your library's APIs in a C++ namespace :

// Contents of library.hnamespace library_name{ int my_api() { return 42; } // ...}// Contents of main.cpp#include <iostream>int main(void){ using namespace library_name; std::cout << "The answer is: " << my_api() << '\n'; return 0;}

As shown in the preceding example, the contents of the library are wrapped in a namespace and stored in the header (this example demonstrates a header-only library, which is an extremely useful design approach as the end user doesn't have to compile libraries, install them on his/her system, and then link against them). The library user simply includes the library header file and uses the using namespace library_name statement to unwrap the library's APIs. If the user has more than one library with the same API names, this statement can be omitted to remove any ambiguity. 

Header-only libraries

Header-only libraries are exactly as they sound; an entire library is implemented using header files (usually a single header file). The benefit of header-only libraries is that they are easy to include in your project as you simply include the header and you are done (there is no need to compile the library as there are no source files to compile). In this recipe, we will learn about some issues that arise when attempting to create a header-only library and how to overcome them. This recipe is important because, if you plan to create your own library, a header-only library is a great place to start and will likely increase your adoption rates as downstream users will have less trouble integrating your library into their code base.

Getting ready

As with all of the recipes in this chapter, ensure that all of the technical requirements have been met, including installing Ubuntu 18.04 or higher and running the following in a Terminal window:

> sudo apt-get install build-essential git cmake

This will ensure your operating system has the proper tools to compile and execute the examples in this recipe. Once you have done this, open a new Terminal. We will use this Terminal to download, compile, and run our examples.

How to do it...

You need to perform the following steps to complete this recipe:

From a new Terminal, run the following to download the source code:

> cd ~/

> git clone https://github.com/PacktPublishing/Advanced-CPP-CookBook.git

> cd Advanced-CPP-CookBook/chapter01

To compile the source code, run the following code:

> mkdir build && cd build

> cmake ..

> make recipe03_examples

Once the source code has been compiled, you can execute each example in this recipe by running the following commands:

> ./recipe03_example01

The answer is: 42

> ./recipe03_example02

The answer is: 42

> ./recipe03_example03

The answer is: 42

> ./recipe03_example04

The answer is: 42

The answer is: 2a

> ./recipe03_example05

> ./recipe03_example06

The answer is: 42

> ./recipe03_example07

The answer is: 42

In the next section, we will step through each of these examples and explain what each example program does and how it relates to the lessons being taught in this recipe.

How it works...

To create a header-only library, simply ensure that all of your code is implemented in header files, as follows:

#ifndef MY_LIBRARY#define MY_LIBRARYnamespace library_name{ int my_api() { return 42; }}#endif

The preceding example implements a simple library with a single function. The entire implementation of this library can be implemented in a single header file and included in our code as follows:

#include "my_library.h"#include <iostream>int main(void){ using namespace library_name; std::cout << "The answer is: " << my_api() << '\n'; return 0;}

Although creating header-only libraries seems simple enough, there are some issues that arise when attempting to create a header-only library that should be taken into account.

How to handle includes

In the preceding example, you might have noticed that, when we used our custom header-only library, we included the library first. This is an essential first step to writing a header-only library. When writing examples or tests for header-only libraries, our library should be the first thing we include to ensure that all of the header's dependencies are defined in the header-only library and not in our example or test.

For example, suppose we change our library as follows:

#ifndef MY_LIBRARY#define MY_LIBRARYnamespace library_name{ void my_api() { std::cout << "The answer is: 42" << '\n'; }}#endif

As shown in the preceding code snippet, instead of returning an integer our API now outputs to stdout. We can use our new API as follows:

#include <iostream>#include "my_library.h"int main(void){ library_name::my_api(); return 0;}

Although the preceding code compiles and runs as expected, there is a bug in the code that would likely only be identified by the user of your library. Specifically, if the user of your library swaps the order of the includes or doesn't #include <iostream>, the code will fail to compile and produce the following error:

This is because the header-only library itself doesn't include all of its dependencies. Since our example put the library after other includes, our example accidentally hides this issue. For this reason, when creating your own header-only library, always include the library first in your tests and examples to ensure this type of issue never happens to your users.

Learning library development best practices