Advanced C++ - Gazihan Alankus - E-Book

Advanced C++ E-Book

Gazihan Alankus

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

Become an expert at C++ by learning all the key C++ concepts and working through interesting exercises




Key Features



  • Explore C++ concepts through descriptive graphics and interactive exercises


  • Learn how to keep your development bug-free with testing and debugging


  • Discover various techniques to optimize your code



Book Description



C++ is one of the most widely used programming languages and is applied in a variety of domains, right from gaming to graphical user interface (GUI) programming and even operating systems. If you're looking to expand your career opportunities, mastering the advanced features of C++ is key.







The book begins with advanced C++ concepts by helping you decipher the sophisticated C++ type system and understand how various stages of compilation convert source code to object code. You'll then learn how to recognize the tools that need to be used in order to control the flow of execution, capture data, and pass data around. By creating small models, you'll even discover how to use advanced lambdas and captures and express common API design patterns in C++. As you cover later chapters, you'll explore ways to optimize your code by learning about memory alignment, cache access, and the time a program takes to run. The concluding chapter will help you to maximize performance by understanding modern CPU branch prediction and how to make your code cache-friendly.







By the end of this book, you'll have developed programming skills that will set you apart from other C++ programmers.




What you will learn



  • Delve into the anatomy and workflow of C++


  • Study the pros and cons of different approaches to coding in C++


  • Test, run, and debug your programs


  • Link object files as a dynamic library


  • Use templates, SFINAE, constexpr if expressions and variadic templates


  • Apply best practice to resource management



Who this book is for



If you have worked in C++ but want to learn how to make the most of this language, especially for large projects, this book is for you. A general understanding of programming and knowledge of using an editor to produce code files in project directories is a must. Some experience with strongly typed languages, such as C and C++, is also recommended.

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

EPUB

Seitenzahl: 747

Veröffentlichungsjahr: 2019

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++

Master the technique of confidently writing robust C++ code

Gazihan Alankus

Olena Lizina

Rakesh Mane

Vivek Nagarajan

Brian Price

Advanced C++

Copyright © 2019 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 authors, nor Packt Publishing, and its dealers and distributors will be held liable for any damages caused or alleged to be 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.

Authors: Gazihan Alankus, Olena Lizina, Rakesh Mane, Vivek Nagarajan, and Brian Price

Technical Reviewers: Anil Achary and Deepak Selvakumar

Managing Editor: Bhavesh Bangera

Acquisitions Editors: Kunal Sawant, Koushik Sen, and Sneha Shinde

Production Editor: Samita Warang

Editorial Board: Shubhopriya Banerjee, Bharat Botle, Ewan Buckingham, Mahesh Dhyani, Manasa Kumar, Alex Mazonowicz, Bridget Neale, Dominic Pereira, Shiny Poojary, Abhisekh Rane, Erol Staveley, Ankita Thakur, Nitesh Thakur, and Jonathan Wray.

First Published: October 2019

Production Reference: 1311019

ISBN: 978-1-83882-113-5

Published by Packt Publishing Ltd.

Livery Place, 35 Livery Street

Birmingham B3 2PB, UK

Table of Contents

Preface   i

1. Anatomy of Portable C++ Software   1

Introduction   2

Managing C++ Projects   3

The Code-Build-Test-Run Loop   3

Building a CMake Project   4

Exercise 1: Using CMake to Generate Ninja Build Files   5

Importing a CMake Project into Eclipse CDT   8

Exercise 2: Importing the CMake File into Eclipse CDT   9

Exercise 3: Adding New Source Files to CMake and Eclipse CDT   13

Activity 1: Adding a New Source-Header File Pair to the Project   16

Unit Testing   17

Preparing for the Unit Tests   17

Exercise 4: Preparing Our Project for Unit Testing   18

Building, Running, and Writing Unit Tests   19

Exercise 5: Building and Running Tests   20

Exercise 6: Testing the Functionality of Code   26

Activity 2: Adding a New Class and Its Test   30

Understanding Compilation, Linking, and Object File Contents   32

Compilation and Linking Steps   32

Exercise 7: Identifying Build Steps   32

The Linking Step   39

Diving Deeper: Viewing Object Files   40

Exercise 8: Exploring Compiled Code   41

Debugging C++ Code   46

Exercise 9: Debugging with Eclipse CDT   47

Writing Readable Code   56

Indentation and Formatting   56

Use Meaningful Names as Identifiers   58

Keeping Algorithms Clear and Simple   60

Exercise 10: Making Code Readable   62

Activity 3: Making Code More Readable   66

Summary   68

2A. No Ducks Allowed – Types and Deduction   71

Introduction   72

C++ Types   73

C++ Fundamental Types   73

C++ Literals   73

Specifying Types – Variables   74

Exercise 1: Declaring Variables and Exploring Sizes   76

Specifying Types – Functions   86

Exercise 2: Declaring Functions    88

Pointer Types   90

Exercise 3: Declaring and Using Pointers    95

Creating User Types   99

Enumerations   99

Exercise 4: Enumerations – Old and New School   104

Structures and Classes   107

Fraction Class   108

Constructors, Initialization, and Destructors   109

Class Special Member Functions   111

Implicit Versus Explicit Constructors   112

Class Special Member Functions – Compiler Generation Rules   113

Defaulting and Deleting Special Member Functions   113

Rule of Three/Five and Rule of Zero   115

Constructors – Initializing the Object   115

Exercise 5: Declaring and Initializing Fractions   118

Values Versus References and Const   120

Exercise 6: Declaring and Using Reference Types   123

Implementing Standard Operators   124

Implementing the Output Stream Operator (<<)   125

Structuring Our Code   126

Exercise 7: Adding Operators to the Fraction Class   127

Function Overloading   131

Classes, Structs, and Unions   132

Activity 1: Graphics Processing   134

Summary   137

2B. No Ducks Allowed – Templates and Deduction   139

Introduction   140

Inheritance, Polymorphism, and Interfaces   140

Inheritance and Access Specifiers   146

Abstract Classes and Interfaces   146

Exercise 1: Implementing Game Characters with Polymorphism   147

Classes, Structs, and Unions Revisited   151

Visibility, Lifetime, and Access   152

Namespaces   155

Templates – Generic Programming   156

What is Generic Programming?   157

Introducing C++ Templates   157

C++ Pre-Packaged Templates   159

Type Aliases – typedef and using   161

Exercise 2: Implementing Aliases   162

Templates – More than Generic Programming   163

Substitution Failure Is Not An Error – SFINAE   164

Floating-Point Representations   168

Constexpr if Expressions   169

Non-Type Template Arguments   170

Exercise 3: Implementing Stringify – specialization Versus constexpr   171

Function Overloading Revisited   174

Template Type Deduction   174

Displaying the Deduced Types   175

Template Type Deduction – the Details   176

SFINAE Expression and Trailing Return Types   183

Class Templates   186

Exercise 4: Writing a Class Template   186

Activity 1: Developing a Generic "contains" Template Function   190

Summary   191

