C++ Crash Course: A Fast-Paced Introduction 1593278888, 9781593278885

A fast-paced, thorough introduction to modern C++ written for experienced programmers. After reading C++ Crash Course, y

278 47 148MB

English Pages 733 [794] Year 2019

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
Brief Contents
Contents in Detail
Foreword
Acknowledgments
Introduction
About This Book
Who Should Read This Book?
What’s in This Book?
Part I: The C++ Core Language
Part II: C++ Libraries and Frameworks
An Overture to C Programmers
Upgrading to Super C
Function Overloading
References
auto Initialization
Namespaces and Implicit typedef of struct, union, and enum
Intermingling C and C++ Object Files
C++ Themes
Expressing Ideas Concisely and Reusing Code
The C++ Standard Library
Lambdas
Generic Programming with Templates
Class Invariants and Resource Management
Move Semantics
Relax and Enjoy Your Shoes
Part I: The C++ Core Language
Chapter 1: Up and Running
The Structure of a Basic C++ Program
Creating Your First C++ Source File
Main: A C++ Program’s Starting Point
Libraries: Pulling in External Code
The Compiler Tool Chain
Setting Up Your Development Environment
Windows 10 and Later: Visual Studio
macOS: Xcode
Linux and GCC
Text Editors
Bootstrapping C++
The C++ Type System
Declaring Variables
Initializing a Variable’s State
Conditional Statements
Functions
printf Format Specifiers
Revisiting step_function
Comments
Debugging
Visual Studio
Xcode
GCC and Clang Debugging with GDB and LLDB
Summary
Chapter 2: Types
Fundamental Types
Integer Types
Floating-Point Types
Character Types
Boolean Types
The std::byte Type
The size_t Type
void
Arrays
Array Initialization
Accessing Array Elements
A Nickel Tour of for Loops
C-Style Strings
User-Defined Types
Enumeration Types
Plain-Old-Data Classes
Unions
Fully Featured C++ Classes
Methods
Access Controls
Constructors
Initialization
The Destructor
Summary
Chapter 3: Reference Types
Pointers
Addressing Variables
Dereferencing Pointers
The Member-of-Pointer Operator
Pointers and Arrays
Pointers Are Dangerous
void Pointers and std::byte Pointers
nullptr and Boolean Expressions
References
Usage of Pointers and References
Forward-Linked Lists: The Canonical Pointer-Based Data Structure
Employing References
this Pointers
const Correctness
const Member Variables
Member Initializer Lists
auto Type Deduction
Initialization with auto
auto and Reference Types
auto and Code Refactorings
Summary
Chapter 4: The Object Life Cycle
An Object’s Storage Duration
Allocation, Deallocation, and Lifetime
Memory Management
Automatic Storage Duration
Static Storage Duration
Thread-Local Storage Duration
Dynamic Storage Duration
Tracing the Object Life Cycle
Exceptions
The throw Keyword
Using try-catch Blocks
stdlib Exception Classes
Handling Exceptions
User-Defined Exceptions
The noexcept Keyword
Call Stacks and Exceptions
A SimpleString Class
Appending and Printing
Using SimpleString
Composing a SimpleString
Call Stack Unwinding
Exceptions and Performance
Alternatives to Exceptions
Copy Semantics
Copy Constructors
Copy Assignment
Default Copy
Copy Guidelines
Move Semantics
Copying Can Be Wasteful
Value Categories
lvalue and rvalue References
The std::move Function
Move Construction
Move Assignment
The Final Product
Compiler-Generated Methods
Summary
Chapter 5: Runtime Polymorphism
Polymorphism
A Motivating Example
Adding New Loggers
Interfaces
Object Composition and Implementation Inheritance
Defining Interfaces
Base Class Inheritance
Member Inheritance
virtual Methods
Pure-Virtual Classes and Virtual Destructors
Implementing Interfaces
Using Interfaces
Updating the Bank Logger
Constructor Injection
Property Injection
Choosing Constructor or Property Injection
Summary
Chapter 6: Compile-Time Polymorphism
Templates
Declaring Templates
Template Class Definitions
Template Function Definitions
Instantiating Templates
Named Conversion Functions
const_cast
static_cast
reinterpret_cast
narrow_cast
mean: A Template Function Example
Genericizing mean
Template Type Deduction
SimpleUniquePointer: A Template Class Example
Type Checking in Templates
Concepts
Defining a Concept
Type Traits
Requirements
Building Concepts from Requires Expressions
Using Concepts
Ad Hoc Requires Expressions
static_assert: The Preconcepts Stopgap
Non-Type Template Parameters
Variadic Templates
Advanced Template Topics
Template Specialization
Name Binding
Type Function
Template Metaprogramming
Template Source Code Organization
Polymorphism at Runtime vs. Compile Time
Summary
Chapter 7: Expressions
Operators
Logical Operators
Arithmetic Operators
Assignment Operators
Increment and Decrement Operators
Comparison Operators
Member Access Operators
Ternary Conditional Operator
The Comma Operator
Operator Overloading
Overloading Operator new
Operator Precedence and Associativity
Evaluation Order
User-Defined Literals
Type Conversions
Implicit Type Conversions
Explicit Type Conversion
C-Style Casts
User-Defined Type Conversions
Constant Expressions
A Colorful Example
The Case for constexpr
Volatile Expressions
Summary
Chapter 8: Statements
Expression Statements
Compound Statements
Declaration Statements
Functions
Namespaces
Type Aliasing
Structured Bindings
Attributes
Selection Statements
if Statements
switch Statements
Iteration Statements
while Loops
do-while Loops
for Loops
Ranged-Based for Loops
Jump Statements
break Statements
continue Statements
goto Statements
Summary
Chapter 9: Functions
Function Declarations
Prefix Modifiers
Suffix Modifiers
auto Return Types
auto and Function Templates
Overload Resolution
Variadic Functions
Variadic Templates
Programming with Parameter Packs
Revisiting the sum Function
Fold Expressions
Function Pointers
Declaring a Function Pointer
Type Aliases and Function Pointers
The Function-Call Operator
A Counting Example
Lambda Expressions
Usage
Lambda Parameters and Bodies
Default Arguments
Generic Lambdas
Lambda Return Types
Lambda Captures
constexpr Lambda Expressions
std::function
Declaring a Function
An Extended Example
The main Function and the Command Line
The Three main Overloads
Exploring Program Parameters
A More Involved Example
Exit Status
Summary
Part II: C++ Libraries and Frameworks
Chapter 10: Testing
Unit Tests
Integration Tests
Acceptance Tests
Performance Tests
An Extended Example: Taking a Brake
Implementing AutoBrake
Test-Driven Development
Adding a Service-Bus Interface
Unit-Testing and Mocking Frameworks
The Catch Unit-Testing Framework
Google Test
Boost Test
Summary: Testing Frameworks
Mocking Frameworks
Google Mock
HippoMocks
A Note on Other Mocking Options: FakeIt and Trompeloeil
Summary
Chapter 11: Smart Pointers
Smart Pointers
Smart Pointer Ownership
Scoped Pointers
Constructing
Bring in the Oath Breakers
Implicit bool Conversion Based on Ownership
RAII Wrapper
Pointer Semantics
Comparison with nullptr
Swapping
Resetting and Replacing a scoped_ptr
Non-transferability
boost::scoped_array
A Partial List of Supported Operations
Unique Pointers
Constructing
Supported Operations
Transferable, Exclusive Ownership
Unique Arrays
Deleters
Custom Deleters and System Programming
A Partial List of Supported Operations
Shared Pointers
Constructing
Specifying an Allocator
Supported Operations
Transferable, Non-Exclusive Ownership
Shared Arrays
Deleters
A Partial List of Supported Operations
Weak Pointers
Constructing
Obtaining Temporary Ownership
Advanced Patterns
Supported Operations
Intrusive Pointers
Summary of Smart Pointer Options
Allocators
Summary
Chapter 12: Utilities
Data Structures
tribool
optional
pair
tuple
any
variant
Date and Time
Boost DateTime
Chrono
Numerics
Numeric Functions
Complex Numbers
Mathematical Constants
Random Numbers
Numeric Limits
Boost Numeric Conversion
Compile-Time Rational Arithmetic
Summary
Chapter 13: Containers
Sequence Containers
Arrays
Vectors
Niche Sequential Containers
Associative Containers
Sets
Unordered Sets
Maps
Niche Associative Containers
Graphs and Property Trees
The Boost Graph Library
Boost Property Trees
Initializer Lists
Summary
Chapter 14: Iterators
Iterator Categories
Output Iterators
Input Iterators
Forward Iterators
Bidirectional Iterators
Random-Access Iterators
Contiguous Iterators
Mutable Iterators
Auxiliary Iterator Functions
std::advance
std::next and std::prev
std::distance
std::iter_swap
Additional Iterator Adapters
Move Iterator Adapters
Reverse Iterator Adapters
Summary
Chapter 15: Strings
std::string
Constructing
String Storage and Small String Optimizations
Element and Iterator Access
String Comparisons
Manipulating Elements
Search
Numeric Conversions
String View
Constructing
Supported string_view Operations
Ownership, Usage, and Efficiency
Regular Expressions
Patterns
basic_regex
Algorithms
Boost String Algorithms
Boost Range
Predicates
Classifiers
Finders
Modifying Algorithms
Splitting and Joining
Searching
Boost Tokenizer
Localizations
Summary
Chapter 16: Streams
Streams
Stream Classes
Stream State
Buffering and Flushing
Manipulators
User-Defined Types
String Streams
File Streams
Stream Buffers
Random Access
Summary
Chapter 17: Filesystems
Filesystem Concepts
std::filesystem::path
Constructing Paths
Decomposing Paths
Modifying Paths
Summary of Filesystem Path Methods
Files and Directories
Error Handling
Path-Composing Functions
Inspecting File Types
Inspecting Files and Directories
Manipulating Files and Directories
Directory Iterators
Constructing
Directory Entries
Recursive Directory Iteration
fstream Interoperation
Summary
Chapter 18: Algorithms
Algorithmic Complexity
Execution Policies
Non-Modifying Sequence Operations
all_of
any_of
none_of
for_each
for_each_n
find, find_if, and find_if_not
find_end
find_first
adjacent_find
count
mismatch
equal
is_permutation
search
search_n
Mutating Sequence Operations
copy
copy_n
copy_backward
move
move_backward
swap_ranges
transform
replace
fill
generate
remove
unique
reverse
sample
shuffle
Sorting and Related Operations
sort
stable_sort
partial_sort
is_sorted
nth_element
Binary Search
lower_bound
upper_bound
equal_range
binary_search
Partitioning Algorithms
is_partitioned
partition
partition_copy
stable_partition
Merging Algorithms
merge
Extreme-Value Algorithms
min and max
min_element and max_element
clamp
Numeric Operations
Useful Operators
iota
accumulate
reduce
inner_product
adjacent_difference
partial_sum
Other Algorithms
Boost Algorithms
Chapter 19: Concurrency and Parallelism
Concurrent Programming
Asynchronous Tasks
Sharing and Coordinating
Low-Level Concurrency Facilities
Parallel Algorithms
An Example: Parallel sort
Parallel Algorithms Are Not Magic
Summary
Chapter 20: Network Programming with Boost Asio
The Boost Asio Programming Model
Network Programming with Asio
The Internet Protocol Suite
Hostname Resolution
Connecting
Buffers
Reading and Writing Data with Buffers
The Hypertext Transfer Protocol (HTTP)
Implementing a Simple Boost Asio HTTP Client
Asynchronous Reading and Writing
Serving
Multithreading Boost Asio
Summary
Chapter 21: Writing Applications
Program Support
Handling Program Termination and Cleanup
Communicating with the Environment
Managing Operating System Signals
Boost ProgramOptions
The Options Description
Parsing Options
Storing and Accessing Options
Putting It All Together
Special Topics in Compilation
Revisiting the Preprocessor
Compiler Optimization
Linking with C
Summary
Index
Blank Page
Blank Page
Recommend Papers

