- •Contents
- •Preface
- •An Experiment
- •Acknowledgments
- •Versions of this Book
- •About This Book
- •What You Should Know Before Reading This Book
- •Overall Structure of the Book
- •The Way I Implement
- •Initializations
- •Error Terminology
- •The C++ Standards
- •Example Code and Additional Information
- •1 Comparisons and Operator <=>
- •1.1.1 Defining Comparison Operators Before C++20
- •1.1.2 Defining Comparison Operators Since C++20
- •1.2 Defining and Using Comparisons
- •1.2.2 Comparison Category Types
- •1.2.4 Calling Operator <=> Directly
- •1.2.5 Dealing with Multiple Ordering Criteria
- •1.3 Defining operator<=> and operator==
- •1.3.1 Defaulted operator== and operator<=>
- •1.3.2 Defaulted operator<=> Implies Defaulted operator==
- •1.3.3 Implementation of the Defaulted operator<=>
- •1.4 Overload Resolution with Rewritten Expressions
- •1.5 Using Operator <=> in Generic Code
- •1.5.1 compare_three_way
- •1.6 Compatibility Issues with the Comparison Operators
- •1.6.2 Inheritance with Protected Members
- •1.7 Afternotes
- •2 Placeholder Types for Function Parameters
- •2.1.1 auto for Parameters of Member Functions
- •2.2 Using auto for Parameters in Practice
- •2.3.1 Basic Constraints for auto Parameters
- •2.4 Afternotes
- •3.1 Motivating Example of Concepts and Requirements
- •3.1.1 Improving the Template Step by Step
- •3.1.2 A Complete Example with Concepts
- •3.2 Where Constraints and Concepts Can Be Used
- •3.2.1 Constraining Alias Templates
- •3.2.2 Constraining Variable Templates
- •3.2.3 Constraining Member Functions
- •3.3 Typical Applications of Concepts and Constraints in Practice
- •3.3.1 Using Concepts to Understand Code and Error Messages
- •3.3.2 Using Concepts to Disable Generic Code
- •3.3.3 Using Requirements to Call Different Functions
- •3.3.4 The Example as a Whole
- •3.3.5 Former Workarounds
- •3.4 Semantic Constraints
- •3.4.1 Examples of Semantic Constraints
- •3.5 Design Guidelines for Concepts
- •3.5.1 Concepts Should Group Requirements
- •3.5.2 Define Concepts with Care
- •3.5.3 Concepts versus Type Traits and Boolean Expressions
- •3.6 Afternotes
- •4.1 Constraints
- •4.2.1 Using && and || in requires Clauses
- •4.3 Ad-hoc Boolean Expressions
- •4.4.1 Simple Requirements
- •4.4.2 Type Requirements
- •4.4.3 Compound Requirements
- •4.4.4 Nested Requirements
- •4.5 Concepts in Detail
- •4.5.1 Defining Concepts
- •4.5.2 Special Abilities of Concepts
- •4.5.3 Concepts for Non-Type Template Parameters
- •4.6 Using Concepts as Type Constraints
- •4.7 Subsuming Constraints with Concepts
- •4.7.1 Indirect Subsumptions
- •4.7.2 Defining Commutative Concepts
- •5 Standard Concepts in Detail
- •5.1 Overview of All Standard Concepts
- •5.1.1 Header Files and Namespaces
- •5.1.2 Standard Concepts Subsume
- •5.2 Language-Related Concepts
- •5.2.1 Arithmetic Concepts
- •5.2.2 Object Concepts
- •5.2.3 Concepts for Relationships between Types
- •5.2.4 Comparison Concepts
- •5.3 Concepts for Iterators and Ranges
- •5.3.1 Concepts for Ranges and Views
- •5.3.2 Concepts for Pointer-Like Objects
- •5.3.3 Concepts for Iterators
- •5.3.4 Iterator Concepts for Algorithms
- •5.4 Concepts for Callables
- •5.4.1 Basic Concepts for Callables
- •5.4.2 Concepts for Callables Used by Iterators
- •5.5 Auxiliary Concepts
- •5.5.1 Concepts for Specific Type Attributes
- •5.5.2 Concepts for Incrementable Types
- •6 Ranges and Views
- •6.1 A Tour of Ranges and Views Using Examples
- •6.1.1 Passing Containers to Algorithms as Ranges
- •6.1.2 Constraints and Utilities for Ranges
- •6.1.3 Views
- •6.1.4 Sentinels
- •6.1.5 Range Definitions with Sentinels and Counts
- •6.1.6 Projections
- •6.1.7 Utilities for Implementing Code for Ranges
- •6.1.8 Limitations and Drawbacks of Ranges
- •6.2 Borrowed Iterators and Ranges
- •6.2.1 Borrowed Iterators
- •6.2.2 Borrowed Ranges
- •6.3 Using Views
- •6.3.1 Views on Ranges
- •6.3.2 Lazy Evaluation
- •6.3.3 Caching in Views
- •6.3.4 Performance Issues with Filters
- •6.4 Views on Ranges That Are Destroyed or Modified
- •6.4.1 Lifetime Dependencies Between Views and Their Ranges
- •6.4.2 Views with Write Access
- •6.4.3 Views on Ranges That Change
- •6.4.4 Copying Views Might Change Behavior
- •6.5 Views and const
- •6.5.1 Generic Code for Both Containers and Views
- •6.5.3 Bringing Back Deep Constness to Views
- •6.6 Summary of All Container Idioms Broken By Views
- •6.7 Afternotes
- •7 Utilities for Ranges and Views
- •7.1 Key Utilities for Using Ranges as Views
- •7.1.1 std::views::all()
- •7.1.2 std::views::counted()
- •7.1.3 std::views::common()
- •7.2 New Iterator Categories
- •7.3 New Iterator and Sentinel Types
- •7.3.1 std::counted_iterator
- •7.3.2 std::common_iterator
- •7.3.3 std::default_sentinel
- •7.3.4 std::unreachable_sentinel
- •7.3.5 std::move_sentinel
- •7.4 New Functions for Dealing with Ranges
- •7.4.1 Functions for Dealing with the Elements of Ranges (and Arrays)
- •7.4.2 Functions for Dealing with Iterators
- •7.4.3 Functions for Swapping and Moving Elements/Values
- •7.4.4 Functions for Comparisons of Values
- •7.5 New Type Functions/Utilities for Dealing with Ranges
- •7.5.1 Generic Types of Ranges
- •7.5.2 Generic Types of Iterators
- •7.5.3 New Functional Types
- •7.5.4 Other New Types for Dealing with Iterators
- •7.6 Range Algorithms
- •7.6.1 Benefits and Restrictions for Range Algorithms
- •7.6.2 Algorithm Overview
- •8 View Types in Detail
- •8.1 Overview of All Views
- •8.1.1 Overview of Wrapping and Generating Views
- •8.1.2 Overview of Adapting Views
- •8.2 Base Class and Namespace of Views
- •8.2.1 Base Class for Views
- •8.2.2 Why Range Adaptors/Factories Have Their Own Namespace
- •8.3 Source Views to External Elements
- •8.3.1 Subrange
- •8.3.2 Ref View
- •8.3.3 Owning View
- •8.3.4 Common View
- •8.4 Generating Views
- •8.4.1 Iota View
- •8.4.2 Single View
- •8.4.3 Empty View
- •8.4.4 IStream View
- •8.4.5 String View
- •8.4.6 Span
- •8.5 Filtering Views
- •8.5.1 Take View
- •8.5.2 Take-While View
- •8.5.3 Drop View
- •8.5.4 Drop-While View
- •8.5.5 Filter View
- •8.6 Transforming Views
- •8.6.1 Transform View
- •8.6.2 Elements View
- •8.6.3 Keys and Values View
- •8.7 Mutating Views
- •8.7.1 Reverse View
- •8.8 Views for Multiple Ranges
- •8.8.1 Split and Lazy-Split View
- •8.8.2 Join View
- •9 Spans
- •9.1 Using Spans
- •9.1.1 Fixed and Dynamic Extent
- •9.1.2 Example Using a Span with a Dynamic Extent
- •9.1.4 Example Using a Span with Fixed Extent
- •9.1.5 Fixed vs. Dynamic Extent
- •9.2 Spans Considered Harmful
- •9.3 Design Aspects of Spans
- •9.3.1 Lifetime Dependencies of Spans
- •9.3.2 Performance of Spans
- •9.3.3 const Correctness of Spans
- •9.3.4 Using Spans as Parameters in Generic Code
- •9.4 Span Operations
- •9.4.1 Span Operations and Member Types Overview
- •9.4.2 Constructors
- •9.5 Afternotes
- •10 Formatted Output
- •10.1 Formatted Output by Example
- •10.1.1 Using std::format()
- •10.1.2 Using std::format_to_n()
- •10.1.3 Using std::format_to()
- •10.1.4 Using std::formatted_size()
- •10.2 Performance of the Formatting Library
- •10.2.1 Using std::vformat() and vformat_to()
- •10.3 Formatted Output in Detail
- •10.3.1 General Format of Format Strings
- •10.3.2 Standard Format Specifiers
- •10.3.3 Width, Precision, and Fill Characters
- •10.3.4 Format/Type Specifiers
- •10.4 Internationalization
- •10.5 Error Handling
- •10.6 User-Defined Formatted Output
- •10.6.1 Basic Formatter API
- •10.6.2 Improved Parsing
- •10.6.3 Using Standard Formatters for User-Defined Formatters
- •10.6.4 Using Standard Formatters for Strings
- •10.7 Afternotes
- •11 Dates and Timezones for <chrono>
- •11.1 Overview by Example
- •11.1.1 Scheduling a Meeting on the 5th of Every Month
- •11.1.2 Scheduling a Meeting on the Last Day of Every Month
- •11.1.3 Scheduling a Meeting Every First Monday
- •11.1.4 Using Different Timezones
- •11.2 Basic Chrono Concepts and Terminology
- •11.3 Basic Chrono Extensions with C++20
- •11.3.1 Duration Types
- •11.3.2 Clocks
- •11.3.3 Timepoint Types
- •11.3.4 Calendrical Types
- •11.3.5 Time Type hh_mm_ss
- •11.3.6 Hours Utilities
- •11.4 I/O with Chrono Types
- •11.4.1 Default Output Formats
- •11.4.2 Formatted Output
- •11.4.3 Locale-Dependent Output
- •11.4.4 Formatted Input
- •11.5 Using the Chrono Extensions in Practice
- •11.5.1 Invalid Dates
- •11.5.2 Dealing with months and years
- •11.5.3 Parsing Timepoints and Durations
- •11.6 Timezones
- •11.6.1 Characteristics of Timezones
- •11.6.2 The IANA Timezone Database
- •11.6.3 Using Timezones
- •11.6.4 Dealing with Timezone Abbreviations
- •11.6.5 Custom Timezones
- •11.7 Clocks in Detail
- •11.7.1 Clocks with a Specified Epoch
- •11.7.2 The Pseudo Clock local_t
- •11.7.3 Dealing with Leap Seconds
- •11.7.4 Conversions between Clocks
- •11.7.5 Dealing with the File Clock
- •11.8 Other New Chrono Features
- •11.9 Afternotes
- •12 std::jthread and Stop Tokens
- •12.1 Motivation for std::jthread
- •12.1.1 The Problem of std::thread
- •12.1.2 Using std::jthread
- •12.1.3 Stop Tokens and Stop Callbacks
- •12.1.4 Stop Tokens and Condition Variables
- •12.2 Stop Sources and Stop Tokens
- •12.2.1 Stop Sources and Stop Tokens in Detail
- •12.2.2 Using Stop Callbacks
- •12.2.3 Constraints and Guarantees of Stop Tokens
- •12.3.1 Using Stop Tokens with std::jthread
- •12.4 Afternotes
- •13 Concurrency Features
- •13.1 Thread Synchronization with Latches and Barriers
- •13.1.1 Latches
- •13.1.2 Barriers
- •13.2 Semaphores
- •13.2.1 Example of Using Counting Semaphores
- •13.2.2 Example of Using Binary Semaphores
- •13.3 Extensions for Atomic Types
- •13.3.1 Atomic References with std::atomic_ref<>
- •13.3.2 Atomic Shared Pointers
- •13.3.3 Atomic Floating-Point Types
- •13.3.4 Thread Synchronization with Atomic Types
- •13.3.5 Extensions for std::atomic_flag
- •13.4 Synchronized Output Streams
- •13.4.1 Motivation for Synchronized Output Streams
- •13.4.2 Using Synchronized Output Streams
- •13.4.3 Using Synchronized Output Streams for Files
- •13.4.4 Using Synchronized Output Streams as Output Streams
- •13.4.5 Synchronized Output Streams in Practice
- •13.5 Afternotes
- •14 Coroutines
- •14.1 What Are Coroutines?
- •14.2 A First Coroutine Example
- •14.2.1 Defining the Coroutine
- •14.2.2 Using the Coroutine
- •14.2.3 Lifetime Issues with Call-by-Reference
- •14.2.4 Coroutines Calling Coroutines
- •14.2.5 Implementing the Coroutine Interface
- •14.2.6 Bootstrapping Interface, Handle, and Promise
- •14.2.7 Memory Management
- •14.3 Coroutines That Yield or Return Values
- •14.3.1 Using co_yield
- •14.3.2 Using co_return
- •14.4 Coroutine Awaitables and Awaiters
- •14.4.1 Awaiters
- •14.4.2 Standard Awaiters
- •14.4.3 Resuming Sub-Coroutines
- •14.4.4 Passing Values From Suspension Back to the Coroutine
- •14.5 Afternotes
- •15 Coroutines in Detail
- •15.1 Coroutine Constraints
- •15.1.1 Coroutine Lambdas
- •15.2 The Coroutine Frame and the Promises
- •15.2.1 How Coroutine Interfaces, Promises, and Awaitables Interact
- •15.3 Coroutine Promises in Detail
- •15.3.1 Mandatory Promise Operations
- •15.3.2 Promise Operations to Return or Yield Values
- •15.3.3 Optional Promise Operations
- •15.4 Coroutine Handles in Detail
- •15.4.1 std::coroutine_handle<void>
- •15.5 Exceptions in Coroutines
- •15.6 Allocating Memory for the Coroutine Frame
- •15.6.1 How Coroutines Allocate Memory
- •15.6.2 Avoiding Heap Memory Allocation
- •15.6.3 get_return_object_on_allocation_failure()
- •15.7 co_await and Awaiters in Detail
- •15.7.1 Details of the Awaiter Interface
- •15.7.2 Letting co_await Update Running Coroutines
- •15.7.3 Symmetric Transfer with Awaiters for Continuation
- •15.8.1 await_transform()
- •15.8.2 operator co_await()
- •15.9 Concurrent Use of Coroutines
- •15.9.1 co_await Coroutines
- •15.9.2 A Thread Pool for Coroutine Tasks
- •15.9.3 What C++ Libraries Will Provide After C++20
- •15.10 Coroutine Traits
- •16 Modules
- •16.1 Motivation for Modules Using a First Example
- •16.1.1 Implementing and Exporting a Module
- •16.1.2 Compiling Module Units
- •16.1.3 Importing and Using a Module
- •16.1.4 Reachable versus Visible
- •16.1.5 Modules and Namespaces
- •16.2 Modules with Multiple Files
- •16.2.1 Module Units
- •16.2.2 Using Implementation Units
- •16.2.3 Internal Partitions
- •16.2.4 Interface Partitions
- •16.2.5 Summary of Splitting Modules into Different Files
- •16.3 Dealing with Modules in Practice
- •16.3.1 Dealing with Module Files with Different Compilers
- •16.3.2 Dealing with Header Files
- •16.4 Modules in Detail
- •16.4.1 Private Module Fragments
- •16.4.2 Module Declaration and Export in Detail
- •16.4.3 Umbrella Modules
- •16.4.4 Module Import in Detail
- •16.4.5 Reachable versus Visible Symbols in Detail
- •16.5 Afternotes
- •17 Lambda Extensions
- •17.1 Generic Lambdas with Template Parameters
- •17.1.1 Using Template Parameters for Generic Lambdas in Practice
- •17.1.2 Explicit Specification of Lambda Template Parameters
- •17.2 Calling the Default Constructor of Lambdas
- •17.4 consteval Lambdas
- •17.5 Changes for Capturing
- •17.5.1 Capturing this and *this
- •17.5.2 Capturing Structured Bindings
- •17.5.3 Capturing Parameter Packs of Variadic Templates
- •17.5.4 Lambdas as Coroutines
- •17.6 Afternotes
- •18 Compile-Time Computing
- •18.1 Keyword constinit
- •18.1.1 Using constinit in Practice
- •18.1.2 How constinit Solves the Static Initialization Order Fiasco
- •18.2.1 A First consteval Example
- •18.2.2 constexpr versus consteval
- •18.2.3 Using consteval in Practice
- •18.2.4 Compile-Time Value versus Compile-Time Context
- •18.4 std::is_constant_evaluated()
- •18.4.1 std::is_constant_evaluated() in Detail
- •18.5 Using Heap Memory, Vectors, and Strings at Compile Time
- •18.5.1 Using Vectors at Compile Time
- •18.5.2 Returning a Collection at Compile Time
- •18.5.3 Using Strings at Compile Time
- •18.6.1 constexpr Language Extensions
- •18.6.2 constexpr Library Extensions
- •18.7 Afternotes
- •19.1 New Types for Non-Type Template Parameters
- •19.1.1 Floating-Point Values as Non-Type Template Parameters
- •19.1.2 Objects as Non-Type Template Parameters
- •19.2 Afternotes
- •20 New Type Traits
- •20.1 New Type Traits for Type Classification
- •20.1.1 is_bounded_array_v<> and is_unbounded_array_v
- •20.2 New Type Traits for Type Inspection
- •20.2.1 is_nothrow_convertible_v<>
- •20.3 New Type Traits for Type Conversion
- •20.3.1 remove_cvref_t<>
- •20.3.2 unwrap_reference<> and unwrap_ref_decay_t
- •20.3.3 common_reference<>_t
- •20.3.4 type_identity_t<>
- •20.4 New Type Traits for Iterators
- •20.4.1 iter_difference_t<>
- •20.4.2 iter_value_t<>
- •20.4.3 iter_reference_t<> and iter_rvalue_reference_t<>
- •20.5 Type Traits and Functions for Layout Compatibility
- •20.5.1 is_layout_compatible_v<>
- •20.5.2 is_pointer_interconvertible_base_of_v<>
- •20.5.3 is_corresponding_member()
- •20.5.4 is_pointer_interconvertible_with_class()
- •20.6 Afternotes
- •21 Small Improvements for the Core Language
- •21.1 Range-Based for Loop with Initialization
- •21.2 using for Enumeration Values
- •21.3 Delegating Enumeration Types to Different Scopes
- •21.4 New Character Type char8_t
- •21.4.2 Broken Backward Compatibility
- •21.5 Improvements for Aggregates
- •21.5.1 Designated Initializers
- •21.5.2 Aggregate Initialization with Parentheses
- •21.5.3 Definition of Aggregates
- •21.6 New Attributes and Attribute Features
- •21.6.1 Attributes [[likely]] and [[unlikely]]
- •21.6.2 Attribute [[no_unique_address]]
- •21.6.3 Attribute [[nodiscard]] with Parameter
- •21.7 Feature Test Macros
- •21.8 Afternotes
- •22 Small Improvements for Generic Programming
- •22.1 Implicit typename for Type Members of Template Parameters
- •22.1.1 Rules for Implicit typename in Detail
- •22.2 Improvements for Aggregates in Generic Code
- •22.2.1 Class Template Argument Deduction (CTAD) for Aggregates
- •22.3.1 Conditional explicit in the Standard Library
- •22.4 Afternotes
- •23 Small Improvements for the C++ Standard Library
- •23.1 Updates for String Types
- •23.1.1 String Members starts_with() and ends_with()
- •23.1.2 Restricted String Member reserve()
- •23.2 std::source_location
- •23.3 Safe Comparisons of Integral Values and Sizes
- •23.3.1 Safe Comparisons of Integral Values
- •23.3.2 ssize()
- •23.4 Mathematical Constants
- •23.5 Utilities for Dealing with Bits
- •23.5.1 Bit Operations
- •23.5.2 std::bit_cast<>()
- •23.5.3 std::endian
- •23.6 <version>
- •23.7 Extensions for Algorithms
- •23.7.1 Range Support
- •23.7.2 New Algorithms
- •23.7.3 unseq Execution Policy for Algorithms
- •23.8 Afternotes
- •24 Deprecated and Removed Features
- •24.1 Deprecated and Removed Core Language Features
- •24.2 Deprecated and Removed Library Features
- •24.2.1 Deprecated Library Features
- •24.2.2 Removed Library Features
- •24.3 Afternotes
- •Glossary
- •Index
11.4 I/O with Chrono Types |
371 |
•Different week number and year formats might result in different output values. For example, Sunday, January 1, 2023 yields:
–Week 00 with %W (week before first Monday)
–Week 01 with %U (week with first Monday)
–Week 52 with %V (ISO week: week before Monday of week 01, which has January 4th)
Because the ISO week might be the last week of the previous year, the ISO year, which is the year of that week, may be one less:
–Year 2023 with %Y
–Year 23 with %y
–Year 2022 with %G (ISO year of the ISO week %V, which is the last week of the previous month)
–Year 22 with %g (ISO year of the ISO week %V, which is the last week of the previous month)
•The following timezone abbreviations are used for the standard clocks:
–UTC for sys_clock, utc_clock, and file_clock
–TAI for tai_clock
–GPS for gps_clock
All conversion specifiers except %q and %Q can also be used for formatted parsing.
11.4.3 Locale-Dependent Output
The default output operator for the various types uses a locale-dependent format if the output stream is imbued by a locale that has its own format. For example:
using namespace std::literals; auto dur = 42.2ms;
std::cout << dur << '\n'; // 42.2ms
#ifdef _MSC_VER
std::locale locG("deu_deu.1252");
#else |
|
std::locale locG("de_DE"); |
|
#endif |
|
std::cout.imbue(locG); |
// switch to German locale |
std::cout << dur << '\n'; |
// 42,2ms |
Formatted output with std::format() is handled as usual:4
•By default, formatted output uses the locale-independent "C" locale.
•By specifying L, you can switch to a locale-dependent output specified via a locale parameter or as a global locale.
4This behavior was specified as a bug fix to C++20 with http://wg21.link/p2372, which means that the original wording of C++20 does not specify this behavior.
372 |
Chapter 11: Dates and Timezones for <chrono> |
This means that to use a locale-dependent notation, you have to use the L specifier and either pass the locale as the first argument to std::format() or set the global locale before calling it. For example:
using namespace std::literals; |
|
auto dur = 42.2ms; |
// duration to print |
#ifdef _MSC_VER |
|
std::locale locG("deu_deu.1252"); |
|
#else |
|
std::locale locG("de_DE"); |
|
#endif |
|
std::string s1 = std::format("{:%S}", dur); |
// "00.042s" (not localized) |
std::string s3 = std::format(locG, "{:%S}", dur); |
// "00.042s" (not localized) |
std::string s2 = std::format(locG, "{:L%S}", dur); |
// "00,042s" (localized) |
std::locale::global(locG); |
// set German locale globally |
std::string s4 = std::format("{:L%S}", dur); |
// "00,042s" (localized) |
In several cases, you can even use an alternative locale’s representation according to strftime() and ISO 8601:2004, which you can specify with a leading O or E in front of the conversion specifier:
•E can be used as the locale’s alternate representations in front of c, C, x, X, y, Y, and z
•O can be used as the locale’s alternate numeric symbols in front of d, e, H, I, m, M, S, u, U V, w, W, y, and z
11.4.4 Formatted Input
The chrono library also supports formatted input. You have two options:
•A free-standing function std::chrono::from_stream() is provided by certain date/time types to read in a specific value according to a passed format string.
•A manipulator std::chrono::parse() allows us to use from_stream() as part of a bigger parsing with the input operator >>.
Using from_stream()
The following code demonstrates how to use from_stream() by parsing a full timepoint:
std::chrono::sys_seconds tp; std::istringstream sstrm{"2021-2-28 17:30:00"};
std::chrono::from_stream(sstrm, "%F %T", tp); if (sstrm) {
std::cout << "tp: " << tp << '\n';
}
else {
std::cerr << "reading into tp failed\n";
}
11.4 I/O with Chrono Types |
373 |
The code generates the following output:
tp: 2021-02-28 17:30:00
As another example, you can parse a year_month from a sequence of characters specifying the full month name and the year, among other things, as follows:
std::chrono::year_month m;
std::istringstream sstrm{"Monday, April 5, 2021"}; std::chrono::from_stream(sstrm, "%A, %B %d, %Y", m); if (sstrm) {
std::cout << "month: " << m << '\n'; // prints: month: 2021/Apr
}
The format string accepts all conversion specifiers of formatted output except %q and %Q with improved flexibility. For example:
•%d stands for one or two characters to specify the day and with %4d, you can specify that even up to four characters are parsed.
•%n stands for exactly one whitespace character.
•%t stands for zero or one whitespace character.
•A whitespace character such as a space represents an arbitrary number of whitespaces (including zero whitespaces).
from_stream() is provided for the following types:
•A duration<> of any type
•A sys_time<>, utc_time<>, gps_time<>, tai_time<>, local_time<>, or file_time<> of any duration
•A day, a month, or a year
•A year_month, a month_day, or a year_month_day
•A weekday
The format has to be a C string of type const char*. It must match the characters in the input stream and the value to parse. The parsing fails if:
•The input sequence of characters does not match the required format
•The format does not provide enough information for the value
•The parsed date is not valid
In that case, the failbit of the stream is set, which you can test with by calling fail() or using the stream as a Boolean value.
A Generic Function for Parsing Dates/Times
In practice, dates and times are rarely hard-coded. However, when testing code, you often need an easy way to specify a date/time value.
374 |
Chapter 11: Dates and Timezones for <chrono> |
Here is a little helper function I used to test the examples in this book: lib/chronoparse.hpp
#include <chrono> #include <string> #include <sstream> #include <cassert>
//parse year-month-day with optional hour:minute and optional :sec
//- returns a time_point<> of the passed clock (default: system_clock)
//in seconds
template<typename Clock = std::chrono::system_clock> auto parseDateTime(const std::string& s)
{
// return value:
std::chrono::time_point<Clock, std::chrono::seconds> tp;
// string stream to read from:
std::istringstream sstrm{s}; // no string_view support
auto posColon = s.find(":");
if (posColon != std::string::npos) { if (posColon != s.rfind(":")) {
// multiple colons: std::chrono::from_stream(sstrm, "%F %T", tp);
}
else {
// one colon:
std::chrono::from_stream(sstrm, "%F %R", tp);
}
}
else {
// no colon:
std::chrono::from_stream(sstrm, "%F", tp);
}
// handle invalid formats: assert((!sstrm.fail()));
} return tp;
11.4 I/O with Chrono Types |
375 |
You can use parseDateTime() as follows: lib/chronoparse.cpp
#include "chronoparse.hpp" #include <iostream>
int main()
{
auto tp1 = parseDateTime("2021-1-1"); std::cout << std::format("{:%F %T %Z}\n", tp1);
auto tp2 = parseDateTime<std::chrono::local_t>("2021-1-1"); std::cout << std::format("{:%F %T}\n", tp2);
auto tp3 = parseDateTime<std::chrono::utc_clock>("2015-6-30 23:59:60"); std::cout << std::format("{:%F %T %Z}\n", tp3);
auto tp4 = parseDateTime<std::chrono::gps_clock>("2021-1-1 18:30");} std::cout << std::format("{:%F %T %Z}\n", tp4);
The program has the following output:
2021-01-01 00:00:00 UTC
2021-01-01 00:00:00
2015-06-30 23:59:60 UTC
2021-01-01 18:30:00 GPS
Note that for a local timepoint, you cannot use %Z to print its timezone (doing so would raise an exception).
Using the parse() Manipulator
Instead of calling from_stream()
std::chrono::from_stream(sstrm, "%F %T", tp);
you could also call:
sstrm >> std::chrono::parse("%F %T", tp);
Please note that the original C++20 standard does not formally allow you to pass the format directly as a string literal, which means that you have to call
sstrm >> std::chrono::parse(std::string{"%F %T"}, tp);
However, this should be fixed with http://wg21.link/lwg3554.
std::chrono::parse() is an I/O stream manipulator. It allows you to parse multiple values inside one statement reading from an input stream. In addition, thanks to move semantics, you can even pass a temporary input stream. For example:
376 |
Chapter 11: Dates and Timezones for <chrono> |
chr::sys_days tp; chr::hours h; chr::minutes m;
// parse date into tp, hour into h and minute into m: std::istringstream{"12/24/21 18:00"} >> chr::parse("%D", tp)
>>chr::parse(" %H", h)
>>chr::parse(":%M", m);
std::cout << tp << " at " << h << ' ' << m << '\n';
This code outputs:
2021-12-24 at 18h 0min
Again, note that you might have to explicitly convert the string literals "%D", " %H", and ":%M" to strings.
Parsing Timezones
Parsing timezones is a little tricky because timezone abbreviations are not unique: To help here, from_stream() has the following formats:
istream from_stream(istream, format, value)
istream from_stream(istream, format, value, abbrevPtr)
istream from_stream(istream, format, value, abbrevPtr, offsetPtr)
As you can see, you can optionally pass the address of a std::string to store a parsed timezone abbreviation into the string and the address of a std::chrono::minutes object to store a parsed timezone offset into that string. In both cases nullptr can be passed.
However, you still have to be careful:
• The following works:
chr::sys_seconds tp;
std::istringstream sstrm{"2021-4-13 12:00 UTC"}; chr::from_stream(sstrm, "%F %R %Z", tp);
std::cout << std::format("{:%F %R %Z}\n", tp); // 2021-04-13 12:00 UTC
However, it works only because system timepoints use UTC anyway.
• The following does not work because it ignores the timezone: chr::sys_seconds tp;
std::istringstream sstrm{"2021-4-13 12:00 MST"}; chr::from_stream(sstrm, "%F %R %Z", tp);
std::cout << std::format("{:%F %R %Z}", tp); // 2021-04-13 12:00 UTC %Z is used to parse MST but there is no parameter to store the value.
• The following seems to work: chr::sys_seconds tp; std::string tzAbbrev;
std::istringstream sstrm{"2021-4-13 12:00 MST"}; chr::from_stream(sstrm, "%F %R %Z", tp, &tzAbbrev);
11.4 I/O with Chrono |
Types |
377 |
std::cout << |
tp << '\n'; |
// 2021-04-13 12:00 |
std::cout << tzAbbrev << '\n'; // MST
However, if you compute the zoned time, you see that you are converting a UTC time to a different timezone:
chr::zoned_time zt{tzAbbrev, tp}; |
// OK: MST exists |
|
std::cout << zt << '\n'; |
// 2021-04-13 05:00:00 MST |
|
• The following really does seem to work: |
|
|
chr::local_seconds tp; |
// local time |
|
std::string tzAbbrev;
std::istringstream sstrm{"2021-4-13 12:00 MST"}; chr::from_stream(sstrm, "%F %R %Z", tp, &tzAbbrev);
std::cout << tp << '\n'; |
// 2021-04-13 |
12:00 |
|
std::cout << tzAbbrev << '\n'; // MST |
|
|
|
chr::zoned_time zt{tzAbbrev, tp}; |
// OK: MST exists |
||
std::cout << zt << '\n'; |
// 2021-04-13 |
12:00:00 MST |
However, we were lucky that MST is one of the few abbreviations available as a deprecated entry in the timezone database. The moment you use this code with CEST or CST, it throws an exception when initializing the zoned_time.
• Therefore, either use only tzAbbrev instead of zoned_time and %Z: chr::local_seconds tp; // local time
std::string tzAbbrev;
std::istringstream sstrm{"2021-4-13 12:00 CST"}; chr::from_stream(sstrm, "%F %R %Z", tp, &tzAbbrev);
std::cout << std::format("{:%F %R} {}", tp, tzAbbrev); // 2021-04-13 12:00 CST or you have to deal with code to map a timezone abbreviation to a timezone.
Note that %Z cannot parse the pseudo timezones GPS and TAI.