3. No Leaks Allowed - Exceptions and Resources   193

Introduction   194

Variable Scope and Lifetime   194

Exceptions in C++   200

The Need for Exceptions   200

Stack Unwinding   206

Exercise 1: Implementing exceptions in Fraction and Stack   206

What Happens When an Exception is Thrown?   212

Throw-by-Value or Throw-by-Pointer   212

Standard Library Exceptions   213

Catching Exceptions   214

Exercise 2: Implementing Exception Handlers   216

CMake Generator Expressions   218

Exception Usage Guidelines   219

Resource Management (in an Exceptional World)    219

Resource Acquisition Is Initialization   220

Exercise 3: Implementing RAII for Memory and File Handles   223

Special Coding Techniques   226

C++ doesn't Need finally   227

RAII and the STL   227

Who Owns This Object?   227

Temporary Objects   229

Move Semantics   229

Implementing a Smart Pointer   230

STL Smart Pointers   235

std::unique_ptr    236

std::shared_ptr    237

std::weak_ptr   238

Smart Pointers and Calling Functions   241

Exercise 4: Implementing RAII with STL Smart Pointers   245

Rule of Zero/Five – A Different Perspective   250

Activity 1: Implementing Graphics Processing with RAII and Move   252

When is a Function Called?   253

Which Function to Call   254

Identifiers   255

Names   255

Name lookup   256

Argument-Dependent Lookup   256

Caveat Emptor    258

Exercise 5: Implementing Templates to Prevent ADL Issues   260

Implicit Conversion   262

Explicit – Preventing Implicit Conversion    262

Contextual Conversion   265

Exercise 6: Implicit and Explicit Conversions    266

Activity 2: Implementing classes for Date Calculations   270

Summary   271

4. Separation of Concerns - Software Architecture, Functions, and Variadic Templates   273

Introduction   274

The Pointer to Implementation (PIMPL) Idiom   274

Logical and Physical Dependencies   274

The Pointer to Implementation (PIMPL) Idiom   276

Advantages and Disadvantages of PIMPL   280

Implementing PIMPL with unique_ptr<>   280

unique_ptr<> PIMPL Special Functions   283

Exercise 1: Implementing a Kitchen with unique_ptr<>   286

Function Objects and Lambda Expressions   290

Function Pointers   290

What is a Function Object?   294

Exercise 2: Implementing function objects   297

std::function<> template   299

Exercise 3: Implementing callbacks with std::function   302

What is a Lambda Expression?   305

Capturing data into Lambdas   307

Exercise 4: Implementing Lambdas   309

Variadic Templates   313

Activity 1: Implement a multicast event handler    317

Summary   318

5. The Philosophers' Dinner – Threads and Concurrency   321

Introduction   322

Synchronous, Asynchronous, and Threaded Execution   323

Concurrency   323

Parallelism   324

Synchronous Execution   327

Asynchronous Execution   330

Exercise 1: Creating Threads in a Different Way   336

Review Synchronization, Data Hazards, and Race Conditions   340

Exercise 2: Writing an Example of Race Conditions   342

Data Hazards   346

RAW Dependency   346

WAR Dependency   347

WAW Dependency   348

Resource Synchronization   349

Event Synchronization   352

Deadlock   355

Move Semantics for Multithreading Closures   356

Exercise 3: Moving Objects to a Thread Function   359

Exercise 4: Creating and Working with an STL Container of Threads    363

Futures, Promises, and Async   368

Exercise 5: Synchronization with Future Results   382

Activity 1: Creating a Simulator to Model the Work of the Art Gallery   385

Summary   389

6. Streams and I/O   391

Introduction   392

Reviewing the I/O Portion of the Standard Library   393

Predefined Standard Stream Objects   395

Exercise 1: Overriding the Left Shift Operator, <<, for User-Defined Types   397

File I/O Implementation Classes   399

Exercise 2: Reading and Writing User-Defined Data Types to a File   403

String I/O Implementation   407

Exercise 3: Creating a Function for Replacement Words in a String   409

I/O Manipulators   412

I/O Manipulators for Changing the Numeric Base of the Stream    412

Exercise 4: Displaying Entered Numbers in Different Numeric Bases   412

I/O Manipulators for Floating-Point Formatting   416

Exercise 5: Displaying Entered Floating-Point Numbers with Different Formatting   417

I/O Manipulators for Boolean Formatting   423

I/O Manipulators for Field Width and Fill Control   424

I/O Manipulators for Other Numeric Formatting   426

I/O Manipulators for Whitespace Processing   428

Making Additional Streams   430

How to Make an Additional Stream – Composition   431

Exercise 6: Composing the Standard Stream Object in the User-Defined Class   432

How to Make an Additional Stream – Inheritance   436

Exercise 7: Inheriting the Standard Stream Object   438

Leveraging Asynchronous I/O   442

Asynchronous I/O on Windows Platforms   442

Asynchronous I/O on Linux Platforms   452

Exercise 8: Asynchronously Reading from a File in Linux   454

Asynchronous Cross-Platform I/O Libraries   459

Interaction of Threads and I/O   462

Exercise 9: Developing a Thread-Safe Wrapper for std::cout   463

Using Macros   468

Activity 1: The Logging System for The Art Gallery Simulator   470

Summary   474

7. Everybody Falls, It's How You Get Back Up – Testing and Debugging   477

Introduction   478

Assertions   479

Exercise 1: Writing and Testing Our First Assertion   479

Static Assertions   483

Exercise 2: Testing Static Assertions   484

Understanding Exception Handling   487

Exercise 3: Performing Exception Handling    488

Unit Testing and Mock Testing   491

Exercise 4: Creating Our First Unit Test Case   492

Unit Testing Using Mock Objects   499

Exercise 5: Creating Mock Objects   500

Breakpoints, Watchpoints, and Data Visualization   509

Working with the Stack Data Structure   510

Activity 1: Checking the Accuracy of the Functions Using Test Cases and Understanding Test-Driven Development (TDD)   524

Summary   532

8. Need for Speed – Performance and Optimization   535

Introduction   536

Performance Measurement   536

Manual Estimation   537

Studying Generated Assembly Code   538

Manual Execution Timing   547

Exercise 1: Timing a Program's Execution   548

Timing Programs without Side Effects   552

Source Code Instrumentation   553

Exercise 2: Writing a Code Timer Class   553

Runtime Profiling   559

Exercise 3: Using perf to Profile Programs   559

Optimization Strategies   561

Compiler-Based Optimization   561

Loop Unrolling   561

Exercise 4: Using Loop Unrolling Optimizations   562

Profile Guided Optimization   564

Exercise 5: Using Profile Guided Optimization   565

Parallelization   566

Exercise 6: Using Compiler Parallelization   566

Source Code Micro Optimizations   568

Using the std::vector Container Efficiently   568

Exercise 7: Optimizing Vector Growth   569

Short-Circuit Logical Operators   573

Exercise 8: Optimizing Logical Operators   573

Branch Prediction   576

Exercise 9: Optimization for Branch Prediction   576

Further Optimizations   579