C++ Crash Course: A Fast-Paced Introduction
 1593278888, 9781593278885

  • 0 0 0
  • Like this paper and download? You can publish your own PDF file online for free in a few minutes! Sign Up
File loading please wait...
Citation preview

MODERN C++, COMPILED AND OPTIMIZED

Written for intermediate to advanced programmers, C++ Crash Course cuts through the weeds to get straight to the core of C++17, the most modern revision of the ISO standard. Part I covers the core C++ language, from types and functions to the object life cycle and expressions. Part II introduces the C++ Standard Library and Boost Libraries, where you’ll learn about special utility classes, data structures, and algorithms, as well as how to manipulate file systems and build high-performance programs that communicate over networks. You’ll learn all the major features of modern C++, including: • Fundamental types, reference types, and userdefined types • Compile-time polymorphism with templates and runtime polymorphism with virtual classes

• The object lifecycle including storage duration, call stacks, memory management, exceptions, and the RAII (resource acquisition is initialization) paradigm • Advanced expressions, statements, and functions • Smart pointers, data structures, dates and times, numerics, and probability/statistics facilities • Containers, iterators, strings, and algorithms • Streams and files, concurrency, networking, and application development With well over 500 code samples and nearly 100 exercises, C++ Crash Course is sure to help you build a strong C++ foundation. ABOUT THE AUTHOR