Cache Friendly Code   580

Exercise 10: Exploring the Effect of Caches on Data Structures   580

Exercise 11: Measuring the Impact of Memory Access   584

Caching   588

Prefetching   588

Effects of Caching on Algorithms   589

Optimizing for Cache-Friendliness   590

Exercise 12: Exploring the Cost of Heap Allocations   591

Struct of Arrays Pattern   594

Exercise 13: Using the Struct of Arrays Pattern   594

Algorithmic Optimizations   598

Exercise 14: Optimizing a Word Count Program   599

Activity 1: Optimizing a Spell Check Algorithm   614

Summary   617

Appendix   619

Preface

About

This section briefly introduces the authors, the coverage of this book, the technical skills you'll need to get started, and the hardware and software requirements required to complete all of the included activities and exercises.

About the Book

C++ is one of the most widely used programming languages and is applied in a variety of domains, right from gaming to graphical user interface (GUI) programming and even operating systems. If you're looking to expand your career opportunities, mastering the advanced features of C++ is key.

The book begins with advanced C++ concepts by helping you decipher the sophisticated C++ type system and understand how various stages of compilation convert source code to object code. You'll then learn how to recognize the tools that need to be used in order to control the flow of execution, capture data, and pass data around. By creating small models, you'll even discover how to use advanced lambdas and captures and express common API design patterns in C++. As you cover later chapters, you'll explore ways to optimize your code by learning about memory alignment, cache access, and the time a program takes to run. The concluding chapter will help you to maximize performance by understanding modern CPU branch prediction and how to make your code cache-friendly.

By the end of this book, you'll have developed programming skills that will set you apart from other C++ programmers.

About the Authors

Gazihan Alankus holds a PhD in computer science from Washington University in St. Louis. Currently, he is an assistant professor at the Izmir University of Economics in Turkey. He teaches and conducts research on game development, mobile application development, and human-computer interaction. He is a Google developer expert in Dart and develops Flutter applications with his students in his company Gbot, which he founded in 2019.

Olena Lizina is a software developer with 5 years experience in C++. She has practical knowledge of developing systems for monitoring and managing remote computers with a lot of users for an international product company. For the last 4 years, she has been working for international outsourcing companies on automotive projects for well-known automotive concerns. She has been participating in the development of complex and highly performant applications on different projects, such as HMI (Human Machine Interface), navigation, and applications for work with sensors.

Rakesh Mane has over 18 years experience in the software industry. He has worked with proficient programmers from a variety of regions: India, the US, and Singapore. He has mostly worked in C++, Python, shell scripting, and database. In his spare time, he likes to listen to music and travel. Also, he likes to play with, experiment with, and break things using software tools and code.

Vivek Nagarajan is a self-taught programmer who started out in the 1980s on 8-bit systems. He has worked on a large number of software projects and has 14 years of professional experience with C++. Aside from this, he has worked on a wide variety of languages and frameworks across the years. He is an amateur powerlifter, DIY enthusiast, and motorcycle racer. He currently works as an independent software consultant.

Brian Price has over 30 years experience working in a variety of languages, projects, and industries, including over 20 years experience in C++. He was worked on power station simulators, SCADA systems, and medical devices. He is currently crafting software in C++, CMake, and Python for a next-generation medical device. He enjoys solving puzzles and the Euler project in a variety of languages.

Learning Objectives

By the end of this book, you will be able to:

Delve into the anatomy and workflow of C++Study the pros and cons of different approaches to coding in C++Test, run, and debug your programsLink object files as a dynamic libraryUse templates, SFINAE, constexpr if expressions and variadic templatesApply best practice to resource management

Audience

If you have worked in C++ but want to learn how to make the most of this language, especially for large projects, this book is for you. A general understanding of programming and knowledge of using an editor to produce code files in project directories is a must. Some experience with strongly typed languages, such as C and C++, is also recommended.

Approach

This fast-paced book is designed to teach you concepts quickly through descriptive graphics and challenging exercises. The book will have "call-outs," with key take away points and the most common pitfalls to keep you interested, while breaking up the topics into manageable sections.

Hardware Requirements

For the optimal student experience, we recommend the following hardware configuration:

Any entry-level PC/Mac with Windows, Linux, or macOS will sufficeProcessor: Dual core or equivalentMemory: 4 GB RAM (8 GB preferred)Storage: 35 GB of available space

Software Requirements

You'll also need the following software installed in advance:

Operating system: Windows 7 SP1 32/64-bit, Windows 8.1 32/64-bit, or Windows 10 32/64-bit, Ubuntu 14.04 or later, or macOS Sierra or laterBrowser: Google Chrome or Mozilla Firefox

Installation and Setup

Before you embark on this book, you will need to install the following libraries used in this book. You will find the steps to install these here.

Installing CMake

We will use CMake version 3.12.1 or later. We have two options for installation.

Option 1:

If you are using Ubuntu 18.10, you can install CMake globally using the following command:

sudo apt install cmake

When you run the following command:

cmake –version

You should see the following output:

cmake version 3.12.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).

If the version you see here is lower than 3.12.1 (for example, 3.10), you should install CMake locally using the following instructions.

Option 2:

If you are using an older Linux version, you may get a CMake version that is lower than 3.12.1. Then, you need to install it locally. Use the following commands:

wget \

https://github.com/Kitware/CMake/releases/download/v3.15.1/cmake-3.15.1-Linux-x86_64.sh

sh cmake-3.15.1-Linux-x86_64.sh

When you see the software license, type y and press Enter. When asked about the installation location, type y and press Enter again. This should install it to a new folder in your system.

Now, we will add that folder to our path. Type the following. Note that the first line is a bit too long and the line breaks in this document. You should write it as a single line, as follows:

echo "export PATH=\"$HOME/cmake-3.15.1-Linux-x86_64/bin:$PATH\"" >> .bash_profile

source .profile

Now, when you type the following:

cmake –version

You should see the following output:

cmake version 3.15.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).

3.15.1 is the current latest release at the time of writing this document. Since it is newer than 3.12.1, this will suffice for our purposes.

Installing Git

Test the current installation by typing the following:

git --version

You should see a line such as the following:

git version 2.17.1

If you see the following line instead, you need to install git:

command 'git' not found

Here is how you can install git in Ubuntu:

sudo apt install git

Installing g++

Test the current installation by typing the following:

g++ --version

You should see an output such as the following:

g++ (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0

Copyright (C) 2017 Free Software Foundation, Inc.

This is free software; see the source for copying conditions. There is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

If it is not installed, type the following code to install it:

sudo apt install g++

Installing Ninja

Test the current installation by typing the following:

ninja --version

You should see an output such as the following:

1.8.2

If it is not installed, type the following code to install it:

sudo apt install ninja-build

Installing Eclipse CDT and cmake4eclipse

There are multiple ways of installing Eclipse CDT. To get the latest stable version, we will use the official installer. Go to this website and download the Linux installer: https://www.eclipse.org/downloads/packages/installer.

Follow the instructions there and install Eclipse IDE for C/C++ Developers. Once you have installed it, run the Eclipse executable. If you did not change the default configuration, typing the following command in the terminal will run it:

~/eclipse/cpp-2019-03/eclipse/eclipse

You will choose a workspace folder and then you will be greeted with a Welcome tab in the main Eclipse window.

Now, we will install cmake4eclipse. An easy way to do this is to go to this website and drag the Install icon to the Eclipse window: https://github.com/15knots/cmake4eclipse#installation. It will ask you to restart Eclipse, after which you are ready to modify the CMake project to work with Eclipse.

Installing GoogleTest

We will install GoogleTest in our system, which will also install other packages that are dependent on it. Write the following command:

sudo apt install libgtest-dev google-mock

This command installs the include files and source files for GoogleTest. Now, we need to build the source files that we installed to create the GoogleTest library. Run the following commands to do this:

cd /usr/src/gtest

sudo cmake CMakeLists.txt

sudo make

sudo cp *.a /usr/lib

Installing the Code Bundle

Copy the code bundle for the class to the C:/Code folder.

Additional Resources

The code bundle for this book is also hosted on GitHub at https://github.com/TrainingByPackt/Advanced-CPlusPlus.

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

1. Anatomy of Portable C++ Software

Learning Objectives

By the end of this chapter, you will be able to:

Establish the code-build-test processDescribe the various stages of compilationDecipher complicated C++ type systemsConfigure projects with unit testsConvert source code to object codeWrite readable code and debug it

In this chapter, we will learn to establish the code-build-test model that will be used throughout the book, write beautiful code, and perform unit tests.

Introduction

C++ is one of the oldest and most popular languages that you can use to write efficient code. It is both "close to the metal," like C, and has advanced object-oriented features, like Java. Being an efficient low-level language makes C++ the language of choice for domains in which efficiency is paramount, such as games, simulations, and embedded systems. At the same time, being an object-oriented language with advanced features such as generics, references, and countless others makes it suitable for large projects that are developed and maintained by multiple people.

Almost any programming experience involves organizing your code base and using libraries written by others. C++ is no different. Unless your program is simple, you will distribute your code into multiple files that you need to organize, and you will use various libraries that fulfill tasks, usually in a much more efficient and robust way than your code would. C++ projects that do not use any third-party libraries are edge cases that do not represent the majority of projects, which use many libraries. These projects and their libraries are expected to work in different hardware architectures and operating systems. Therefore, it is important to spend time on project setup and understand the tools used to manage dependencies if you are going to develop anything meaningful with C++.

Most modern and popular high-level languages have standard tools to maintain projects, build them, and handle their library dependencies. Many of these have repositories that host libraries and tools that automatically download and use libraries from those repositories. For example, Python has pip, which takes care of downloading and using appropriate versions of libraries that the programmer wants to use. Similarly, JavaScript has npm, Java has maven, Dart has pub, and C# has NuGet. In most of these languages, you list the name of the library and the version that you would like to use, and the tool automatically downloads and uses the compatible version of the library. These languages benefit from the fact that the programs are built and run in a controlled environment in which a certain level of hardware and software requirements are satisfied. C++, on the other hand, is expected to work in a variety of contexts with different architectures, including very primitive hardware. Hence, C++ programmers are less pampered when it comes to building programs and performing dependency management.

Managing C++ Projects

In the world of C++, we have several tools that help in managing project sources and their dependencies. For example, pkg-config, Autotools, make, and CMake are the most notable ones in the community. Compared to the tools of the other high-level languages, these are much more complicated to use. CMake has arisen among these as the de facto standard for managing C++ projects and their dependencies. It is more opinionated compared to make, and it is accepted as the direct project format for most IDEs (Integrated Development Environments).

While CMake helps with managing projects and their dependencies, the experience is still far from higher-level languages in which you list the libraries and their versions that you want to use and everything else is taken care of for you. With CMake, you still are responsible for installing libraries properly in your development environment, and you are expected to use compatible versions for each library. In popular Linux distributions with extensive package managers, you can easily install binary versions of most popular libraries. However, sometimes, you may have to compile and install the libraries yourself. This is a part of the whole C++ developer experience, which you will gather by learning more about the development platform of your choice. Here, we will focus more on how to properly set up our CMake projects, including understanding and resolving issues related to libraries.

The Code-Build-Test-Run Loop

In order to base our discussion on a solid foundation, we will immediately start with a practical example. We will start with a C++ code base template that you can use as a starting point for your own projects. We will see how we can build and compile it using CMake on the command line. We will also set up the Eclipse IDE for C/C++ developers and import our CMake project. The use of an IDE will provide us with facilities that ease the creation of source code and enable us to debug our programs line by line to view what exactly happens during the execution of our program and correct our mistakes in an informed fashion rather than trial and error and superstition.

Building a CMake Project

The de facto standard for C++ projects is to use CMake to organize and build the project. Here, we will use a basic template project as a starting point. The following is the folder structure of a sample template:

Figure 1.1: Folder structure of a sample template

In the preceding figure, the .gitignore file lists the file patterns that should not be added to the git version control system. Such ignored files include the outputs of the build process, which are created locally and should not be shared among computers.

The files in the include and src folders are the actual C++ source files, and the CMakeLists.txt file is the CMake script file that glues the project together by handling the source compilation rules, library dependencies, and other project settings. CMake rules are high-level platform-independent rules. CMake uses them to create various types of make files for different platforms.

Building a project with CMake is a two-step process. First, we get CMake to generate platform-dependent configuration files for a native build system that will compile and build the project. Then, we will use the generated files to build the project. The platform-dependent build systems that CMake can generate configuration files for include UNIXMakefiles, Ninjabuild files, NMakeMakefiles, and MinGWMakefiles. The choice here depends on the platform in use, the availability of these tools, and personal preference. UNIXMakefiles are a de facto standard for Unix and Linux, whereas NMake is its Windows and Visual Studio counterpart. MinGW, on the other hand, is a Unix-like environment in Windows in which Makefiles are also in use. Ninja is a modern build system that provides exceptional speed compared to other build systems coupled with multi-platform support, which we choose to use here. Furthermore, in addition to these command-line build systems, we can also generate IDE projects for Visual Studio, XCode, Eclipse CDT, and many others, and build our projects inside the IDE. Therefore, CMake is a meta tool that will create the configuration files for another system that will actually build the project. In the next section, we will solve an exercise, wherein we will generate Ninjabuild files using CMake.

Exercise 1: Using CMake to Generate Ninja Build Files

In this exercise, we will use CMake to generate Ninja build files, which are used to build C++ projects. We will first download our source code from a git repository and will use CMake and Ninja to build it. The aim of this exercise is to use CMake to generate Ninja build files, build the project, and then run them.

Note

The link to the GitHub repository can be found here: https://github.com/TrainingByPackt/Advanced-CPlusPlus/tree/master/Lesson1/Exercise01/project.

Perform the following steps to complete the exercise:

In a terminal window, type the following command to download the CxxTemplate repository from GitHub onto your local system:

git clone https://github.com/TrainingByPackt/Advanced-CPlusPlus/tree/master/Lesson1/Exercise01/project

The output of the previous command is similar to the following:

Figure 1.2: Checking out the sample project from GitHub

Now you have the source code in the CxxTemplate folder.

Navigate into the CxxTemplate folder by typing the following command in the terminal:

cd CxxTemplate

Now you can list all the files in the project by typing the following command:

find .

Generate our Ninja build file using the cmake command in the CxxTemplate folder. To do that, write the following command:

cmake -Bbuild -H. -GNinja

The output of the preceding command is as follows:

Figure 1.3: Generating the Ninja build file

Let's explain parts of the preceding command. With -Bbuild, we are telling CMake to use the build folder to generate build artifacts. Since this folder does not exist, CMake will create it. With –H., we are telling CMake to use the current folder as the source. By using a separate build folder, we will keep our source files clean and all the build artifacts will live in the build folder, which is ignored by Git thanks to our .gitignore file. With –GNinja, we are telling CMake to use the Ninja build system.

Run the following commands to list the project files and to check the files that were created inside the build folder:

ls

ls build

The preceding command will show the following output in the terminal:

Figure 1.4: Files in the build folder

It's clear that the preceding files will be present inside the build folder. build.ninja and rules.ninja in the preceding output are the Ninja build files that can actually build our project in this platform.

Note

By using CMake, we did not have to write the Ninja build files and avoided committing to the Unix platform. Instead, we have a meta-build system that can generate low-level build files for other platforms such as UNIX/Linux, MinGW, and Nmake.

Now, go into the build folder and build our project by typing the following commands in the terminal:

cd build

ninja

You should see a final output like the following:

Figure 1.5: Building with ninja
Type ls in the build folder and check whether we have generated the CxxTemplate executable or not:

ls

The previous command yields the following output in the terminal:

Figure 1.6: Files in the build folder after running ninja

In the preceding figure, you can see that the CxxTemplate executable is generated.

In the terminal, type the following command to run the CxxTemplate executable:

./CxxTemplate

The previous command in the terminal will provide the following output:

Figure 1.7: Running the executable

The following line from the src/CxxTemplate.cpp file is responsible for writing the previous output:

std::cout << "Hello CMake." << std::endl;

Now you have successfully built a CMake project in Linux. Ninja and CMake work quite well together. You have to run CMake only once and Ninja will detect whether CMake should be called again and will call it for you. For example, even if you add new source files to your CMakeLists.txt file, you only need to type the ninja command in the terminal, and it will run CMake automatically for you to update the Ninja build files. Now that you have learned about building a CMake project in Linux, in the next section, we will look at how to import a CMake project into Eclipse CDT.

Importing a CMake Project into Eclipse CDT

A Ninja build file is useful for building our project in Linux. However, a CMake project is portable and can be used with other build systems and IDEs as well. Many IDEs accept CMake as their configuration file and provide a seamless experience as you modify and build your project. In this section, we will discuss how to import a CMake project into Eclipse CDT, which is a popular cross-platform C/C++ IDE.

There are multiple ways of using Eclipse CDT with CMake. The default one that CMake provides is the one-way generation of the IDE project. Here, you create the IDE project once, and any modifications you make to your IDE project will not change back the original CMake project. This is useful if you manage your project as a CMake project and do one-time builds with Eclipse CDT. However, it's not ideal if you want to do your development in Eclipse CDT.

Another way of using CMake with Eclipse CDT is to use the custom cmake4eclipse plugin. When using this plugin, you do not abandon your CMakeLists.txt file and make a one-way switch to Eclipse CDT's own project manager. Instead, you keep managing your project through the CMakeLists.txt file, which continues to be the main configuration file of your project. Eclipse CDT actively works with your CMakeLists.txt file to build your project. You can add or remove source files and make other changes in your CMakeLists.txt, and the cmake4eclipse plugin applies those changes to the Eclipse CDT project at every build. You will have a nice IDE experience while keeping your CMake project current. The benefit of this approach is that you can always quit using Eclipse CDT and use your CMakeLists.txt file to switch to another build system (such as Ninja) later. We will use this second approach in the following exercise.

Exercise 2: Importing the CMake File into Eclipse CDT

In the last exercise, you developed a CMake project and you would like to start using Eclipse CDT IDE to edit and build that project. In this exercise, we will import our CMake project into the Eclipse CDT IDE using the cmake4eclipse plugin. Perform the following steps to complete the exercise:

Open Eclipse CDT. Create a new C++ project in the location of our current project (the folder that contains the CMakeLists.txt file and the src folder). Go to File | New | Project. A New Project dialog box appears like the one in the following screenshot:
Figure 1.8: New Project dialog box
Select the C++ Project option and click on the Next button. A C++ Project dialog box appears like the one in the following screenshot:
Figure 1.9: C++ Project dialog box
Accept everything, including switching to the C/C++ perspective, and click Finish.Click on the Restore button at the top-left corner to view the newly created project:
Figure 1.10: The Restore button
Click on the CxxTemplate project. Go to Project | Properties, then select Tool Chain Editor under C/C++ Build from the left pane and set Current builder to CMake Builder (portable). Then, click on the Apply and Close button:
Figure 1.11: Project properties
Then, choose the Project | Build All menu item to build the project:
Figure 1.12: Building the project
In the following Console pane, you will see the output of CMake as if you called it from the command line, followed by a call to make all that actually builds our project:
Figure 1.13: The build output
If you did not get any errors in the previous steps, you can run the project using the menu item Run | Run. If you are given some options, choose Local C/C++ Application and CxxTemplate as the executable:
Figure 1.14: Running a project
When it runs, you will see the output of the program in the Console pane as follows:
Figure 1.15: Output of the project

You have successfully built and run a CMake project using Eclipse CDT. In the next exercise, we will introduce a frequent change to our projects by adding new source files with new classes.

Exercise 3: Adding New Source Files to CMake and Eclipse CDT

As you develop significantly bigger C++ projects, you will tend to add new source files to it as the project grows to meet the set expectations. In this exercise, we will add a new .cpp and .h file pair to our project and see how CMake and Eclipse CDT work together with these changes. We will add these files inside the project using the new class wizard, but you can also create them with any other text editor. Perform the following steps to add new source files to CMake and Eclipse CDT:

First, open the project that we have been using until now. In the Project Explorer pane on the left, expand the root entry, CxxTemplate, and you will see the files and folders of our project. Right-click the src folder and select New | Class from the pop-up menu:
Figure 1.16: Creating a new class
In the dialog box that opened, type ANewClass for the class name. When you click on the Finish button, you will see the ANewClass.cpp and ANewClass.h files generated under the src folder.Now, let's write some code into the ANewClass class and access it from the CxxTemplate class that we already had. Open ANewClass.cpp and change the beginning of the file to match the following, and then save the file:

#include "ANewClass.h"

#include <iostream>

void ANewClass::run() {

    std::cout << "Hello from ANewClass." << std::endl;

}

You will see that Eclipse warns us with a Member declaration not found message:

Figure 1.17: Analyzer warning

This error is generated since we need to add this to our ANewClass.h file as well. Such warnings are made possible by analyzers in IDEs and are quite useful as they help you fix your code as you are typing, without running the compiler.

Open the ANewClass.h file, add the following code, and save the file:

public:

    void run(); // we added this line

    ANewClass();

You should see that the error in the .cpp file went away. If it did not go away, it may be because you may have forgotten to save one of the files. You should make it a habit to press Ctrl + S to save the current file, or Shift + Ctrl + S to save all the files that you edited.

Now, let's use this class from our other class, CxxTemplate.cpp. Open that file, perform the following modifications, and save the file. Here, we are first importing header files and in the constructor of CxxApplication, we are printing text to the console. Then, we are creating a new instance of ANewClass and calling its run method:

#include "CxxTemplate.h"

#include "ANewClass.h"

#include <string>

...

CxxApplication::CxxApplication( int argc, char *argv[] ) {

  std::cout << "Hello CMake." << std::endl;

  ::ANewClass anew;

  anew.run();

}

Note

The complete code of this file can be found here: https://github.com/TrainingByPackt/Advanced-CPlusPlus/blob/master/Lesson1/Exercise03/src/CxxTemplate.cpp.

Try to build the project by clicking on the Project | Build All menu options. You will get some undefined reference errors in two lines. This is because our project is built with CMake's rules and we did not let CMake know about this new file. Open the CMakeLists.txt file, make the following modification, and save the file:

add_executable(CxxTemplate

  src/CxxTemplate.cpp  

  src/ANewClass.cpp

)

Try to build the project again. This time you should not see any errors.

Run the project using the Run | Run menu option. You should see the following output in the terminal:
Figure 1.18: Program output

You modified a CMake project, added new files to it, and ran it fine. Note that we created the files in the src folder and let the CMakeLists.txt file know about the CPP file. If you do not use Eclipse, you can simply continue with the usual CMake build commands and your program will run successfully. So far, we have checked out the sample code from GitHub and built it both with plain CMake and with the Eclipse IDE. We also added a new class to the CMake project and rebuilt it in Eclipse IDE. Now you know how to build and modify CMake projects. In the next section, we will perform an activity of adding a new source-header file pair to the project.

Activity 1: Adding a New Source-Header File Pair to the Project

As you develop C++ projects, you add new source files to it as the project grows. You may want to add new source files for various reasons. For example, let's say you are developing an accounting application in which you calculate interest rates in many places of your project, and you want to create a function in a separate file so that you can reuse it throughout your project. To keep things simple, here we will create a simple summation function instead. In this activity, we will add a new source-header file pair to the project. Perform the following steps to complete the activity:

Open the project that we created in the earlier exercise in the Eclipse IDE.Add the SumFunc.cpp and SumFunc.h file pair to the project. Create a simple function named sum that returns the sum of two integers.Call the function from the CxxTemplate class constructor.Build and run the project in Eclipse.

The expected output should be similar to the following:

Figure 1.19: Final output

Note

The solution for this activity can be found on page 620.

In the following section, we will talk about how to write unit tests for our projects. It is common to divide projects into many classes and functions that work together to achieve the desired goal. You must manage the behavior of these classes and functions with unit tests to ensure that they behave in expected ways.

Unit Testing

Unit tests are an important part of programming in general. Basically, unit tests are little programs that use our classes in various scenarios with expected results, live in a parallel file hierarchy in our project, and do not end up in the actual executable but are executed separately by us during development to ensure that our code is behaving in expected ways. We should write unit tests for our C++ programs to ensure that they behave as they are supposed to after each change.

Preparing for the Unit Tests

There are several C++ test frameworks that we can use with CMake. We will use Google Test, which has several benefits over other options. In the next exercise, we will prepare our project for unit testing with Google Test.

Exercise 4: Preparing Our Project for Unit Testing

We have installed Google Test but our project is not set up to use Google Test for unit testing. In addition to the installation, there are settings that need to be carried out in our CMake project to have Google Test unit tests. Follow these steps to implement this exercise:

Open Eclipse CDT and select the CxxTemplate project that we have been using.Create a new folder named tests as we will perform all our tests there. Edit our base CMakeLists.txt file to allow tests in the tests folder. Note that we already had code to find the GTest package that brings GoogleTest capability to CMake. We will add our new lines just after that:

find_package(GTest)

if(GTEST_FOUND)

set(Gtest_FOUND TRUE)

endif()

if(GTest_FOUND)

include(GoogleTest)

endif()

# add these two lines below

enable_testing()

add_subdirectory(tests)

This is all we need to add to our main CMakeLists.txt file.

Create another CMakeLists.txt file inside our tests folder. This will be used because of the add_subdirectory(tests) line that we had in our main CMakeLists.txt file. This tests/CMakeLists.txt file will manage the test sources. Add the following code in the tests/CMakeLists.txt file:

include(GoogleTest)

add_executable(tests CanTest.cpp)

target_link_libraries(tests GTest::GTest)

gtest_discover_tests(tests)

Let's dissect this code line by line. The first line brings in the Google Test capability. The second line creates the tests executable, which will include all our test source files. In this case, we only have one CanTest.cpp file, which will just verify that the testing works. After that, we link the GTest library to the tests executable. The last line identifies all individual tests in the tests executable and adds them to CMake as a test. This way, various test tools will be able to tell us which individual tests failed and which ones passed.

Create a tests/CanTest.cpp file. Add this code to simply verify that tests are running, without actually testing anything in our actual project:

#include "gtest/gtest.h"

namespace {

class CanTest: public ::testing::Test {};

TEST_F(CanTest, CanReallyTest) {

  EXPECT_EQ(0, 0);

}

}  

int main(int argc, char **argv) {

  ::testing::InitGoogleTest(&argc, argv);

  return RUN_ALL_TESTS();

}

The TEST_F line is an individual test. Now, EXPECT_EQ(0, 0) is testing whether zero is equal to zero, which will always succeed if we can actually run the test. We will later add the results of our own classes here to be tested against various values. Now we have the necessary setup for Google Test in our project. Next, we will build and run these tests.

Building, Running, and Writing Unit Tests

Now, we will discuss how to build, run, and write unit tests. The example that we have so far is a simple dummy test that is ready to be built and run. Later, we will add tests that make more sense and view the output of passing and failing tests. In the following exercise, we will build, run, and write unit tests for the project that we created in the previous exercise.

Exercise 5: Building and Running Tests

So far, you have created a project with GoogleTest set up, but you did not build or run the tests we created. In this exercise, we will build and run the tests that we created. Since we added our tests folder using add_subdirectory, building the project will automatically build the tests. Running the tests will require some more effort. Perform the following steps to complete the exercise:

Open our CMake project in Eclipse CDT.To build the tests, simply build the project just like you did before. Here is the output of building the project one more time from Eclipse after a full build using Project | Build All:
Figure 1.20: Build operation and its output
If you do not see this output, your console may be in the wrong view. You can correct it as shown in the following figures:
Figure 1.21: Viewing the correct console output
Figure 1.22: Viewing the correct console output

As you can see, our project now has two executable targets. They both live in the build folder, as with any other build artifact. Their locations are build/Debug/CxxTemplate and build/Debug/tests/tests. Since they are executables, we can simply run them.

We ran CxxTemplate before and will not see any extra output now. Run the other executable by typing the following command in the terminal while we are in our project folder:

./build/Debug/tests/tests

The preceding code generates the following output in the terminal:

Figure 1.23: Running the tests executable

This is the simple output of our tests executable. If you want to see whether the tests have passed, you can simply run this. However, testing is so much more than that.

One of the ways you can run your tests is by using the ctest command. Write the following commands in the terminal while you are in the project folder. We go to the folder where the tests executable resides, run ctest there, and come back:

cd build/Debug/tests

ctest

cd ../../..

And here is the output that you will see:

Figure 1.24: Running ctest

Note

The ctest command can run your tests executable with a number of options, including the ability to submit test results automatically to online dashboards. Here, we will simply run the ctest command; its further features are left as an exercise for the interested reader. You can type ctest --help or visit the online documentation to discover ctest further at https://cmake.org/cmake/help/latest/manual/ctest.1.html#.

Another way to run the tests is to run them inside Eclipse, in a nice graphical report format. For this, we will create a run configuration that is test-aware. In Eclipse, click on Run | Run Configurations…, right-click C/C++ Unit on the left, and select New Configuration.Change the name from CxxTemplate Debug to CxxTemplate Tests as follows:
Figure 1.25: Changing the name of the run configuration
Under C/C++ Application, select the Search Project option:
Figure 1.26: Run Configurations
Choose tests in the new dialog:
Figure 1.27: Creating the test run configuration and selecting the tests executable
Next, go to the C/C++ Testing tab and select Google Tests Runner in the dropdown. Click on Apply at the bottom of the dialog and click on the Run option for the test that we have to run for the first time:
Figure 1.28: Run Configurations
In the upcoming runs, you can either click the dropdown next to the play button in the toolbar, or choose Run | Run History to choose CxxTemplate Tests:
Figure 1.29: Finalizing the run configuration settings and selecting a configuration to run

The result will be similar to the following screenshot:

Figure 1.30: Run results of the unit test

This is a nice report that contains entries for all tests—only one for now. You may prefer this if you do not want to leave the IDE. Furthermore, when you have many tests, this interface can help you filter them effectively. Now you have built and run tests that were written using Google Test. You ran them in a couple of different ways, including directly executing the test, using ctest, and using Eclipse CDT. In the next section, we will solve an exercise wherein we will actually test the functionality of our code.

Exercise 6: Testing the Functionality of Code

You have run simple tests but now you want to write meaningful tests that are testing functionality. In the initial activity, we created SumFunc.cpp, which had the sum function. Now, in this exercise, we will write a test for that file. In this test, we will use the sum function to add two numbers and verify that the result is correct. Let's recall the contents of the following files with the sum function from before:

src/SumFunc.h:

#ifndef SRC_SUMFUNC_H_

#define SRC_SUMFUNC_H_

int sum(int a, int b);

#endif /* SRC_SUMFUNC_H_ */

src/SumFunc.cpp:

#include "SumFunc.h"

#include <iostream>

int sum(int a, int b) {

  return a + b;

}

Relevant lines of CMakeLists.txt:

add_executable(CxxTemplate

  src/CxxTemplate.cpp  

  src/ANewClass.cpp

  src/SumFunc.cpp

)

Also, let's recall our CantTest.cpp file, which has the main() function of our unit tests:

#include "gtest/gtest.h"

namespace {

class CanTest: public ::testing::Test {};

TEST_F(CanTest, CanReallyTest) {

  EXPECT_EQ(0, 0);

}

}  

int main(int argc, char **argv) {

  ::testing::InitGoogleTest(&argc, argv);

  return RUN_ALL_TESTS();

}

Perform the following steps to complete the exercise:

Open our CMake project in Eclipse CDT.Add a new test source file (tests/SumFuncTest.cpp) with the following content:

#include "gtest/gtest.h"

#include "../src/SumFunc.h"

namespace {

  class SumFuncTest: public ::testing::Test {};

  TEST_F(SumFuncTest, CanSumCorrectly) {

    EXPECT_EQ(7, sum(3, 4));

  }

}

Note that this does not have a main() function since CanTest.cpp has one and these will be linked together. Secondly, note that this includes SumFunc.h, which is in the src folder of the project and uses it as sum(3, 4) inside the test. This is how we use our project code in tests.

Make the following change in the tests/CMakeLists.txt file to build the test:

include(GoogleTest)

add_executable(tests CanTest.cpp SumFuncTest.cpp ../src/SumFunc.cpp) # added files here

target_link_libraries(tests GTest::GTest)

gtest_discover_tests(tests)

Note that we added both the test (SumFuncTest.cpp) and the code that it tests (../src/SumFunc.cpp) to the executable, as our test code is using the code from the actual project.

Build the project and run the test as before. You should see the following report:
Figure 1.31: Output after running the test

We can add such tests to our project and all of them will appear on the screen as shown in the preceeding screenshot.

Now, let's add one more test that will actually fail. In the tests/SumFuncTest.cpp file, make the following change:

TEST_F(SumFuncTest, CanSumCorrectly) {

  EXPECT_EQ(7, sum(3, 4));

}

// add this test

TEST_F(SumFuncTest, CanSumAbsoluteValues) {

  EXPECT_EQ(6, sum(3, -3));

}

Note that this test assumes that the absolute values of the inputs are summed up, which is incorrect. The result of this call is 0 but is expected to be 6 in this example. This is the only change that we have to make in our project to add this test.

Now, build the project and run the test. You should see this report:
Figure 1.32: The build report

As you can see in the preceding figure, the first two tests passed and the last test failed. When we see this output, there are two options: either our project code is wrong, or the test is wrong. In this case, our test is wrong. This is because our CanSumAbsoluteValues test case expects that 6 is equal to sum(3, -3). This is because we assumed that our function sums up the absolute values of the integers provided. However, this is not the case. Our function simply adds the given numbers, whether they are positive or negative. Therefore, this test had a faulty assumption and failed.

Let's change the test and fix it. Change the test so that we expect the sum of -3 and 3 to be 0. Rename the test to reflect what this test actually does:

TEST_F(SumFuncTest, CanSumCorrectly) {

  EXPECT_EQ(7, sum(3, 4));

}

// change this part

TEST_F(SumFuncTest, CanUseNegativeValues) {

  EXPECT_EQ(0, sum(3, -3));

}

Run it now and observe in the report that all the tests pass:
Figure 1.33: Test execution is successful

Finally, we have set up Google Test with CMake both in our system and project. We also wrote, built, and ran unit tests with Google Test, both in the terminal and in Eclipse. Ideally, you should write unit tests for every class and cover every possible usage. You should also run the tests after each major change and make sure you do not break existing code. In the next section, we will perform an activity of adding a new class and its test.

Activity 2: Adding a New Class and Its Test

As you develop a C++ project, you add new source files to it as the project grows. You also write tests for them to ensure that they are working properly. In this activity, we will add a new class that simulates 1D linear motion. The class will have double fields for position and velocity. It will also have a advanceTimeBy() method, which receives a double dt parameter, which modifies position based on the value of velocity. Use EXPECT_DOUBLE_EQ instead of EXPECT_EQ for double values. In this activity, we will add a new class and its test to the project. Follow these steps to perform this activity:

Open the project that we have created in the Eclipse IDE.Add the LinearMotion1D.cpp and LinearMotion1D.h file pair to the project that contains the LinearMotion1D class. In this class, create two double fields: position and velocity. Also, create an advanceTimeBy(double dt) function that modifies position.Write tests for this in the tests/LinearMotion1DTest.cpp file. Write two tests that represent motion in two different directions.Build and run it in the Eclipse IDE.Verify that the tests have passed.

The final test results should look similar to the following:

Figure 1.34: Final test results

Note

The solution for this activity can be found on page 622.

Adding new classes and their tests is a very common task in C++ development. We create classes for various reasons. Sometimes, we have a nice software design plan and we create the classes that it calls for. Other times, when a class becomes too large and monolithic, we separate some of its responsibility to another class in a meaningful way. Having this task be practical is important to prevent dragging your feet and ending up with huge monolithic classes. In the following section, we discuss what happens during the compilation and linking stages. This will give us a better perspective of what is happening under the hood of C++ programs.

Understanding Compilation, Linking, and Object File Contents

One of the main reasons for using C++ is efficiency. C++ gives us control over memory management, which is why understanding how objects are laid out in memory is important. Furthermore, C++ source files and libraries are compiled to object files for the target hardware and linked together. Often, C++ programmers have to deal with linker problems, which is why understanding the steps of the compilation and being able to investigate object files is important. On the other hand, large projects are developed and maintained by teams over a long period of time, which is why creating clean and understandable code is important. As with any other software, bugs arise in C++ projects and need to be identified, analyzed, and resolved carefully by observing the program behavior. Therefore, learning how to debug C++ code is also important. In the next section, we will learn how to create code that is efficient, plays well with other code, and is maintainable.

Compilation and Linking Steps

A C++ project is created as a set of source code files and project configuration files that organize the sources and library dependencies. In the compilation step, these sources are first converted to object files. In the linking step, these object files are linked together to form the executable that is the ultimate output of the project. The libraries that the project uses are also linked at this step.

In the upcoming exercises, we will use our existing project to observe the compilation and linking stages. Then, we will manually recreate them to view the process in more detail.

Exercise 7: Identifying Build Steps

You have been building your projects without investigating the details of the build actions. In this exercise, we will investigate the details of our project's build steps. Perform the following to complete the exercise:

Open the terminal.Navigate to the build folder wherein our Makefile file resides by typing the following command:

cd build/Debug

Clean the project and run the build in VERBOSE mode using the following command:

make clean

make VERBOSE=1 all

You will get a detailed output of the build process in the terminal, which may look a bit crowded:

Figure 1.35: The build process part 1
Figure 1.36: The build process part 2
Figure 1.37: The full build output

Here are some of the lines from this output. The following lines are the important ones related to the compilation and linkage of the main executable:

/usr/bin/c++    -g   -pthread -std=gnu++1z -o CMakeFiles/CxxTemplate.dir/src/CxxTemplate.cpp.o -c /home/username/Packt/Cpp2019/CxxTemplate/src/CxxTemplate.cpp

/usr/bin/c++    -g   -pthread -std=gnu++1z -o CMakeFiles/CxxTemplate.dir/src/ANewClass.cpp.o -c /home/username/Packt/Cpp2019/CxxTemplate/src/ANewClass.cpp

/usr/bin/c++    -g   -pthread -std=gnu++1z -o CMakeFiles/CxxTemplate.dir/src/SumFunc.cpp.o -c /home/username/Packt/Cpp2019/CxxTemplate/src/SumFunc.cpp

/usr/bin/c++    -g   -pthread -std=gnu++1z -o CMakeFiles/CxxTemplate.dir/src/LinearMotion1D.cpp.o -c /home/username/Packt/Cpp2019/CxxTemplate/src/LinearMotion1D.cpp

/usr/bin/c++  -g   CMakeFiles/CxxTemplate.dir/src/CxxTemplate.cpp.o CMakeFiles/CxxTemplate.dir/src/ANewClass.cpp.o CMakeFiles/CxxTemplate.dir/src/SumFunc.cpp.o CMakeFiles/CxxTemplate.dir/src/LinearMotion1D.cpp.o  -o CxxTemplate -pthread

The c++ command here is just a symbolic link to the g++ compiler. To see that it's actually a chain of symbolic links, type the following command:

namei /usr/bin/c++

You will see the following output:

Figure 1.38: The chain of symbolic links for /usr/bin/c++

Therefore, we will use c++ and g++ interchangeably throughout our discussion. In the build output that we quoted earlier, the first four lines are compiling each .cpp source file and creating the corresponding .o object file. The last line is linking together these object files to create the CxxTemplate executable. The following figure visually presents this process:

Figure 1.39: Execution stages of a C++ project

As the previous figure shows, the CPP files that are added to CMake as a part of a target, along with the header files that they included, are compiled to object files, which are later linked together to create the target executable.

To understand this process even further, let's carry out the compilation steps ourselves. In the terminal, go to the project folder and create a new folder named mybuild using the following commands:

cd ~/CxxTemplate

mkdir mybuild

Then, run the following commands to compile the CPP source files to object files:

/usr/bin/c++ src/CxxTemplate.cpp -o mybuild/CxxTemplate.o -c

/usr/bin/c++ src/ANewClass.cpp -o mybuild/ANewClass.o -c

/usr/bin/c++ src/SumFunc.cpp -o mybuild/SumFunc.o -c

/usr/bin/c++ src/LinearMotion1D.cpp -o mybuild/LinearMotion1D.o -c

Go into the mybuild directory and see what's there using the following command:

cd mybuild

ls

We see the following output as expected. These are our object files:

Figure 1.40: Compiled object files
In the next step, link the object files together to form our executable. Type the following command:

/usr/bin/c++  CxxTemplate.o ANewClass.o SumFunc.o LinearMotion1D.o  -o CxxTemplate

Now, let's see our executable among the list of files here by typing the following command:

ls

This shows the new CxxTemplate file in the following figure:

Figure 1.41: Linked executable file
Now, run our executable by typing the following command:

./CxxTemplate

And see the output that we had before:

Figure 1.42: Executable file output

Now that you have examined the details of the build process and have recreated them yourself, in the next section, let's explore the linking process.

The Linking Step

In this section, let's look at a connection between two source files and how they end up in the same executable. Look at the sum function in the following figure:

Figure 1.43: The linking process

The sum function's body is defined in SumFunc.cpp. It has a forward declaration in SumFunc.h