Josh Lospinoso served for 15 years in the US Army and built the C++ course used by the US Cyber Command to teach its junior developers. He has published over 20 peer-reviewed articles and co-founded a successfully acquired security company. A Rhodes Scholar, Lospinoso holds a PhD in Statistics from the University of Oxford.

w w w.nostarch.com

$59.95 ($78.95 CDN) SHELVE IN: PROGRAMMING LANGUAGES/C++

LOSPINOSO

T H E F I N E ST I N G E E K E N T E RTA I N M E N T ™

C ++ C R A S H C O U R S E

C++ is one of the most widely used languages for real-world software. In the hands of a knowledgeable programmer, C++ can produce small, efficient, and readable code that any programmer would be proud of.

C ++

Covers C++17

CR A SH COURSE A

F A S T - P A C E D

I N T R O D U C T I O N

JOSH LOSPINOSO

C++ CRASH COURSE

C++ CRASH COURSE A Fast-Paced Introduction

b y Jos h L o s p i n o s o

San Francisco

C++ CRASH COURSE. Copyright © 2019 by Josh Lospinoso. All rights reserved. No part of this work may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording, or by any information storage or retrieval system, without the prior written permission of the copyright owner and the publisher. Printed in USA Second printing 23 22 21 20   2 3 4 5 6 7 8 9

FSC LOGO

FPO

ISBN-10: 1-59327-888-8 ISBN-13: 978-1-59327-888-5 Publisher: William Pollock Production Editors: Meg Sneeringer and Riley Hoffman Cover Illustration: Josh Ellingson Interior Design: Octopod Studios Developmental Editors: Chris Cleveland and Patrick DiJusto Technical Reviewer: Kyle Willmon Copyeditor: Anne Marie Walker Compositors: Happenstance Type-O-Rama, Riley Hoffman, and Meg Sneeringer Proofreader: Paula L. Fleming For information on distribution, translations, or bulk sales, please contact No Starch Press, Inc. directly: No Starch Press, Inc. 245 8th Street, San Francisco, CA 94103 phone: 1.415.863.9900; [email protected] www.nostarch.com Library of Congress Cataloging-in-Publication Data Names: Lospinoso, Josh, author. Title: C++ crash course : a fast-paced introduction / Josh Lospinoso. Description: First edition. | San Francisco, CA : No Starch Press, Inc., [2019] Identifiers: LCCN 2019020529 (print) | LCCN 2019022057 (ebook) | ISBN 9781593278892 (epub) | ISBN 1593278896 (epub) | ISBN 9781593278885 (print) | ISBN 1593278888 (print) Subjects: LCSH: C++ (Computer program language) | Computer programming. Classification: LCC QA76.73.C153 (ebook) | LCC QA76.73.C153 L67 2019 (print) | DDC 005.13/3--dc23 LC record available at https://lccn.loc.gov/2019020529

No Starch Press and the No Starch Press logo are registered trademarks of No Starch Press, Inc. Other product and company names mentioned herein may be the trademarks of their respective owners. Rather than use a trademark symbol with every occurrence of a trademarked name, we are using the names only in an editorial fashion and to the benefit of the trademark owner, with no intention of infringement of the trademark. The information in this book is distributed on an “As Is” basis, without warranty. While every precaution has been taken in the preparation of this work, neither the author nor No Starch Press, Inc. shall have any liability to any person or entity with respect to any loss or damage caused or alleged to be caused directly or indirectly by the information contained in it.

#include #include #include int main() { auto i{ 0x01B99644 }; std::string x{ " DFaeeillnor" }; while (i--) std::next_permutation(x.begin(), x.end()); std::cout is_sentient = true; } void make_sentient(HolmesIV& mike) { mike.is_sentient = true; } Listing 5: A program illustrating the use of the dot and arrow operators

xl   An Overture to C Programmers

Under the hood, references are equivalent to pointers because they’re also a zero-overhead abstraction. The compiler produces similar code. To illustrate this, consider the results of compiling the make_sentient functions on GCC 8.3 targeting x86-64 with -O2. Listing 6 contains the assembly generated by compiling Listing 5. make_sentient(HolmesIV*): mov BYTE PTR [rdi], 1 ret make_sentient(HolmesIV&): mov BYTE PTR [rdi], 1 ret Listing 6: The assembly generated from compiling Listing 5

However, at compile time, references provide some safety over raw pointers because, generally speaking, they cannot be null. With pointers, you might add a nullptr check to be safe. For example, you might add a check to make_sentient, as in Listing 7. void make_sentient(HolmesIV* mike) { if(mike == nullptr) return; mike->is_sentient = true; } Listing 7: A refactor of make_sentient from Listing 5 so it performs a nullptr check

Such a check is unnecessary when taking a reference; however, this doesn’t mean that references are always valid. Consider the following function: HolmesIV& not_dinkum() { HolmesIV mike; return mike; }

The not_dinkum function returns a reference, which is guaranteed to be non-null. But it’s pointing to garbage memory (probably in the returnedfrom stack frame of not_dinkum). You must never do this. The result will be utter misery, also known as undefined runtime behavior: it might crash, it might give you an error, or it might do something completely unexpected. One other safety feature of references is that they can’t be reseated. In other words, once a reference is initialized, it can’t be changed to point to another memory address, as Listing 8 shows. int main() { int a = 42; int& a_ref = a; u int b = 100; a_ref = b; v } Listing 8: A program illustrating that references cannot be reseated An Overture to C Programmers   xli

You declare a_ref as a reference to int a u. There is no way to reseat a_ref to point to another int. You might try to reseat a with operator= v, but this actually sets the value of a to the value of b instead of setting a_ref to reference b. After the snippet is run both a and b are equal to 100, and a_ref still points to a. Listing 9 contains equivalent code using pointers instead. int main() { int a = 42; int* a_ptr = &a; u int b = 100; *a_ptr = b; v } Listing 9: An equivalent program to Listing 8 using pointers

Here, you declare the pointer with a * instead of a & u. You assign the value of b to the memory pointed to by a_ptr v. With references, you don’t need any decoration on the left side of the equal sign. But if you omit the * in *a_ptr, the compiler would complain that you’re trying to assign an int to a pointer type. References are just pointers with extra safety precautions and a sprinkle of syntactic sugar. When you put a reference on the left side of an equal sign, you’re setting the pointed-to value equal to the right side of the equal sign.

auto Initialization C often requires you to repeat type information more than once. In C++, you can express a variable’s type information just once by utilizing the auto keyword. The compiler will know the variable’s type because it knows the type of the value being used to initialize the variable. Consider the following C++ variable initializations: int x = 42; auto y = 42;

Here, x and y are both of int type. You might be surprised to know that the compiler can deduce the type of y, but consider that 42 is an integer literal. With auto, the compiler deduces the type on the right side of the equal sign = and sets the variable’s type to the same. Because an integer literal is of int type, in this example the compiler deduces that the type of y is also an int. This doesn’t seem like much of a benefit in such a simple example, but consider initializing a variable with a function’s return value, as Listing 10 illustrates. #include struct HolmesIV { --snip-};

xlii   An Overture to C Programmers

HolmesIV* make_mike(int sense_of_humor) { --snip-} int main() { auto mike = make_mike(1000); free(mike); } Listing 10: A toy program initializing a variable with the return value of a function

The auto keyword is easier to read and is more amenable to code refactoring than explicitly declaring a variable’s type. If you use auto freely while declaring a function, there will be less work to do later if you need to change the return type of make_mike. The case for auto strengthens with more complex types, such as those involved with the template-laden code of the stdlib. The auto keyword makes the compiler do all the work of type deduction for you. NOTE

You can also add const, volatile, &, and * qualifiers to auto.

Namespaces and Implicit typedef of struct, union, and enum C++ treats type tags as implicit typedef names. In C, when you want to use a struct, union, or enum, you have to assign a name to the type you’ve created using the typedef keyword. For example: typedef struct Jabberwocks { void* tulgey_wood; int is_galumphing; } Jabberwock;

In C++ land, you chortle at such code. Because the typedef keyword can be implicit, C++ allows you instead to declare the Jabberwock type like this: struct Jabberwock { void* tulgey_wood; int is_galumphing; };

This is more convenient and saves some typing. What happens if you also want to define a Jabberwock function? Well, you shouldn’t, because reusing the same name for a data type and a function is likely to cause confusion. But if you’re really committed to it, C++ allows you to declare a namespace to create different scopes for identifiers. This helps to keep user types and functions tidy, as shown in Listing 11. #include namespace Creature { u struct Jabberwock { void* tulgey_wood; int is_galumphing; An Overture to C Programmers   xliii

}; } namespace Func { v void Jabberwock() { printf("Burble!"); } } Listing 11: Using namespaces to disambiguate functions and types with identical names

In this example, Jabberwock the struct and Jabberwock the function now live together in frabjous harmony. By placing each element in its own namespace— the struct in the Creature namespace u and the function in the Func namespace v–you can disambiguate which Jabberwock you mean. You can do such disambiguation in several ways. The simplest is to qualify the name with its namespace, for example: Creature::Jabberwock x; Func::Jabberwock();

You can also employ a using directive to import all the names in a namespace, so you’d no longer need to use the fully qualified element name. Listing 12 uses the Creature namespace. #include namespace Creature { struct Jabberwock { void* tulgey_wood; int is_galumphing; }; } namespace Func { void Jabberwock() { printf("Burble!"); } } using namespace Creature; u int main() { Jabberwock x; v Func::Jabberwock(); } Listing 12: Employing using namespace to refer to a type within the Creature namespace

The using namespace u enables you to omit the namespace qualification v. But you still need a qualifier on Func::Jabberwock, because it isn’t part of the Creature namespace.

xliv   An Overture to C Programmers

Use of a namespace is idiomatic C++ and is a zero-overhead abstraction. Just like the rest of a type’s identifiers, the namespace is erased by the compiler when emitting assembly code. In large projects, it’s incredibly helpful for separating code in different libraries.

Intermingling C and C++ Object Files C and C++ code can coexist peacefully if you’re careful. Sometimes, it’s necessary for a C compiler to link object files emitted by a C++ compiler (and vice versa). Although this is possible, it requires a bit of work. Two issues are related to linking the files. First, the calling conventions in the C and C++ code could potentially be mismatched. For example, the protocols for how the stack and registers are set when you call a function could be different. These calling conventions are language-level mismatches and aren’t generally related to how you’ve written your functions. Second, C++ compilers emit different symbols than C compilers do. Sometimes the linker must identify an object by name. C++ compilers assist by decorating the object, associating a string called a decorated name with the object. Because of function overloads, calling conventions, and namespace usage, the compiler must encode additional information about a function beyond just its name through decoration. This is done to ensure that the linker can uniquely identify the function. Unfortunately, there is no standard for how this decoration occurs in C++ (which is why you should use the same tool chain and settings when linking between translation units). C linkers know nothing about C++ name decoration, which can cause problems if decoration isn’t suppressed whenever you link against C code within C++ (and vice versa). The fix is simple. You wrap the code you want to compile with C-style linkages using the statement extern "C", as in Listing 13. // header.h #ifdef __cplusplus extern "C" { #endif void extract_arkenstone(); struct MistyMountains { int goblin_count; }; #ifdef __cplusplus } #endif Listing 13: Employing C-style linkage

This header can be shared between C and C++ code. It works because __cplusplus is a special identifier that the C++ compiler defines (but the C

compiler doesn’t). Accordingly, the C compiler sees the code in Listing 14 after preprocessing completes. Listing 14 illustrates the code that remains.

An Overture to C Programmers   xlv

void extract_arkenstone(); struct MistyMountains { int goblin_count; }; Listing 14: The code remaining after the preprocessor processes Listing 13 in a C environment

This is just a simple C header. The code between the #ifdef __cplusplus statements is removed during preprocessing, so the extern "C" wrapper isn’t visible. For the C++ compiler, __cplusplus is defined in header.h, so it sees the contents of Listing 15. extern "C" { void extract_arkenstone(); struct MistyMountains { int goblin_count; }; } Listing 15: The code remaining after the preprocessor processes Listing 13 in a C++ environment

Both extract_arkenstone and MistyMountains are now wrapped with extern "C", so the compiler knows to use C linkage. Now your C source can call into compiled C++ code, and your C++ source can call into compiled C code.

C++ Themes This section takes you on a brief tour of some core themes that make C++ the premier system-programming language. Don’t worry too much about the details. The point of the following subsections is to whet your appetite.

Expressing Ideas Concisely and Reusing Code Well-crafted C++ code has an elegant, compact quality. Consider the evolution from ANSI-C to modern C++ in the following simple operation: looping over some array v with n elements, as Listing 16 illustrates. #include int main() { const size_t n{ 100 }; int v[n]; // ANSI-C size_t i; for (i=0; i