From 3919ae3042918fbba32224f4115da9237d7de116 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 30 Jul 2022 13:52:36 +0200 Subject: [PATCH 01/44] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 90556a4..7c22451 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.1.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.1.1/book_with_cover_v0.1.1.pdf) +[Latest PDF release (v0.2.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.1.1/book_with_cover_v0.1.1.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) ## Changelog +- `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. - `0.1.0` First pre-release From 92fe35b168a1e3aad19fc81018adf3bcfd0a59f1 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 12:41:39 +0200 Subject: [PATCH 02/44] Switch to docker devcontainer. --- .devcontainer/Dockerfile | 8 ++++++++ .devcontainer/devcontainer.json | 8 ++++++++ .vscode/extensions.json | 5 +++++ .vscode/settings.json | 24 ++++++++++++++++++++++++ build/.keep | 1 + code_examples/CMakeLists.txt | 8 ++++---- code_examples/verify.sh | 2 +- packages/code_blocks.tex | 2 +- 8 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 build/.keep diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..ac70e49 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,8 @@ +#FROM ubuntu:22.04 +FROM texlive/texlive:latest + +ARG DEBIAN_FRONTEND=noninteractive + +RUN set -ex && \ + apt-get update && \ + apt-get install -y cmake gcc g++ \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..f2f73c5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,8 @@ +{ + "build": { + "dockerfile": "Dockerfile" + }, + "extensions": [ + "James-Yu.latex-workshop" + ] +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..9c7c13a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode-remote.remote-containers" + ], +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e58fcfb --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "latex-workshop.latex.tools": [ + { + "name": "LuaLaTex", + "command": "lualatex", + "args": [ + "-shell-escape", + "-output-directory=%OUTDIR%", + "%DOC%" + ] + } + ], + "latex-workshop.latex.recipes": [ + { + "name": "Build the book", + "tools": [ + "LuaLaTex" + ] + }, + ], + "latex-workshop.latex.recipe.default": "first", + "latex-workshop.latex.outDir": "build" + +} \ No newline at end of file diff --git a/build/.keep b/build/.keep new file mode 100644 index 0000000..e050474 --- /dev/null +++ b/build/.keep @@ -0,0 +1 @@ +# Dummy file to keep the folder in git. \ No newline at end of file diff --git a/code_examples/CMakeLists.txt b/code_examples/CMakeLists.txt index 2464471..8e37855 100644 --- a/code_examples/CMakeLists.txt +++ b/code_examples/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.5) -project(code_examples LANGUAGES CXX) +set(CMAKE_C_COMPILER "/usr/bin/gcc") +set(CMAKE_CXX_COMPILER "/usr/bin/g++") -set(CMAKE_C_COMPILER "clang-14") -set(CMAKE_CXX_COMPILER "clang++-14") +project(code_examples LANGUAGES CXX) -set(CMAKE_CXX_FLAGS "-std=c++20 -Wall -Wextra -Werror -Wno-unused-variable -pedantic -fsanitize=address,undefined") +set(CMAKE_CXX_FLAGS "-std=c++20 -Wall -Wextra -Werror -Wno-unused-variable -Wno-unused-but-set-variable -pedantic -fsanitize=address,undefined") set(CMAKE_VERBOSE_MAKEFILE OFF) diff --git a/code_examples/verify.sh b/code_examples/verify.sh index d37e6db..a7c3e79 100644 --- a/code_examples/verify.sh +++ b/code_examples/verify.sh @@ -1,5 +1,5 @@ +#!/bin/bash CMAKE="/usr/bin/cmake" - set -e $CMAKE -S. -B build -G "Unix Makefiles" diff --git a/packages/code_blocks.tex b/packages/code_blocks.tex index f60c2e9..88f3745 100644 --- a/packages/code_blocks.tex +++ b/packages/code_blocks.tex @@ -1,4 +1,4 @@ -\usepackage[newfloat=true]{minted} +\usepackage[newfloat=true,outputdir=build]{minted} \usepackage[]{xcolor} \usemintedstyle{xcode} From 71d1fec963e83a5733cd30a87da6b8d3f2a48c84 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 12:52:12 +0200 Subject: [PATCH 03/44] Page numbering fix + docker documentation. Added documentation for building the book from sources. Fixed page numbering issue. --- .gitignore | 1 + README.md | 16 ++++++++++++++++ chapters/00_title_page.tex | 3 ++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ea880d5..07bc41b 100644 --- a/.gitignore +++ b/.gitignore @@ -277,3 +277,4 @@ TSWLatexianTemp* code_examples/bin code_examples/build +build \ No newline at end of file diff --git a/README.md b/README.md index 7c22451..0fbe758 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,19 @@ This repository contains the LaTeX source and C++ code samples for the book "A C - `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. - `0.1.0` First pre-release + +## Building from sources + +The repository is configured with VSCode devcontainer support. + +Make sure that you have VSCode and Docker installed, then simply open the repository in VSCode. You will be prompted to reopen the project in a docker image. + +VSCode is configured to use the LaTeX Workshop extension. To build the PDF simply press `CTRL+ALT+B` or select `LaTeX Workshop: Build LaTeX Project` from the command palette. +Note that due to the high number of code examples, the build does take a while. +The resulting PDF will be in the build folder. + +### Code samples + +Most code files have wrapping main files that exercise both the build and also contain `assert` expressions that verify correctness of the code. + +The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. \ No newline at end of file diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index 48a45fa..3e0aaf1 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -38,4 +38,5 @@ \endgroup -\clearpage \ No newline at end of file +\clearpage +\pagestyle{plain} \ No newline at end of file From 0b61253e1e7d7519deeb49008f1d598547f5493d Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 13:27:47 +0200 Subject: [PATCH 04/44] Fixed page numbering, small formulation changes. --- book.tex | 2 +- chapters/02_introduction.tex | 10 ++++------ packages/code_blocks.tex | 13 +++++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/book.tex b/book.tex index 9f773ce..f5cf9a9 100644 --- a/book.tex +++ b/book.tex @@ -4,7 +4,7 @@ %\usepackage{showframe} %\checkandfixthelayout -\newcommand{\version}{0.2.0} +\newcommand{\version}{0.2.1} \usepackage{hyperref} \hypersetup{ colorlinks=true, diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index b255703..e946031 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -38,7 +38,7 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} \cppfile{code_examples/introduction/history_cc20_code.h} \end{box-note} -As of the time of writing, the \CC23 is not finalized. However, we already know that it will introduce more range algorithms, more views and the ability to implement custom views. +As of the time of writing, the \CC23 standard is not finalized. However, we already know that it will introduce more range algorithms, more views and the ability to implement custom views. \section{Iterators and ranges} @@ -56,7 +56,7 @@ \section{Iterators and ranges} \cppfile{code_examples/introduction/iterators_code.h} \end{box-note} -Sentinels follow the same idea. However, they do not need to be of an iterator type. Instead, they only need to be comparable to an iterator. The end of the range is then the first iterator that compares equal to the sentinel. +Sentinels follow the same idea. However, they do not need to be of an iterator type. Instead, they only need to be comparable to an iterator. The exclusive end of the range is then the first iterator that compares equal to the sentinel. \begin{box-note} \footnotesize Example of specifying a range using an iterator and custom sentinel. The sentinel will compare \cpp{true} with iterators at least the given distance from the start iterator, therefore defining a range with the specified number of elements. @@ -97,9 +97,7 @@ \section{Naming and common behaviour} \subsection{Counted variants "\texttt{\_n}"} -Counted variants of algorithms accept the range specified using the start iterator and the number of elements (instead of begin and end). This behaviour can be a convenient alternative when working with input and output ranges, where we often do not have an explicit end iterator. - -Since \CC20 we also have access to \cpp{std::counted_iterator} adapter, which will only work with the range versions of algorithms.\\ +Counted variants of algorithms accept the range specified using the start iterator and the number of elements (instead of begin and end). This behaviour can be a convenient alternative when working with input and output ranges, where we often do not have an explicit end iterator.\\ \noindent Examples: \cpp{std::for_each_n}, \cpp{std::copy_n}\\\index{\cpp{std::for_each_n}}\index{\cpp{std::copy_n}} @@ -107,7 +105,7 @@ \subsection{Counted variants "\texttt{\_n}"} \subsection{Copy variants "\texttt{\_copy}"} -Copy variants of in-place algorithms that output the result through the provided iterator. The copy behaviour allows this variant to operate on immutable ranges.\\ +Copy variants of in-place algorithms do not write their output back to the source range. Instead, they output the result to one or more output ranges, usually defined by a single iterator denoting the first element to be written to (the number of elements is implied from the source range). The copy behaviour allows these variants to operate on immutable ranges.\\ \noindent Examples: \cpp{std::remove_copy}, \cpp{std::partial_sort_copy}\index{\cpp{std::remove_copy}}\index{\cpp{std::partial_sort_copy}} diff --git a/packages/code_blocks.tex b/packages/code_blocks.tex index 88f3745..dc79bb6 100644 --- a/packages/code_blocks.tex +++ b/packages/code_blocks.tex @@ -1,4 +1,17 @@ +\def\StripPrefix#1>{} +\def\isOverleaf{\fi + \def\overleafJobname{output}% overleaf defaults to 'output' as \jobname + \edef\overleafJobname{\expandafter\StripPrefix\meaning\overleafJobname}% + \edef\job{\jobname}% + \ifx\job\overleafJobname +} + +\if\isOverleaf% +\usepackage[newfloat=true]{minted} +\else \usepackage[newfloat=true,outputdir=build]{minted} +\fi + \usepackage[]{xcolor} \usemintedstyle{xcode} From d9701c009fc76ecf207c01f6e8f2e575fb81fdf8 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 2 Aug 2022 13:32:36 +0200 Subject: [PATCH 05/44] Update README.md --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0fbe758..37b767b 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.2.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) +[Latest PDF release (v0.2.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.0/book_release_v0.2.0.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) ## Changelog +- `0.2.1` Fixed page numbering issue, small text changes. - `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. - `0.1.0` First pre-release @@ -26,4 +27,4 @@ The resulting PDF will be in the build folder. Most code files have wrapping main files that exercise both the build and also contain `assert` expressions that verify correctness of the code. -The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. \ No newline at end of file +The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. From d421c90deae14604d077218a283bda5523fca488 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 17 Aug 2022 10:07:23 +0200 Subject: [PATCH 06/44] Updates from Overleaf --- book.tex | 2 +- chapters/02_introduction.tex | 4 +++- packages/color_blocks.tex | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/book.tex b/book.tex index f5cf9a9..74c444f 100644 --- a/book.tex +++ b/book.tex @@ -11,7 +11,7 @@ linkcolor=blue, filecolor=magenta, urlcolor=cyan, - pdftitle={Overleaf Example}, + pdftitle={Standard C++ Algorithms}, pdfpagemode=FullScreen, } \usepackage{enumitem} diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index e946031..e93b336 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -119,4 +119,6 @@ \subsection{Restrictions on invocable} Many algorithms can be customized using an invocable. However, with a few exceptions, the invocable is not permitted to modify elements of the range or invalidate iterators. On top of that, unless explicitly noted, the algorithms do not guarantee any particular order of invocation. -These restrictions in practice mean that the passed invocable must be regular. The invocable must return the same result if invoked again with the same arguments. This definition permits accessing a global state such as a cache but does not permit invocables that change their result based on their internal state (such as generators). \ No newline at end of file +These restrictions in practice mean that the passed invocable must be regular. The invocable must return the same result if invoked again with the same arguments. This definition permits accessing a global state such as a cache but does not permit invocables that change their result based on their internal state (such as generators). + +\section{A simple mental model for iterators} diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex index af29078..e5200af 100644 --- a/packages/color_blocks.tex +++ b/packages/color_blocks.tex @@ -37,6 +37,7 @@ boxrule = 0pt, toprule = 4.5pt, % top rule weight enhanced, + breakable, drop fuzzy shadow = black!35, fontupper=\sffamily } \ No newline at end of file From b6ce9b2d6e0945cf963d0a6418564d16a26c10a7 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 20:12:49 +0200 Subject: [PATCH 07/44] Example for input ranges. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_range.cpp | 10 ++++++++++ code_examples/introduction/mental_range_code.h | 7 +++++++ 3 files changed, 20 insertions(+) create mode 100644 code_examples/introduction/mental_range.cpp create mode 100644 code_examples/introduction/mental_range_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index a7719a9..328ad32 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -18,3 +18,6 @@ install(TARGETS sentinels DESTINATION bin) add_executable(categories categories.cpp) install(TARGETS categories DESTINATION bin) + +add_executable(mental_range mental_range.cpp) +install(TARGETS mental_range DESTINATION bin) diff --git a/code_examples/introduction/mental_range.cpp b/code_examples/introduction/mental_range.cpp new file mode 100644 index 0000000..4a92442 --- /dev/null +++ b/code_examples/introduction/mental_range.cpp @@ -0,0 +1,10 @@ +#include +#include +#include +#include + +int main() { +#include "mental_range_code.h" +assert(std::ranges::equal(data, out)); +std::cerr << "."; +} diff --git a/code_examples/introduction/mental_range_code.h b/code_examples/introduction/mental_range_code.h new file mode 100644 index 0000000..11b6a6a --- /dev/null +++ b/code_examples/introduction/mental_range_code.h @@ -0,0 +1,7 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7}; +std::vector out(7,0); + +std::copy(data.begin(), data.end(), // input range + out.begin() // output range, end iterator is implied: + // std::next(out.begin(), std::distance(data.begin(), data.end())); +); From 9899c8a55d0bcf90ad71ad5e3d3a9036b1f363e8 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 20:15:04 +0200 Subject: [PATCH 08/44] Updates from Overleaf --- book.tex | 4 +- chapters/01_preface.tex | 6 +- chapters/02_introduction.tex | 22 ++++-- chapters/03_algorithms_14_uninitialized.tex | 2 +- chapters/03_algorithms_17_minmax.tex | 3 +- chapters/04_ranges_in_depth.tex | 2 +- packages/code_blocks.tex | 8 ++- packages/color_blocks.tex | 36 ++-------- packages/local.tex | 12 ++-- packages/slashbox.sty | 77 --------------------- 10 files changed, 42 insertions(+), 130 deletions(-) delete mode 100644 packages/slashbox.sty diff --git a/book.tex b/book.tex index 74c444f..9995572 100644 --- a/book.tex +++ b/book.tex @@ -4,7 +4,7 @@ %\usepackage{showframe} %\checkandfixthelayout -\newcommand{\version}{0.2.1} +\newcommand{\version}{0.3.0} \usepackage{hyperref} \hypersetup{ colorlinks=true, @@ -33,7 +33,7 @@ \frontmatter \input{chapters/00_title_page} -\input{chapters/01_preface} % In review +\input{chapters/01_preface} % Reviewed \mainmatter \tableofcontents diff --git a/chapters/01_preface.tex b/chapters/01_preface.tex index 8975fce..b273196 100644 --- a/chapters/01_preface.tex +++ b/chapters/01_preface.tex @@ -4,17 +4,17 @@ \chapter{Preface} \section*{About this book} -This book is a complete guide to the \CC standard algorithms. However, that might not mean much to you, so let me unpack this statement. +This book is a complete guide to the \CC\,standard algorithms. However, that might not mean much to you, so let me unpack this statement. This book is a guide, as opposed to a reference, meaning that instead of describing every detail, the book concentrates on examples and pointing out notable, surprising, dangerous or interesting aspects of the different algorithms. Furthermore, unlike a reference, it is supposed to be read, for the most part, like a book in sequential order. -\CC already has one canonical reference, the \CC standard, and for quick lookup, the \href{https://cppreference.com}{cppreference.com} wiki is a great source. +\CC\,already has one canonical reference, the \CC\,standard, and for quick lookup, the \href{https://cppreference.com}{cppreference.com} wiki is a great source. The "complete" part of the statement refers to the width of coverage. The book covers all algorithms and relevant theory up to the \CC20 standard (the \CC23 standard is not finalized at the time of writing). All information is present only in sufficient depth required by the context. This depth limitation keeps the book's overall size reasonable and within the "guide" style. \section*{About the author} -I am Šimon Tóth, the sole author of this book. My primary qualification is 20 years of \CC experience, with approximately 15 of those years \CC being my primary language in professional settings. +I am Šimon Tóth, the sole author of this book. My primary qualification is 20 years of \CC\,experience, with approximately 15 of those years \CC\,being my primary language in professional settings. My background is HPC, spanning academia, big tech and startup environments. I have architected, built and operated systems of all scales, from single machine hardware supported high-availability to planet-scale services.\footnote{You can check my \href{https://cz.linkedin.com/in/simontoth}{LinkedIn profile} for a detailed view of my past career.} diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index e93b336..95e6764 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -1,12 +1,12 @@ \chapter{Introduction} -The \CC standard library is arguably quite limited in its functionality. However, when it comes to data and number crunching, the \CC standard library provides a versatile toolkit of algorithms. +The \CC\,standard library is arguably quite limited in its functionality. However, when it comes to data and number crunching, the \CC\,standard library provides a versatile toolkit of algorithms. -If you are a \CC developer, good familiarity with \CC standard algorithms can save you a lot of effort and accidental bugs. Notably, whenever you see a raw loop in your code, you should question whether calling a standard algorithm wouldn't be a better solution (it usually is). +If you are a \CC\,developer, good familiarity with \CC\,standard algorithms can save you a lot of effort and accidental bugs. Notably, whenever you see a raw loop in your code, you should question whether calling a standard algorithm wouldn't be a better solution (it usually is). \section{History of standard \texorpdfstring{\CC}{C++} algorithms} -While each \CC standard introduced new algorithms or variants, there are few notable milestones in the history of \CC standard algorithms. +While each \CC\,standard introduced new algorithms or variants, there are few notable milestones in the history of \CC\,standard algorithms. The \CC98 standard introduced most of the algorithms. However, it was the \CC11 standard with its introduction of lambdas that made algorithms worthwhile. Before lambdas, the time investment of writing a custom function object made the usefulness of algorithms dubious. @@ -44,7 +44,7 @@ \section{Iterators and ranges} Algorithms operate on data structures, which poses an issue. How do you abstract the implementation details of a specific data structure and allow the algorithm to work with any data structure that satisfies the algorithm's requirements? -The \CC standard library solution to this problem are iterators and ranges. Iterators encapsulate implementation details of data structure traversal and simultaneously expose a set of operations possible on the given data structure in constant time and space. +The \CC\, standard library solution to this problem are iterators and ranges. Iterators encapsulate implementation details of data structure traversal and simultaneously expose a set of operations possible on the given data structure in constant time and space. A range is then denoted by a pair of iterators, or more generally, since \CC20, an iterator and a sentinel. In mathematical terms, a pair of iterators \cpp{it1}, \cpp{it2} denotes a range \cpp{[it1, it2)}, that is, the range includes the element referenced by \cpp{it1} and ends before the element referenced by \cpp{it2}. @@ -121,4 +121,16 @@ \subsection{Restrictions on invocable} These restrictions in practice mean that the passed invocable must be regular. The invocable must return the same result if invoked again with the same arguments. This definition permits accessing a global state such as a cache but does not permit invocables that change their result based on their internal state (such as generators). -\section{A simple mental model for iterators} +\section{A simpler mental model for iterators} + +It can be tricky to internalize all the rules associated with iterators when working with standard algorithms. One shorthand that can help is to think in terms of ranges instead of iterators. + +Ranges passed in as arguments are usually apparent, typically specified by pair of iterators. + + + +The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. + +In some cases, a single returned iterator denotes multiple ranges. + +Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file diff --git a/chapters/03_algorithms_14_uninitialized.tex b/chapters/03_algorithms_14_uninitialized.tex index f517069..3869cb4 100644 --- a/chapters/03_algorithms_14_uninitialized.tex +++ b/chapters/03_algorithms_14_uninitialized.tex @@ -21,7 +21,7 @@ \subsection{\texorpdfstring{\cpp{std::construct_at}, \cpp{std::destroy_at}}{\tex \cppfile{code_examples/algorithms/create_at_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized_value_construct},\newline\texttt{std::uninitialized_fill}, \newline\texttt{std::destroy}}} +\subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized\_value\_construct},\newline\texttt{std::uninitialized\_fill}, \newline\texttt{std::destroy}}} \index{\cpp{std::uninitialized_default_construct}} \index{\cpp{std::uninitialized_value_construct}} \index{\cpp{std::uninitialized_fill}} diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index fc6bcb9..176b044 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -77,7 +77,8 @@ \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} \cppfile{code_examples/algorithms/clamp_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, \texttt{std::minmax\_element}}} +\subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \newline\cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, +\textCR\texttt{std::minmax\_element}}} \index{\cpp{std::min_element}} \index{\cpp{std::max_element}} \index{\cpp{std::minmax_element}} diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index 36cc91b..c99f13b 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -225,7 +225,7 @@ \subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} \cppfile{code_examples/views/all_code.h} \end{box-note} -\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \texttt{std::views::join\_view}}} +\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \newline\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \textCR\texttt{std::views::join\_view}}} \index{\cpp{std::views::split}} \index{\cpp{std::views::lazy_split}} \index{\cpp{std::views::join_view}} diff --git a/packages/code_blocks.tex b/packages/code_blocks.tex index dc79bb6..972f694 100644 --- a/packages/code_blocks.tex +++ b/packages/code_blocks.tex @@ -1,3 +1,4 @@ +% Overleaf botch. Normally we use minted files in the build folder, but Overleaf needs special care. \def\StripPrefix#1>{} \def\isOverleaf{\fi \def\overleafJobname{output}% overleaf defaults to 'output' as \jobname @@ -19,6 +20,9 @@ \newminted{cpp}{ linenos=true, + firstnumber=1, + tabsize=4, + obeytabs, xleftmargin=1em, breaklines, fontsize=\footnotesize, @@ -34,4 +38,6 @@ breaklines, fontsize=\footnotesize, numbersep=0.75em -} \ No newline at end of file +} + +\newmintinline[cpp]{cpp}{} \ No newline at end of file diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex index e5200af..de71fdf 100644 --- a/packages/color_blocks.tex +++ b/packages/color_blocks.tex @@ -1,36 +1,4 @@ \usepackage[many]{tcolorbox} % for COLORED BOXES (tikz and xcolor included) -\usepackage{xcolor} - -\tcbset{ - sharp corners, - colback = white, - before skip = 0.2cm, % add extra space before the box - after skip = 0.5cm % add extra space after the box -} % setting global options for tcolorbox - -\newtcolorbox{box-info}{ - colback = LightSkyBlue!20, - colframe = MidnightBlue!50, - boxrule = 0pt, - leftrule = 6pt, % left rule weight - fontupper=\sffamily -} - -\newtcolorbox{box-warning}{ - colback = LightGoldenrodYellow, - colframe = LightGoldenrod, - boxrule = 0pt, - leftrule = 6pt, % left rule weight - fontupper=\sffamily -} - -\newtcolorbox{box-critical}{ - colback = LightSalmon!20, - colframe = OrangeRed!80, - boxrule = 0pt, - leftrule = 6pt, % left rule weight - fontupper=\sffamily -} \newtcolorbox{box-note}{ sharpish corners, % better drop shadow @@ -38,6 +6,10 @@ toprule = 4.5pt, % top rule weight enhanced, breakable, + enlarge top by=5pt, + colback = white, + before skip = 0.2cm, % add extra space before the box + after skip = 0.5cm, % add extra space after the box drop fuzzy shadow = black!35, fontupper=\sffamily } \ No newline at end of file diff --git a/packages/local.tex b/packages/local.tex index 4de713d..76a389e 100644 --- a/packages/local.tex +++ b/packages/local.tex @@ -1,5 +1,6 @@ -\usepackage{sidenotes} -\usepackage{multirow} +\usepackage{sidenotes} % Required +\usepackage{multirow} % Required +\usepackage{diagbox} % Used for boolean algorithms. % Command to consistently format side-panel with C++ versions that introduced different variants of the algorithm. \newcommand{\cppversions}[5]{ @@ -19,6 +20,7 @@ \end{margintable} } +% Command to consistently format the algorithm info table. \newcommand{\constraints}[4]{ \begin{center} \footnotesize @@ -45,8 +47,4 @@ \hline \end{tabular} \end{center} -} - -\newmintinline[cpp]{cpp}{} - -\usepackage{diagbox} +} \ No newline at end of file diff --git a/packages/slashbox.sty b/packages/slashbox.sty deleted file mode 100644 index 7c273f5..0000000 --- a/packages/slashbox.sty +++ /dev/null @@ -1,77 +0,0 @@ -% slashbox.sty by Koichi Yasuoka, May 27, 1993 -% minor modification by Toru Sato, May 31, 1993 -\typeout{slashbox style by K.Yasuoka, May 1993.}% -\newbox\@slashboxa -\newbox\@slashboxb -\newbox\@slashboxc -\newcount\@slashboxwd -\newcount\@slashboxht -\newdimen\@slashsepl -\newdimen\@slashsepr -\def\slashbox{% - \def\@slashboxpicture##1{% - \put(0,0){\line(##1,1){\@slashboxwd}}% - \put(0,\@slashboxht){\makebox(0,0)[tl]{\box\@slashboxa}}% - \put(\@slashboxwd,0){\makebox(0,0)[br]{\box\@slashboxb}}% - }% - \@slashbox -}% -\def\backslashbox{% - \def\@slashboxpicture##1{% - \put(0,\@slashboxht){\line(##1,-1){\@slashboxwd}}% - \put(0,0){\makebox(0,0)[bl]{\box\@slashboxa}}% - \put(\@slashboxwd,\@slashboxht){\makebox(0,0)[tr]{\box\@slashboxb}}% - }% - \@slashbox -}% -\def\@slashbox{\@ifnextchar [{\@@slashbox}{\@@slashbox[0pt]}} -\def\@@slashbox[#1]{\@ifnextchar [{\@@@slashbox[#1]}{\@@@slashbox[#1][c]}} -\def\@@@slashbox[#1][#2]#3#4{% -% #1: width, #2: suppression of \tabcolsep on `l', `r', or `lr' side -% #3: left item, #4: right item - \@slashsepl=\tabcolsep - \@slashsepr=\tabcolsep - \@tfor\@tempa :=#2\do{\expandafter\let - \csname @slashsep\@tempa\endcsname=\z@}% - \setbox\@slashboxa=\hbox{\strut\hskip\tabcolsep\shortstack[l]{#3}}% - \setbox\@slashboxb=\hbox{\shortstack[r]{#4}\hskip\tabcolsep\strut}% - \setbox\@slashboxa=\hbox{\raise\dp\@slashboxa\box\@slashboxa}% - \setbox\@slashboxb=\hbox{\raise\dp\@slashboxb\box\@slashboxb}% - \setbox\@slashboxc=\hbox{% - \@tempdima=\wd\@slashboxa - \advance\@tempdima by \wd\@slashboxb - \advance\@tempdima by \@slashsepl - \advance\@tempdima by \@slashsepr - \@tempdimb=#1\relax% - \ifdim\@tempdimb>\@tempdima \@tempdima=\@tempdimb\fi% - \@tempdimb=\ht\@slashboxa - \advance\@tempdimb by \dp\@slashboxa - \advance\@tempdimb by \ht\@slashboxb - \advance\@tempdimb by \dp\@slashboxb - \@tempcnta=\@tempdima - \@tempcntb=\@tempdimb - \advance\@tempcnta by \@tempcntb - \advance\@tempcnta by -1 - \divide\@tempcnta by \@tempcntb - \ifnum\@tempcnta>6 \@tempcnta=6 - \@tempdimb=0.166666666\@tempdima - \else - \ifnum\@tempcnta<1 \@tempcnta=1\fi - \@tempdima=\@tempdimb - \multiply\@tempdima by \@tempcnta - \fi% - \advance\@tempdima by -\@slashsepl - \advance\@tempdima by -\@slashsepr - \@slashboxwd=\@tempdima - \@slashboxht=\@tempdimb - \@tempcntb=\@slashsepl - \setlength{\unitlength}{1sp}% - \begin{picture}(\@slashboxwd,\@slashboxht)(\@tempcntb,0) - \advance\@tempdima by \@slashsepl - \advance\@tempdima by \@slashsepr - \@slashboxwd=\@tempdima - \@slashboxpicture{\@tempcnta} - \end{picture}% - }% - $\vcenter{\box\@slashboxc}$% -}% \ No newline at end of file From 484e9fedf34e6652fe7053ca35dcd975e3261d9c Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 21:48:11 +0200 Subject: [PATCH 09/44] Example of is_sorted_until. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_range_code.h | 3 ++- code_examples/introduction/mental_sorted.cpp | 9 +++++++++ code_examples/introduction/mental_sorted_code.h | 13 +++++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 code_examples/introduction/mental_sorted.cpp create mode 100644 code_examples/introduction/mental_sorted_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index 328ad32..8368160 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -21,3 +21,6 @@ install(TARGETS categories DESTINATION bin) add_executable(mental_range mental_range.cpp) install(TARGETS mental_range DESTINATION bin) + +add_executable(mental_sorted mental_sorted.cpp) +# Compile only diff --git a/code_examples/introduction/mental_range_code.h b/code_examples/introduction/mental_range_code.h index 11b6a6a..9abb862 100644 --- a/code_examples/introduction/mental_range_code.h +++ b/code_examples/introduction/mental_range_code.h @@ -3,5 +3,6 @@ std::vector out(7,0); std::copy(data.begin(), data.end(), // input range out.begin() // output range, end iterator is implied: - // std::next(out.begin(), std::distance(data.begin(), data.end())); + // std::next(out.begin(), + // std::distance(data.begin(), data.end())); ); diff --git a/code_examples/introduction/mental_sorted.cpp b/code_examples/introduction/mental_sorted.cpp new file mode 100644 index 0000000..6669769 --- /dev/null +++ b/code_examples/introduction/mental_sorted.cpp @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include + +int main() { +#include "mental_sorted_code.h" +} diff --git a/code_examples/introduction/mental_sorted_code.h b/code_examples/introduction/mental_sorted_code.h new file mode 100644 index 0000000..896dba2 --- /dev/null +++ b/code_examples/introduction/mental_sorted_code.h @@ -0,0 +1,13 @@ +std::vector data{1, 4, 5, 7, 9, 2, 3}; + +// is_sorted_until returns the first out of order element. +auto result = std::is_sorted_until(data.begin(), data.end()); + +// [begin, result) is the maximal sorted sub-range +for (auto it = data.begin(); it != result; it++) { + // Iterate over all elements in the sorted sub-range. + // {1, 4, 5, 7, 9} +} +for (auto v : std::ranges::subrange(data.begin(), result)) { + // Same, but using a range-based for loop. +} From 41a21882ee09e129cf17aa29497b51a6aac556de Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 21:49:30 +0200 Subject: [PATCH 10/44] Updates from Overleaf --- book.tex | 6 +++--- chapters/02_introduction.tex | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/book.tex b/book.tex index 9995572..59ae40f 100644 --- a/book.tex +++ b/book.tex @@ -33,14 +33,14 @@ \frontmatter \input{chapters/00_title_page} -\input{chapters/01_preface} % Reviewed +%\input{chapters/01_preface} % Reviewed \mainmatter \tableofcontents \input{chapters/02_introduction} % In review -\input{chapters/03_algorithms_00_main} % In review -\input{chapters/04_ranges_in_depth} % In review +%\input{chapters/03_algorithms_00_main} % In review +%\input{chapters/04_ranges_in_depth} % In review \appendix \input{chapters/XX_index} diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 95e6764..c93ddc3 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -127,10 +127,16 @@ \section{A simpler mental model for iterators} Ranges passed in as arguments are usually apparent, typically specified by pair of iterators. - +\begin{box-note} +\footnotesize Example with two ranges passed in as an argument. The input range is fully specified, and the end iterator for the output range is implied from the number of elements in the input range. +\tcblower +\cppfile{code_examples/introduction/mental_range_code.h} +\end{box-note} The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. + + In some cases, a single returned iterator denotes multiple ranges. Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file From 974ad98444b4ce457d4b53f748e84aa8b84a9e71 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 22:19:29 +0200 Subject: [PATCH 11/44] Example with two sub-ranges. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_two.cpp | 9 +++++++++ code_examples/introduction/mental_two_code.h | 11 +++++++++++ 3 files changed, 23 insertions(+) create mode 100644 code_examples/introduction/mental_two.cpp create mode 100644 code_examples/introduction/mental_two_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index 8368160..05e8e32 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -24,3 +24,6 @@ install(TARGETS mental_range DESTINATION bin) add_executable(mental_sorted mental_sorted.cpp) # Compile only + +add_executable(mental_two mental_two.cpp) +# Compile only diff --git a/code_examples/introduction/mental_two.cpp b/code_examples/introduction/mental_two.cpp new file mode 100644 index 0000000..3985d07 --- /dev/null +++ b/code_examples/introduction/mental_two.cpp @@ -0,0 +1,9 @@ +#include +#include +#include +#include +#include + +int main() { +#include "mental_two_code.h" +} diff --git a/code_examples/introduction/mental_two_code.h b/code_examples/introduction/mental_two_code.h new file mode 100644 index 0000000..6bc63d4 --- /dev/null +++ b/code_examples/introduction/mental_two_code.h @@ -0,0 +1,11 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; + +// lower_bound returns the first element !(el < 4) +auto lb = std::lower_bound(data.begin(), data.end(), 4); + +for (auto v : std::ranges::subrange(data.begin(), lb)) { + // lower range [begin, lb): elements < 4 +} +for (auto v : std::ranges::subrange(lb, data.end())) { + // upper range [lb, end): elements >= 4 +} From cb4be6e8b96f3ff0c76c617d19dc8dd5a43bab35 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 5 Sep 2022 22:21:24 +0200 Subject: [PATCH 12/44] Updates from Overleaf --- chapters/02_introduction.tex | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index c93ddc3..952dbf6 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -135,8 +135,20 @@ \section{A simpler mental model for iterators} The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. +\begin{box-note} +\footnotesize Example of \cpp{std::is_sorted_until} that returns an iterator to the first out-of-order element, which can also be thought as the end iterator for a maximal sorted sub-range. +\tcblower +\cppfile{code_examples/introduction/mental_sorted_code.h} +\end{box-note} +The benefit of thinking about the returned value as the end iterator of a range is that it removes the potential for corner cases. For example, what if the algorithm doesn't find any element out of order? The returned value will be the end iterator of the source range, meaning that the range returned is simply the entire source range. In some cases, a single returned iterator denotes multiple ranges. +\begin{box-note} +\footnotesize Example of \cpp{std::lower_bound} that splits the range into two sub-ranges. +\tcblower +\cppfile{code_examples/introduction/mental_two_code.h} +\end{box-note} + Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file From b33661d675c5ef4a5826292b4e14af9cd54f21bb Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 6 Sep 2022 16:26:32 +0200 Subject: [PATCH 13/44] Example with find. --- code_examples/introduction/CMakeLists.txt | 3 +++ code_examples/introduction/mental_find.cpp | 6 ++++++ code_examples/introduction/mental_find_code.h | 10 ++++++++++ 3 files changed, 19 insertions(+) create mode 100644 code_examples/introduction/mental_find.cpp create mode 100644 code_examples/introduction/mental_find_code.h diff --git a/code_examples/introduction/CMakeLists.txt b/code_examples/introduction/CMakeLists.txt index 05e8e32..6af82e0 100644 --- a/code_examples/introduction/CMakeLists.txt +++ b/code_examples/introduction/CMakeLists.txt @@ -27,3 +27,6 @@ add_executable(mental_sorted mental_sorted.cpp) add_executable(mental_two mental_two.cpp) # Compile only + +add_executable(mental_find mental_find.cpp) +# Compile only diff --git a/code_examples/introduction/mental_find.cpp b/code_examples/introduction/mental_find.cpp new file mode 100644 index 0000000..be40475 --- /dev/null +++ b/code_examples/introduction/mental_find.cpp @@ -0,0 +1,6 @@ +#include +#include + +int main() { +#include "mental_find_code.h" +} diff --git a/code_examples/introduction/mental_find_code.h b/code_examples/introduction/mental_find_code.h new file mode 100644 index 0000000..0fa87e7 --- /dev/null +++ b/code_examples/introduction/mental_find_code.h @@ -0,0 +1,10 @@ +std::string str("Hello World!"); + +// Returns the iterator to the first occurence of ' ' +auto it = std::find(str.begin(), str.end(), ' '); + +// Range [begin, it) is the maximal prefix range +// that doesn't contain ' ' +for (auto v : std::string_view(str.begin(), it)) { + // iterate over "Hello" +} From f28fac5c6ec5694684ba8a24497919291d606efa Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 6 Sep 2022 16:27:51 +0200 Subject: [PATCH 14/44] Updates from Overleaf --- chapters/02_introduction.tex | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 952dbf6..a32d8f9 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -133,7 +133,7 @@ \section{A simpler mental model for iterators} \cppfile{code_examples/introduction/mental_range_code.h} \end{box-note} -The less obvious can be the ranges returned by the algorithms. In some cases, the range is evident from the algorithm. +The returned range can also be evident from the semantics of the algorithm. \begin{box-note} \footnotesize Example of \cpp{std::is_sorted_until} that returns an iterator to the first out-of-order element, which can also be thought as the end iterator for a maximal sorted sub-range. @@ -151,4 +151,5 @@ \section{A simpler mental model for iterators} \cppfile{code_examples/introduction/mental_two_code.h} \end{box-note} -Even when the algorithms seem to return an iterator to a specific element, we can still think of the output as a range. \ No newline at end of file +Even when the algorithm returns an iterator to a specific element, it might be worth considering the implied range. + From 981ae109215bbe4efc2b2831b0e76ac44cd2f041 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 19 Sep 2022 10:04:12 +0200 Subject: [PATCH 15/44] Unqualified lookup example. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_unqalified.cpp | 2 ++ code_examples/theory/adl_unqalified_code.h | 11 +++++++++++ 3 files changed, 16 insertions(+) create mode 100644 code_examples/theory/adl_unqalified.cpp create mode 100644 code_examples/theory/adl_unqalified_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index c70b848..19beec4 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -12,3 +12,6 @@ install(TARGETS const_cast DESTINATION bin) add_executable(initializer_list initializer_list.cpp) install(TARGETS initializer_list DESTINATION bin) + +add_executable(adl_unqalified adl_unqalified.cpp) +# Compile only diff --git a/code_examples/theory/adl_unqalified.cpp b/code_examples/theory/adl_unqalified.cpp new file mode 100644 index 0000000..dde1059 --- /dev/null +++ b/code_examples/theory/adl_unqalified.cpp @@ -0,0 +1,2 @@ +#include "adl_unqalified_code.h" +int main() {} diff --git a/code_examples/theory/adl_unqalified_code.h b/code_examples/theory/adl_unqalified_code.h new file mode 100644 index 0000000..d13b7fd --- /dev/null +++ b/code_examples/theory/adl_unqalified_code.h @@ -0,0 +1,11 @@ +namespace A { +void some_call(int) {} +namespace B { +void some_call(double) {} + +void test() { + some_call(1); // A::B::some_call + some_call(2.0); // A::B::some_call +} +} +} From 66863c68fbd050624e9d1596c0821f5f85b5c6b3 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Mon, 19 Sep 2022 10:11:13 +0200 Subject: [PATCH 16/44] Updates from Overleaf --- book.tex | 6 +++++- chapters/02_introduction.tex | 10 ++++++++-- chapters/03_algorithms_01_foreach.tex | 8 ++++---- chapters/90_theory.tex | 13 +++++++++++++ packages/color_blocks.tex | 13 +++++++++++++ 5 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 chapters/90_theory.tex diff --git a/book.tex b/book.tex index 59ae40f..bff00c6 100644 --- a/book.tex +++ b/book.tex @@ -38,13 +38,17 @@ \mainmatter \tableofcontents -\input{chapters/02_introduction} % In review +%\input{chapters/02_introduction} % In review %\input{chapters/03_algorithms_00_main} % In review %\input{chapters/04_ranges_in_depth} % In review +\input{chapters/90_theory} % TODO + \appendix \input{chapters/XX_index} +% Section on iterator invalidation + \backmatter \end{document} \ No newline at end of file diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index a32d8f9..70941f1 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -10,11 +10,12 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} The \CC98 standard introduced most of the algorithms. However, it was the \CC11 standard with its introduction of lambdas that made algorithms worthwhile. Before lambdas, the time investment of writing a custom function object made the usefulness of algorithms dubious. -\begin{box-note} +\raggedbottom +\begin{box-nobreak} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a custom function object, calculating the number of elements and their sum. \tcblower \cppfile{code_examples/introduction/history_cc98_code.h} -\end{box-note} +\end{box-nobreak} \begin{box-note} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a capturing lambda, calculating the number of elements and their sum. @@ -153,3 +154,8 @@ \section{A simpler mental model for iterators} Even when the algorithm returns an iterator to a specific element, it might be worth considering the implied range. +\begin{box-note} +\footnotesize Example of \cpp{std::find} establishing a prefix range that doesn't contain the searched-for element. +\tcblower +\cppfile{code_examples/introduction/mental_find_code.h} +\end{box-note} \ No newline at end of file diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index f068ace..2acd028 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -7,7 +7,7 @@ \section{Introducing the algorithms} \begin{enumerate}[label=\protect\circled{\arabic*}] \item The presentation of each algorithm will start with a short description. \item The margin will contain information about the history of the algorithm: which C++ standard introduced it and whether it has constexpr, parallel and range variants and including versions of the standard that introduced them. - \item Following that will be the description of constraints. Algorithms that write data to a distinct output range are denoted with an arrow: \cpp{input_range -> output_range}. + \item Following that will be the description of constraints. Algorithms that write data to a distinct output range are denoted with an arrow: \newline\cpp{input_range -> output_range}. \item Finally, each description will conclude with one or more examples with explanations. \end{enumerate} @@ -36,7 +36,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} \end{tabular} \end{center} -\circled{4} The C++11 standard introduced the range loop, which mostly replaced the uses of \cpp{std::for_each}. +\circled{4} The C++11 standard introduced the range-based for loop, which mostly replaced the uses of \cpp{std::for_each}. \begin{box-note} \footnotesize Example of a range loop over all elements of a \cpp{std::vector}. @@ -52,7 +52,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} However, there are still a few corner cases when using \cpp{std::for_each} is preferable. -The first case is straightforward parallelization. Invoking an expensive operation for each element in parallel is trivial with \cpp{std::for_each}. On top of that, as long as the operations are independent, there is no need for synchronization primitives. +The first case is straightforward parallelization. Invoking an expensive operation for each element in parallel is trivial with \cpp{std::for_each}. As long as the operations are independent, there is no need for synchronization primitives. \begin{box-note} \footnotesize Example of a parallel \cpp{std::for_each} invoking a method on each element independently in parallel. @@ -63,7 +63,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} Second, the range version can provide a more concise and explicit syntax in some cases because of the projection support introduced in C++20. \begin{box-note} -\footnotesize Example of the range version \cpp{std::ranges::for_each} utilizing a projection to iterate over the result of invoking the method \cpp{getValue()} on each element. +\footnotesize Example of the range version of \cpp{std::ranges::for_each} utilizing a projection to invoke the method \cpp{getValue()} (line 13) on each element and summing the resulting values using a lambda (line 12). \tcblower \cppfile{code_examples/algorithms/for_each_code_cpp20.h} \end{box-note} diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex new file mode 100644 index 0000000..91a3382 --- /dev/null +++ b/chapters/90_theory.tex @@ -0,0 +1,13 @@ +\chapter{In-depth} + +In this chapter, we will go over relevant in-depth topics referenced throughout the rest of the book. + +\section{Argument-dependent lookup (ADL)} + +When calling a method without qualification (i.e. not specifying the namespace), the compiler needs to determine the set of candidate functions. As a first step, the compiler will do an unqualified name lookup, which starts at the local scope and examines the parent scopes until it finds the first instance of the name (at which point it stops). + +\begin{box-note} +\footnotesize Example of unqualified lookup. Both calls to \cpp{some_call} will resolve to \cpp{::A::B::some_call} since this is the first instance discovered by the compiler. +\tcblower +\cppfile{code_examples/theory/adl_unqualified.h} +\end{box-note} \ No newline at end of file diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex index de71fdf..602ca3b 100644 --- a/packages/color_blocks.tex +++ b/packages/color_blocks.tex @@ -12,4 +12,17 @@ after skip = 0.5cm, % add extra space after the box drop fuzzy shadow = black!35, fontupper=\sffamily +} + +\newtcolorbox{box-nobreak}{ + sharpish corners, % better drop shadow + boxrule = 0pt, + toprule = 4.5pt, % top rule weight + enhanced, + enlarge top by=5pt, + colback = white, + before skip = 0.2cm, % add extra space before the box + after skip = 0.5cm, % add extra space after the box + drop fuzzy shadow = black!35, + fontupper=\sffamily } \ No newline at end of file From 98f84e98fc2ecfe73594e59574a66bdab6641c54 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 20 Sep 2022 15:29:11 +0200 Subject: [PATCH 17/44] Simple ADL example. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_code.h | 2 +- code_examples/theory/adl_simple.cpp | 5 +++++ code_examples/theory/adl_simple_code.h | 17 +++++++++++++++++ code_examples/theory/adl_unqalified.cpp | 4 +++- code_examples/theory/adl_unqalified_code.h | 4 +++- 6 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 code_examples/theory/adl_simple.cpp create mode 100644 code_examples/theory/adl_simple_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 19beec4..265d275 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -15,3 +15,6 @@ install(TARGETS initializer_list DESTINATION bin) add_executable(adl_unqalified adl_unqalified.cpp) # Compile only + +add_executable(adl_simple adl_simple.cpp) +# Compile only diff --git a/code_examples/theory/adl_code.h b/code_examples/theory/adl_code.h index 5f03abc..51de88b 100644 --- a/code_examples/theory/adl_code.h +++ b/code_examples/theory/adl_code.h @@ -10,5 +10,5 @@ std::ostream& operator << (std::ostream& s, const X&) { void calling_site() { Custom::X x; Custom::operator << (std::cout, x); // OK, explicit call - std::cout << x; // OK, unqualified operator <<, ADL + std::cout << x; // Requires ADL } diff --git a/code_examples/theory/adl_simple.cpp b/code_examples/theory/adl_simple.cpp new file mode 100644 index 0000000..e35667d --- /dev/null +++ b/code_examples/theory/adl_simple.cpp @@ -0,0 +1,5 @@ +#include +#include "adl_simple_code.h" +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_simple_code.h b/code_examples/theory/adl_simple_code.h new file mode 100644 index 0000000..f9400b8 --- /dev/null +++ b/code_examples/theory/adl_simple_code.h @@ -0,0 +1,17 @@ +namespace A { + struct X {}; + void some_func(const X&) {} +} + +namespace B { + struct Y {}; + void some_func(const Y&) {} +} + +void some_func(const auto&) {} + +void calling_site() { + A::X x; B::Y y; + some_func(x); // Calls A::some_func + some_func(y); // Calls B::some_func +} diff --git a/code_examples/theory/adl_unqalified.cpp b/code_examples/theory/adl_unqalified.cpp index dde1059..82a0145 100644 --- a/code_examples/theory/adl_unqalified.cpp +++ b/code_examples/theory/adl_unqalified.cpp @@ -1,2 +1,4 @@ #include "adl_unqalified_code.h" -int main() {} +int main() { + A::B::calling_site(); +} diff --git a/code_examples/theory/adl_unqalified_code.h b/code_examples/theory/adl_unqalified_code.h index d13b7fd..93df3bd 100644 --- a/code_examples/theory/adl_unqalified_code.h +++ b/code_examples/theory/adl_unqalified_code.h @@ -1,11 +1,13 @@ namespace A { void some_call(int) {} +void some_call(const char*) {} namespace B { void some_call(double) {} -void test() { +void calling_site() { some_call(1); // A::B::some_call some_call(2.0); // A::B::some_call + //some_call("hello world"); Will not compile, no conversion from const char* -> double } } } From 3c9eb61f3e75e3abb91a3ad19e5991859aeae723 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 20 Sep 2022 15:30:48 +0200 Subject: [PATCH 18/44] Updates from Overleaf --- chapters/90_theory.tex | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 91a3382..7065569 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -9,5 +9,21 @@ \section{Argument-dependent lookup (ADL)} \begin{box-note} \footnotesize Example of unqualified lookup. Both calls to \cpp{some_call} will resolve to \cpp{::A::B::some_call} since this is the first instance discovered by the compiler. \tcblower -\cppfile{code_examples/theory/adl_unqualified.h} +\cppfile{code_examples/theory/adl_unqalified_code.h} +\end{box-note} + +Due to the simplicity of unqualified lookup, we need an additional mechanism to discover overloads. Notably, it is a requirement for operator overloading since operator calls are unqualified. This is where argument-dependent lookup comes in. + +\begin{box-note} +\footnotesize Without ADL, any call to a custom operator overload would have to be fully qualified, requiring the function call syntax. +\tcblower +\cppfile{code_examples/theory/adl_code.h} +\end{box-note} + +While the full rules for ADL are long, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. + +\begin{box-note} +\footnotesize +\tcblower +\cppfile{code_examples/theory/adl_code.h} \end{box-note} \ No newline at end of file From 608397d06741151dfa3dcbbbb90e138e7c079dfc Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 18:38:01 +0200 Subject: [PATCH 19/44] Example of non-function symbol. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_nonfunc.cpp | 5 +++++ code_examples/theory/adl_nonfunc_code.h | 11 +++++++++++ code_examples/theory/adl_unqalified_code.h | 3 ++- 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 code_examples/theory/adl_nonfunc.cpp create mode 100644 code_examples/theory/adl_nonfunc_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 265d275..3771446 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -18,3 +18,6 @@ add_executable(adl_unqalified adl_unqalified.cpp) add_executable(adl_simple adl_simple.cpp) # Compile only + +add_executable(adl_nonfunc adl_nonfunc.cpp) +# Compile only diff --git a/code_examples/theory/adl_nonfunc.cpp b/code_examples/theory/adl_nonfunc.cpp new file mode 100644 index 0000000..8643d7b --- /dev/null +++ b/code_examples/theory/adl_nonfunc.cpp @@ -0,0 +1,5 @@ +#include "adl_nonfunc_code.h" + +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_nonfunc_code.h b/code_examples/theory/adl_nonfunc_code.h new file mode 100644 index 0000000..3879cec --- /dev/null +++ b/code_examples/theory/adl_nonfunc_code.h @@ -0,0 +1,11 @@ +namespace Custom { + struct X {}; + + constexpr inline auto some_func = [](const X&) {}; +} + +void calling_site() { + Custom::X x; + // some_func(x); // Will not compile, not visible to ADL. + Custom::some_func(x); // OK +} diff --git a/code_examples/theory/adl_unqalified_code.h b/code_examples/theory/adl_unqalified_code.h index 93df3bd..e136be0 100644 --- a/code_examples/theory/adl_unqalified_code.h +++ b/code_examples/theory/adl_unqalified_code.h @@ -7,7 +7,8 @@ void some_call(double) {} void calling_site() { some_call(1); // A::B::some_call some_call(2.0); // A::B::some_call - //some_call("hello world"); Will not compile, no conversion from const char* -> double + // some_call("hello world"); will not compile + // no conversion from const char* -> double } } } From edfd4934aa87d6be0fdd619cd5e3854cb6bc9bf3 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 18:38:24 +0200 Subject: [PATCH 20/44] Updates from Overleaf --- chapters/90_theory.tex | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 7065569..f25acb5 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -1,6 +1,6 @@ -\chapter{In-depth} +\chapter{Bits of C++ theory} -In this chapter, we will go over relevant in-depth topics referenced throughout the rest of the book. +This chapter will dive deep into the various topics referenced throughout the book. While this chapter serves as a reference, the topics are still presented in a heavily simplified, example-heavy format. For a proper reference, please refer to the C++ standard. \section{Argument-dependent lookup (ADL)} @@ -20,10 +20,25 @@ \section{Argument-dependent lookup (ADL)} \cppfile{code_examples/theory/adl_code.h} \end{box-note} -While the full rules for ADL are long, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. +While the full rules for ADL are quite complex, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. \begin{box-note} \footnotesize \tcblower -\cppfile{code_examples/theory/adl_code.h} -\end{box-note} \ No newline at end of file +\cppfile{code_examples/theory/adl_simple_code.h} +\end{box-note} + +Arguably the true power of ADL lies in the interactions with other language features, so let's look at how ADL interacts with friend functions and function objects. + +\subsection{Friend functions vs ADL} + +Friend functions (when defined inline) do not participate in the normal lookup (they are part of the surrounding namespace but are not visible). However, they are still visible to ADL, which permits a common implementation pattern for a default implementation with a customization point through ADL. + +\begin{box-note} +\footnotesize Example demonstrating a default implementation with a customization point through ADL. The default implementation needs to be discovered during the unqualified lookup; therefore, if any custom implementation is visible in the surrounding namespaces, it will block this discovery. +\tcblower +\cppfile{code_examples/theory/adl_default_code.h} +\end{box-note} + +\subsection{Function objects vs ADL} + From 0e94f238791b36b98b2c398122aeea19d1321571 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 19:26:02 +0200 Subject: [PATCH 21/44] Example of shutting down ADL. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_shutdown.cpp | 4 ++++ code_examples/theory/adl_shutdown_code.h | 13 +++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 code_examples/theory/adl_shutdown.cpp create mode 100644 code_examples/theory/adl_shutdown_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 3771446..1884d01 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -21,3 +21,6 @@ add_executable(adl_simple adl_simple.cpp) add_executable(adl_nonfunc adl_nonfunc.cpp) # Compile only + +add_executable(adl_shutdown adl_shutdown.cpp) +# Compile only diff --git a/code_examples/theory/adl_shutdown.cpp b/code_examples/theory/adl_shutdown.cpp new file mode 100644 index 0000000..a4c4e78 --- /dev/null +++ b/code_examples/theory/adl_shutdown.cpp @@ -0,0 +1,4 @@ +#include "adl_shutdown_code.h" +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_shutdown_code.h b/code_examples/theory/adl_shutdown_code.h new file mode 100644 index 0000000..4a93428 --- /dev/null +++ b/code_examples/theory/adl_shutdown_code.h @@ -0,0 +1,13 @@ +namespace Custom { + struct X { + friend void some_func(const X&) {} + }; +} + +constexpr inline auto some_func = [](const auto&) {}; + +void calling_site() { + Custom::X x; + some_func(x); // calls ::some_func + // Because ::some_func prevents ADL, Custom::some_func cannot be called. +} From 2c1641be364597b4974ac9ec498322b38fc1c1de Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Tue, 27 Sep 2022 19:28:18 +0200 Subject: [PATCH 22/44] Updates from Overleaf --- chapters/90_theory.tex | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index f25acb5..ef15732 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -42,3 +42,20 @@ \subsection{Friend functions vs ADL} \subsection{Function objects vs ADL} +The second notable interaction occurs with non-function symbols. In the context of this section, the important ones are function objects (and lambdas). + +Argument-dependent lookup will not consider non-function symbols. This means that a function object or a lambda must be either visible to an unqualified lookup or need to be fully qualified. + +\begin{box-note} +\footnotesize Example demonstrating a lambda stored in an inline variable that can only be invoked through a qualified call. +\tcblower +\cppfile{code_examples/theory/adl_nonfunc_code.h} +\end{box-note} + +Finally, discovering a non-function symbol during the unqualified lookup phase will prevent ADL completely. + +\begin{box-note} +\footnotesize Example demonstrating a non-function object preventing ADL, making a friend function impossible to invoke. +\tcblower +\cppfile{code_examples/theory/adl_shutdown_code.h} +\end{box-note} \ No newline at end of file From f31788f44b5a35886e92882217408b96acdb2e76 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:34:15 +0200 Subject: [PATCH 23/44] Example for Niebloid. --- code_examples/theory/CMakeLists.txt | 3 +++ code_examples/theory/adl_niebloid.cpp | 6 +++++ code_examples/theory/adl_niebloid_code.h | 29 ++++++++++++++++++++++++ code_examples/theory/adl_shutdown_code.h | 2 +- 4 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 code_examples/theory/adl_niebloid.cpp create mode 100644 code_examples/theory/adl_niebloid_code.h diff --git a/code_examples/theory/CMakeLists.txt b/code_examples/theory/CMakeLists.txt index 1884d01..f1bdbbd 100644 --- a/code_examples/theory/CMakeLists.txt +++ b/code_examples/theory/CMakeLists.txt @@ -24,3 +24,6 @@ add_executable(adl_nonfunc adl_nonfunc.cpp) add_executable(adl_shutdown adl_shutdown.cpp) # Compile only + +add_executable(adl_niebloid adl_niebloid.cpp) +# Compile only diff --git a/code_examples/theory/adl_niebloid.cpp b/code_examples/theory/adl_niebloid.cpp new file mode 100644 index 0000000..9b45688 --- /dev/null +++ b/code_examples/theory/adl_niebloid.cpp @@ -0,0 +1,6 @@ +#include + +#include "adl_niebloid_code.h" +int main() { + calling_site(); +} diff --git a/code_examples/theory/adl_niebloid_code.h b/code_examples/theory/adl_niebloid_code.h new file mode 100644 index 0000000..5552b66 --- /dev/null +++ b/code_examples/theory/adl_niebloid_code.h @@ -0,0 +1,29 @@ +namespace dflt { +namespace impl { + template + concept HasCustomImpl = requires(T a) { do_something(a); }; + + struct DoSomethingFn { + template void operator()(T&& arg) const + requires HasCustomImpl { do_something(std::forward(arg)); } + template void operator()(T&&) const + requires (!HasCustomImpl) { /* default implementation */ } + }; +} + +inline namespace var { + constexpr inline auto do_something = impl::DoSomethingFn{}; +} +} + +namespace custom { + struct X { friend void do_something(const X&){}; }; + struct Y {}; +} + +void calling_site() { + custom::X x; + custom::Y y; + dflt::do_something(x); // calls custom::do_something(const X&) + dflt::do_something(y); // calls default implementation +} diff --git a/code_examples/theory/adl_shutdown_code.h b/code_examples/theory/adl_shutdown_code.h index 4a93428..ef6ac20 100644 --- a/code_examples/theory/adl_shutdown_code.h +++ b/code_examples/theory/adl_shutdown_code.h @@ -9,5 +9,5 @@ constexpr inline auto some_func = [](const auto&) {}; void calling_site() { Custom::X x; some_func(x); // calls ::some_func - // Because ::some_func prevents ADL, Custom::some_func cannot be called. + // Because ADL is skipped, Custom::some_func cannot be called. } From e7e556ca88373c3499ff944bc5dc3c8a79bc5791 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:34:39 +0200 Subject: [PATCH 24/44] Updates from Overleaf --- chapters/00_title_page.tex | 2 ++ chapters/90_theory.tex | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index 3e0aaf1..ce7d96f 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -30,6 +30,8 @@ Original copy of this book can be obtained at \href{https://github.com/HappyCerberus/book-cpp-algorithms}{https://github.com/HappyCerberus/book-cpp-algorithms}. +The book is also available through LeanPub where the proceeds go to Electronic Frontier Foundation (after LeanPub takes their cut) \href{https://leanpub.com/cpp-algorithms-guide}{https://leanpub.com/cpp-algorithms-guide}. + This copy of the book is version \version. \vfill diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index ef15732..186f7f6 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -58,4 +58,10 @@ \subsection{Function objects vs ADL} \footnotesize Example demonstrating a non-function object preventing ADL, making a friend function impossible to invoke. \tcblower \cppfile{code_examples/theory/adl_shutdown_code.h} -\end{box-note} \ No newline at end of file +\end{box-note} + +\subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customization point}} + +With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. + +This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. \ No newline at end of file From a5ab43e6241621790e0296964362e169bb28112a Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:36:21 +0200 Subject: [PATCH 25/44] Formatting fix --- code_examples/theory/adl_niebloid_code.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code_examples/theory/adl_niebloid_code.h b/code_examples/theory/adl_niebloid_code.h index 5552b66..0b90065 100644 --- a/code_examples/theory/adl_niebloid_code.h +++ b/code_examples/theory/adl_niebloid_code.h @@ -5,7 +5,10 @@ namespace impl { struct DoSomethingFn { template void operator()(T&& arg) const - requires HasCustomImpl { do_something(std::forward(arg)); } + requires HasCustomImpl { + do_something(std::forward(arg)); + } + template void operator()(T&&) const requires (!HasCustomImpl) { /* default implementation */ } }; From ac10beb25a485c1f20ab164a4cf5d361769d9b1e Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:36:43 +0200 Subject: [PATCH 26/44] Updates from Overleaf --- chapters/90_theory.tex | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 186f7f6..3c31311 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -64,4 +64,10 @@ \subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customizati With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. +\begin{box-note} +\footnotesize Example. +\tcblower +\cppfile{code_examples/theory/adl_niebloid_code.h} +\end{box-note} + This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. \ No newline at end of file From cf6f83fb09d2fc32945e506f8ff402b7f41e623c Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 18:38:43 +0200 Subject: [PATCH 27/44] Formatting fix. --- code_examples/theory/adl_niebloid_code.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code_examples/theory/adl_niebloid_code.h b/code_examples/theory/adl_niebloid_code.h index 0b90065..422be90 100644 --- a/code_examples/theory/adl_niebloid_code.h +++ b/code_examples/theory/adl_niebloid_code.h @@ -6,8 +6,8 @@ namespace impl { struct DoSomethingFn { template void operator()(T&& arg) const requires HasCustomImpl { - do_something(std::forward(arg)); - } + do_something(std::forward(arg)); + } template void operator()(T&&) const requires (!HasCustomImpl) { /* default implementation */ } From b62ac19023893aff5a6b0eb6ac104849f3ff2d94 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 19:50:59 +0200 Subject: [PATCH 28/44] Version 0.3 update --- book.tex | 8 ++--- chapters/02_introduction.tex | 2 ++ chapters/03_algorithms_02_swaps.tex | 30 ++----------------- chapters/03_algorithms_03_sorting.tex | 17 +++++++---- chapters/03_algorithms_04_partitioning.tex | 4 +-- chapters/03_algorithms_05_divide.tex | 2 +- chapters/03_algorithms_07_sets.tex | 4 +-- chapters/03_algorithms_08_transformations.tex | 6 ++-- .../03_algorithms_10_general_reductions.tex | 4 +-- chapters/03_algorithms_12_generators.tex | 8 ++--- chapters/03_algorithms_13_copies_moves.tex | 4 +-- chapters/03_algorithms_15_heap.tex | 6 ++-- chapters/03_algorithms_16_searching.tex | 2 +- chapters/03_algorithms_17_minmax.tex | 2 +- chapters/04_ranges_in_depth.tex | 8 ++--- chapters/90_theory.tex | 3 +- 16 files changed, 47 insertions(+), 63 deletions(-) diff --git a/book.tex b/book.tex index bff00c6..69bce2d 100644 --- a/book.tex +++ b/book.tex @@ -33,14 +33,14 @@ \frontmatter \input{chapters/00_title_page} -%\input{chapters/01_preface} % Reviewed +\input{chapters/01_preface} % Reviewed \mainmatter \tableofcontents -%\input{chapters/02_introduction} % In review -%\input{chapters/03_algorithms_00_main} % In review -%\input{chapters/04_ranges_in_depth} % In review +\input{chapters/02_introduction} % In review +\input{chapters/03_algorithms_00_main} % In review +\input{chapters/04_ranges_in_depth} % In review \input{chapters/90_theory} % TODO diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 70941f1..5420c20 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -144,6 +144,8 @@ \section{A simpler mental model for iterators} The benefit of thinking about the returned value as the end iterator of a range is that it removes the potential for corner cases. For example, what if the algorithm doesn't find any element out of order? The returned value will be the end iterator of the source range, meaning that the range returned is simply the entire source range. +\newpage + In some cases, a single returned iterator denotes multiple ranges. \begin{box-note} diff --git a/chapters/03_algorithms_02_swaps.tex b/chapters/03_algorithms_02_swaps.tex index ecfabde..a70fe58 100644 --- a/chapters/03_algorithms_02_swaps.tex +++ b/chapters/03_algorithms_02_swaps.tex @@ -1,32 +1,6 @@ \section{Swaps} -Before discussing swaps, we need to make a short detour and discuss argument-dependent lookup, an essential aspect of the pre-C++20 version of the std::swap algorithm. - -Argument-dependent lookup is relied upon heavily in C++, notably when overloading operators. - -\begin{box-note} -\footnotesize Example of argument-dependent lookup for \cpp{operator<<} implemented as a function. -\tcblower -\cppfile{code_examples/theory/adl_code.h} -\end{box-note} - -The situation changes slightly when the function is implemented as a friend function. Such a function is a member of the surrounding namespace. However, it is not visible outside of ADL. - -\begin{box-note} -\footnotesize Example of argument-dependent lookup for \cpp{operator<<} implemented as a friend function. -\tcblower -\cppfile{code_examples/theory/adl_friend_code.h} -\end{box-note} - -The benefit of relying on ADL is that there is no complexity on the caller's site. An unqualified call will invoke the correct overload. Except, this wouldn't be C++ if there weren't an exception to this rule. - -If on top of having the ability to specialize, we also want default behaviour as a fallback, the caller now needs to make sure to pull in the default overload to the local scope. - -\begin{box-note} -\footnotesize Example of argument-dependent lookup with a default templated version of the function. -\tcblower -\cppfile{code_examples/theory/adl_default_code.h} -\end{box-note} +Before C++11 and the introduction of move operations, swaps were the only way objects with value semantics could exchange content without involving a deep copy. \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \index{\cpp{std::swap}} @@ -35,7 +9,7 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \cppversions{\texttt{swap}}{\CC98}{\CC20}{N/A}{\CC20} -Correctly calling swap (as mentioned at the beginning of this section) requires pulling the default std::swap version to the local scope. +Correctly calling swap requires pulling the default std::swap version to the local scope. To read more on why this is needed check out the theory chapter of this book, specifically the section on ADL (\ref{theory:adl}). \begin{box-note} \footnotesize Example of correctly calling \cpp{std::swap}. diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 66f5ae7..82356a3 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -35,7 +35,7 @@ \section{Sorting} \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lexicographical\_compare}}} \index{\cpp{std::lexicographical_compare}} -Lexicographical \texttt{strict\_weak\_ordering} for ranges is exposed through the \texttt{std::lexicographical\_compare} algorithm. +Lexicographical \texttt{strict\_weak\_ordering} for ranges is exposed through the \newline\texttt{std::lexicographical\_compare} algorithm. \cppversions{\texttt{lex\dots compare}}{\CC98}{\CC20}{\CC17}{\CC20} @@ -58,7 +58,14 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lex \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\texttt{std::lexicographical\_compare\_three\_way}}} \index{\cpp{std::lexicographical_compare_three_way}} -The \texttt{std::lexicographical\_compare\_three\_way} is the spaceship operator equivalent to \texttt{std::lexicographical\_compare}. It returns one of \texttt{std::strong\_ordering}, \texttt{std::weak\_ordering} or \texttt{std::partial\_ordering} types, depending on the type returned by the elements' spaceship operator. +The \texttt{std::lexicographical\_compare\_three\_way} is the spaceship operator equivalent to \texttt{std::lexicographical\_compare}. It returns one of: +\begin{itemize} + \item\texttt{std::strong\_ordering} + \item \texttt{std::weak\_ordering} + \item \texttt{std::partial\_ordering} +\end{itemize} + +The type depends on the type returned by the elements' spaceship operator. \cppversions{\texttt{lex\dots three\_way}}{\CC20}{\CC20}{N/A}{N/A} \constraints{\texttt{(input\_range, input\_range)}}{}{\texttt{operator<=>}}{\texttt{strong\_ordering}, \texttt{weak\_ordering}, \texttt{partial\_ordering}} @@ -140,7 +147,7 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\ \end{box-note} Note that because of the behaviour of \cpp{std::is_sorted_until}, the following is always true:\\ -\small\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))} +\begin{small}\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))}\end{small} \subsection{\texorpdfstring{\cpp{std::partial_sort}}{\texttt{std::partial\_sort}}} \index{\cpp{std::partial_sort}} @@ -188,8 +195,8 @@ \subsection{\texorpdfstring{\cpp{qsort}}{\texttt{qsort}} - C standard library} I would strongly recommend avoiding \cpp{qsort}, as \cpp{std::sort} and \cpp{std::ranges::sort} should be a better choice in every situation. Moreover, \cpp{qsort} is only valid for trivially copyable types, and those will correctly optimize to \cpp{memcpy}/\cpp{memmove} operations even when using \cpp{std::sort}. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::sort} to achieve the same result as in the previous example. \tcblower \cppfile{code_examples/algorithms/qsort_not_code.h} -\end{box-note} \ No newline at end of file +\end{box-nobreak} \ No newline at end of file diff --git a/chapters/03_algorithms_04_partitioning.tex b/chapters/03_algorithms_04_partitioning.tex index c1d4e96..26460ce 100644 --- a/chapters/03_algorithms_04_partitioning.tex +++ b/chapters/03_algorithms_04_partitioning.tex @@ -45,11 +45,11 @@ \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitio Note that a sorted range is always partitioned for any possible value (with a different partition point). -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::is_partitioned}. \tcblower \cppfile{code_examples/algorithms/is_partitioned_code.h} -\end{box-note} +\end{box-nobreak} \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_copy}}} \index{\cpp{std::partition_copy}} diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index 911a951..c865974 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -57,7 +57,7 @@ \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_point}}} \index{\cpp{std::partition_point}} -Despite the naming, \cpp{std:partition_point} works very similarly to \cpp{std::upper_bound}, however instead of searching for a particular value, it searches using a predicate. +Despite the naming, \cpp{std:partition_point} works very similarly to \texttt{std::upper\-\_bound}, however instead of searching for a particular value, it searches using a predicate. \cppversions{\texttt{partition\_point}}{\CC11}{\CC20}{N/A}{\CC20} \constraints{\texttt{forward\_range}}{}{N/A}{\texttt{unary\_predicate}} diff --git a/chapters/03_algorithms_07_sets.tex b/chapters/03_algorithms_07_sets.tex index 2fbcbf9..e567d8e 100644 --- a/chapters/03_algorithms_07_sets.tex +++ b/chapters/03_algorithms_07_sets.tex @@ -34,11 +34,11 @@ \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::se For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::abs(M-N)} such elements from the corresponding range. That is, if $M>N$, $M-N$ elements will be copied from the first range; otherwise, $N-M$ elements will be copied from the second range. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::set_symmetric_difference}. \tcblower \cppfile{code_examples/algorithms/set_symmetric_difference_code.h} -\end{box-note} +\end{box-nobreak} \begin{box-note} \footnotesize Example demonstrating \cpp{std::set_symmetric_difference} behaviour when equivalent elements are present. diff --git a/chapters/03_algorithms_08_transformations.tex b/chapters/03_algorithms_08_transformations.tex index 1dbf64d..87b632a 100644 --- a/chapters/03_algorithms_08_transformations.tex +++ b/chapters/03_algorithms_08_transformations.tex @@ -93,7 +93,7 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \cppfile{code_examples/algorithms/reverse_code.h} \end{box-note} -C-style arrays and C-style strings can be adapted using \cpp{std::span} and \cpp{std::string_view} to allow reverse iteration. +C-style arrays and C-style strings can be adapted using \cpp{std::span} and \texttt{std::string\-\_view} to allow reverse iteration. \begin{box-note} \footnotesize Example of using \cpp{std::span} and \cpp{std::string_view} to addapt C-style constructs for reverse iteration. @@ -104,7 +104,7 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \index{\cpp{std::rotate}} -The \cpp{std::rotate} algorithm rearranges elements in the range from \cpp{[first, middle),[middle, last)} to \cpp{[middle, last),[first, middle)}. +The \cpp{std::rotate} algorithm rearranges elements in the range from \texttt{[first, middle),} \texttt{[middle, last)} to \texttt{[middle, last),} \texttt{[first, middle)}. \cppversions{\texttt{rotate}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{}{} @@ -118,7 +118,7 @@ \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \index{\cpp{std::shuffle}} -The \cpp{std::shuffle} algorithm is a successor of the now-defunct \cpp{std::random_shuffle} algorithm (deprecated in \CC14, removed in \CC17) and relies on new random facilities added in \CC11. +The \cpp{std::shuffle} algorithm is a successor of the now-defunct (deprecated in \CC14, removed in \CC17) \cpp{std::random_shuffle} algorithm and relies on new random facilities added in \CC11. \cppversions{\texttt{shuffle}}{\CC11}{N/A}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{}{} diff --git a/chapters/03_algorithms_10_general_reductions.tex b/chapters/03_algorithms_10_general_reductions.tex index 450a5a3..398abe6 100644 --- a/chapters/03_algorithms_10_general_reductions.tex +++ b/chapters/03_algorithms_10_general_reductions.tex @@ -14,7 +14,7 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} \cppversions{\texttt{reduce}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{\texttt{std::plus<>()}}{\texttt{binary\_functor}} -Note that while we have access to a sequenced execution policy (\cpp{std::execution::seq}), this does not make \cpp{std::reduce} sequenced in a left-fold sense. +Note that while we have access to a sequenced execution policy (i.e. \newline\cpp{std::execution::seq}), this does not make \cpp{std::reduce} sequenced in a left-fold sense. \begin{box-note} \footnotesize Example of using \cpp{std::reduce} with and without an execution policy. @@ -35,7 +35,7 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} \subsection{\texorpdfstring{\cpp{std::transform_reduce}}{\texttt{std::transform\_reduce}}} \index{\cpp{std::transform_reduce}} -The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \cpp{std::inner_product}. On top of the two-range variant, the algorithm also provides a unary overload. +The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \texttt{std::inner\-\_product}. On top of the two-range variant, the algorithm also provides a unary overload. \cppversions{\texttt{transform\_reduce}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{N/A}{\texttt{(binary\_functor, unary\_functor)}} diff --git a/chapters/03_algorithms_12_generators.tex b/chapters/03_algorithms_12_generators.tex index 9919ea6..89c2a15 100644 --- a/chapters/03_algorithms_12_generators.tex +++ b/chapters/03_algorithms_12_generators.tex @@ -50,12 +50,12 @@ \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} \cppfile{code_examples/algorithms/iota_code.h} \end{box-note} -Notably, the \cpp{std::iota} algorithm is also an outlier in the support added with the C++20 standard. The std::iota algorithm did not receive a range version. However, we do have access to an iota view. +Notably, the \cpp{std::iota} algorithm is also an outlier in the support added with the C++20 standard. The \cpp{std::iota} algorithm did not receive a range version. However, we do have access to an iota view. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using both finite and infinite \cpp{std::views::iota}. \tcblower \cppfile{code_examples/algorithms/iota_view_code.h} -\end{box-note} +\end{box-nobreak} -Here we take advantage of the finite view constructor \cpp{std::views::iota(1,10)} to establish the output size (line 3), which allows us to use the infinite view \cpp{std::views::iota(5)} for the second parameter. Functionally, we could swap even the second view for a finite one. However, this would impose an additional (and unnecessary) boundary check. +Here we take advantage of the finite view constructor \cpp{std::views::iota(1,10)} to establish the output size (line 3), which allows us to use the infinite view \texttt{std::views\-::iota(5)} for the second parameter. Functionally, we could swap even the second view for a finite one. However, this would impose an additional (and unnecessary) boundary check. diff --git a/chapters/03_algorithms_13_copies_moves.tex b/chapters/03_algorithms_13_copies_moves.tex index a4c6ec1..2b2e5bd 100644 --- a/chapters/03_algorithms_13_copies_moves.tex +++ b/chapters/03_algorithms_13_copies_moves.tex @@ -50,11 +50,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{ The output iterator cannot be within \cpp{(first, last]} and will be treated as the end iterator for the destination range, meaning that the algorithm will write the first value to \cpp{std::prev(end)}. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of a non-overlapping and permitted overlapping case of \cpp{std::copy_backward}. \tcblower \cppfile{code_examples/algorithms/copy_backward_code.h} -\end{box-note} +\end{box-nobreak} \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} \index{\cpp{std::copy_n}} diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index cf690a1..d7dd349 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -20,11 +20,11 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std The \cpp{std::make_heap} algorithm reorders elements in the given range such that the elements maintain the max-heap property, that is, the element at index $i$ compares greater or equal to the elements at indexes $2i+1$ and $2i+2$. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using \cpp{std::make_heap} to construct a max-heap and a min-heap (using a custom comparator). \tcblower \cppfile{code_examples/algorithms/make_heap_code.h} -\end{box-note} +\end{box-nobreak} The \cpp{std::push_heap} and \cpp{std::pop_heap} algorithms simulate push and pop operations for the max-heap data structure. However, because they operate on top of a range, they cannot manipulate the underlying data structure. Therefore, they use the last element of the range as the input/output. @@ -61,7 +61,7 @@ \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\textt \constraints{\texttt{random\_access\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -The two algorithms follow the same logic as \cpp{std::is_sorted} and \cpp{std::is_sorted_until}, returning a boolean and an iterator to the first out-of-order element respectively. +The two algorithms follow the same logic as \cpp{std::is_sorted} and \texttt{std::is\-\_sorted\-\_until}, returning a boolean and an iterator to the first out-of-order element respectively. \begin{box-note} \footnotesize Example of using \cpp{std::is_heap} and \cpp{std::is_heap_until}. diff --git a/chapters/03_algorithms_16_searching.tex b/chapters/03_algorithms_16_searching.tex index 4a385a7..c66e014 100644 --- a/chapters/03_algorithms_16_searching.tex +++ b/chapters/03_algorithms_16_searching.tex @@ -85,7 +85,7 @@ \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std: \index{\cpp{std::find_end}} Both \cpp{std::search} and \cpp{std::find_end} algorithms search for a sub-sequence in a sequence. -The \cpp{std::search} algorithm will return the first instance, and \cpp{std::find_end} will return the last. +The \cpp{std::search} algorithm will return the first instance, and \texttt{std::find\-\_end} will return the last. \cppversions{\texttt{search, find\_end}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator==}}{\texttt{binary\_predicate}} diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index 176b044..3f5952b 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -105,4 +105,4 @@ \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \new \cppfile{code_examples/algorithms/min_element_dangling_code.h} \end{box-note} -All ranged versions of algorithms that return iterators will return the \cpp{std::ranges::dangling} type when invoked on a temporary range. This would preclude the use case of using \cpp{std::span} to sub-reference a range, which is why the range algorithms have an additional concept of a \cpp{borrowed_range}. Such ranges can be passed in as temporaries since they do not own their elements. \ No newline at end of file +All ranged versions of algorithms that return iterators will return the \texttt{std::ranges\-::dangling} type when invoked on a temporary range. This would preclude the use case of using \cpp{std::span} to sub-reference a range, which is why the range algorithms have an additional concept of a \cpp{borrowed_range}. Such ranges can be passed in as temporaries since they do not own their elements. \ No newline at end of file diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex index c99f13b..25ea711 100644 --- a/chapters/04_ranges_in_depth.tex +++ b/chapters/04_ranges_in_depth.tex @@ -14,11 +14,11 @@ \section{Reliance on concepts} First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. -\begin{box-note} +\begin{box-nobreak} \footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. \tcblower \cppfile{code_examples/ranges/concepts_code.h} -\end{box-note} +\end{box-nobreak} Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. @@ -86,7 +86,7 @@ \section{Dangling iterator protection} \cppfile{code_examples/ranges/dangling_code.h} \end{box-note} -User types can declare themselves as borrowed ranges by specializing the \cpp{enable_borrowed_range} constant. +User types can declare themselves as borrowed ranges by specializing the \texttt{enable\-\_borrowed\-\_range} constant. \begin{box-note} \footnotesize Example demonstrating declaring MyView as a borrowed range. @@ -231,7 +231,7 @@ \subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split \index{\cpp{std::views::join_view}} The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. -The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \cpp{std::view::lazy_split} does not, but it does support input ranges. +The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \texttt{std::view::\-lazy\_split} does not, but it does support input ranges. \begin{box-note} \footnotesize Example of using split view to parse a version number. diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 3c31311..76d2238 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -3,6 +3,7 @@ \chapter{Bits of C++ theory} This chapter will dive deep into the various topics referenced throughout the book. While this chapter serves as a reference, the topics are still presented in a heavily simplified, example-heavy format. For a proper reference, please refer to the C++ standard. \section{Argument-dependent lookup (ADL)} +\label{theory:adl} When calling a method without qualification (i.e. not specifying the namespace), the compiler needs to determine the set of candidate functions. As a first step, the compiler will do an unqualified name lookup, which starts at the local scope and examines the parent scopes until it finds the first instance of the name (at which point it stops). @@ -65,7 +66,7 @@ \subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customizati With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. \begin{box-note} -\footnotesize Example. +\footnotesize The concept on line 4 will be satisfied if an ADL call is valid, i.e. there is a custom implementation. This is then used on lines 8 and 13 to differentiate between the two overloads. One calls the custom implementation, and the other contains the default implementation. The inline variable on line 18 is in an inline namespace to prevent collision with friend functions in the same namespace. However, because friend functions are only visible to ADL, a fully qualified call will always invoke this function object. \tcblower \cppfile{code_examples/theory/adl_niebloid_code.h} \end{box-note} From 28b77792141a0e842a412068610de51f36b2aa60 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 28 Sep 2022 20:11:57 +0200 Subject: [PATCH 29/44] Update README.md Update for v0.3.0 release --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 37b767b..e4b1a8e 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.2.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) +[Latest PDF release (v0.3.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.2.1/book_release_v0.2.1.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) ## Changelog +- `0.3.0` New chapter with ADL information and formatting cleanup. - `0.2.1` Fixed page numbering issue, small text changes. - `0.2.0` Added chapter covering C++20 ranges and views. - `0.1.1` Added index and a cover page, small text changes. From de0610961684b0d893909a1c259a3188f711805f Mon Sep 17 00:00:00 2001 From: Simone Gasparini Date: Tue, 27 Dec 2022 18:30:23 +0100 Subject: [PATCH 30/44] [ch03] missing formatting for std::unique --- chapters/03_algorithms_06_linear_sorted.tex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/chapters/03_algorithms_06_linear_sorted.tex b/chapters/03_algorithms_06_linear_sorted.tex index ff5767c..ffbd795 100644 --- a/chapters/03_algorithms_06_linear_sorted.tex +++ b/chapters/03_algorithms_06_linear_sorted.tex @@ -62,7 +62,7 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s \index{\cpp{std::unique}} \index{\cpp{std::unique_copy}} -The std::unique algorithm removes consecutive duplicate values. The typical use case is in conjunction with a sorted range. However, this is not a requirement of std::unique. +The \cpp{std::unique} algorithm removes consecutive duplicate values. The typical use case is in conjunction with a sorted range. However, this is not a requirement of \cpp{std::unique}. \cppversions{\texttt{unique}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\texttt{operator==}}{\texttt{binary\_predicate}} @@ -75,7 +75,7 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s \cppfile{code_examples/algorithms/unique_code.h} \end{box-note} -The \cpp{std::unique_copy} is a variant of std::unique that outputs the unique values to a second range. +The \cpp{std::unique_copy} is a variant of \cpp{std::unique} that outputs the unique values to a second range. \cppversions{\texttt{unique\_copy}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator==}}{\texttt{binary\_predicate}} @@ -84,4 +84,4 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s \footnotesize Example of using \cpp{std::unique_copy}. \tcblower \cppfile{code_examples/algorithms/unique_copy_code.h} -\end{box-note} \ No newline at end of file +\end{box-note} From 19574aa292c3bea3b7d3c0383881cc8633ad2b7c Mon Sep 17 00:00:00 2001 From: japm48 Date: Fri, 30 Dec 2022 23:05:16 +0100 Subject: [PATCH 31/44] disable ligatures in code --- packages/fonts.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/fonts.tex b/packages/fonts.tex index 66fea16..625447d 100644 --- a/packages/fonts.tex +++ b/packages/fonts.tex @@ -28,7 +28,8 @@ UprightFont=*-Regular, BoldFont=*-Bold, ItalicFont=*-Italic, - BoldItalicFont=*-BoldItalic + BoldItalicFont=*-BoldItalic, + Contextuals=AlternateOff ] \renewcommand{\familydefault}{\sfdefault} From 6b185726950dc25ee38d9984c3d549bb14fe8f1b Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 1 Feb 2023 14:53:25 +0000 Subject: [PATCH 32/44] Version 1.0.0 update. --- LICENSE.md | 2 +- book.tex | 48 +-- chapters/00_title_page.tex | 2 +- chapters/01_preface.tex | 12 +- chapters/02_introduction.tex | 44 +-- chapters/03_algorithms_01_foreach.tex | 24 +- chapters/03_algorithms_02_swaps.tex | 21 +- chapters/03_algorithms_03_sorting.tex | 56 ++-- chapters/03_algorithms_04_partitioning.tex | 20 +- chapters/03_algorithms_05_divide.tex | 28 +- chapters/03_algorithms_06_linear_sorted.tex | 24 +- chapters/03_algorithms_07_sets.tex | 60 ++-- chapters/03_algorithms_08_transformations.tex | 87 +++--- chapters/03_algorithms_09_left_folds.tex | 67 ++++- .../03_algorithms_10_general_reductions.tex | 26 +- chapters/03_algorithms_11_boolean.tex | 5 +- chapters/03_algorithms_12_generators.tex | 18 +- chapters/03_algorithms_13_copies_moves.tex | 40 +-- chapters/03_algorithms_14_uninitialized.tex | 21 +- chapters/03_algorithms_15_heap.tex | 24 +- chapters/03_algorithms_16_searching.tex | 48 ++- chapters/03_algorithms_17_minmax.tex | 32 +- chapters/04_ranges_in_depth.tex | 284 ------------------ chapters/04_views_00_main.tex | 2 + chapters/04_views_01_intro_ranges.tex | 116 +++++++ chapters/04_views_02_the_views.tex | 178 +++++++++++ chapters/90_theory.tex | 186 +++++++++++- chapters/XX_index.tex | 1 - chapters/Y4_TODO.tex | 16 - chapters/Y4_making_your_own.tex | 1 - chapters/Y5_parallel_algorithms.tex | 1 - chapters/Y8_extra_topics.tex | 191 +----------- code_examples/algorithms/accumulate_code.h | 6 +- .../algorithms/accumulate_right_code.h | 3 +- .../algorithms/adjacent_difference_code.h | 2 +- .../adjacent_difference_extra_code.h | 2 +- .../algorithms/adjacent_difference_par_code.h | 6 + code_examples/algorithms/adjacent_find_code.h | 2 +- code_examples/algorithms/clamp_code.h | 2 +- code_examples/algorithms/copy_backward_code.h | 2 +- code_examples/algorithms/copy_code.h | 2 +- code_examples/algorithms/copy_if_code.h | 2 +- code_examples/algorithms/copy_n_code.h | 2 +- .../algorithms/equal_with_range_code.h | 4 +- .../algorithms/exclusive_scan_code.h | 2 +- code_examples/algorithms/fill_n_code.h | 2 +- code_examples/algorithms/find_code.h | 3 +- code_examples/algorithms/find_first_of_code.h | 2 +- code_examples/algorithms/find_if_code.h | 2 +- .../algorithms/for_each_code_parallel.h | 3 +- .../algorithms/for_each_code_range.h | 3 +- .../algorithms/for_each_code_simple.h | 3 +- code_examples/algorithms/for_each_n_code.h | 2 +- code_examples/algorithms/generate_code.h | 2 +- code_examples/algorithms/includes_code.h | 3 +- .../algorithms/inclusive_scan_code.h | 2 +- code_examples/algorithms/inner_product_code.h | 2 +- .../algorithms/inner_product_one_code.h | 2 +- code_examples/algorithms/iota_code.h | 2 +- .../algorithms/is_partitioned_code.h | 2 +- .../algorithms/is_sorted_until_code.h | 2 +- .../algorithms/iter_swap_partition_code.h | 2 +- .../algorithms/iter_swap_unique_ptr_code.h | 2 +- .../algorithms/lexicographical_compare_code.h | 2 +- .../lexicographical_compare_three_way_code.h | 6 +- code_examples/algorithms/lower_bound_code.h | 6 +- code_examples/algorithms/make_heap_code.h | 2 +- code_examples/algorithms/merge_code.h | 3 +- code_examples/algorithms/min_max_code.h | 3 +- code_examples/algorithms/minmax_code.h | 2 +- code_examples/algorithms/minmax_extra_code.h | 2 +- code_examples/algorithms/mismatch_code.h | 5 +- code_examples/algorithms/move_code.h | 3 +- code_examples/algorithms/nth_element_code.h | 11 +- code_examples/algorithms/partial_sort_code.h | 2 +- .../algorithms/partial_sort_copy_code.h | 3 +- code_examples/algorithms/partial_sum_code.h | 2 +- code_examples/algorithms/partition_code.h | 2 +- .../algorithms/partition_copy_code.h | 2 +- code_examples/algorithms/push_heap_code.h | 2 +- code_examples/algorithms/qsort_code.h | 2 +- code_examples/algorithms/qsort_not_code.h | 2 +- code_examples/algorithms/reduce_code.h | 5 +- code_examples/algorithms/reduce_noinit_code.h | 2 +- code_examples/algorithms/replace_copy_code.h | 5 +- code_examples/algorithms/reverse_copy_code.h | 2 +- code_examples/algorithms/rotate_copy_code.h | 5 +- code_examples/algorithms/search_code.h | 2 +- code_examples/algorithms/search_n_code.h | 2 +- code_examples/algorithms/searchers_code.h | 4 +- .../algorithms/set_difference_equal_code.h | 3 +- .../algorithms/set_intersection_equal_code.h | 3 +- .../set_symmetric_difference_equal_code.h | 10 +- code_examples/algorithms/set_union_code.h | 2 +- .../algorithms/set_union_equal_code.h | 9 +- code_examples/algorithms/shift_code.h | 7 + code_examples/algorithms/shift_move_code.h | 20 ++ code_examples/algorithms/shuffle_code.h | 3 +- code_examples/algorithms/sort_code.h | 2 +- code_examples/algorithms/sort_heap_code.h | 2 +- .../algorithms/sort_projection_code.h | 2 +- .../algorithms/stable_partition_code.h | 2 +- code_examples/algorithms/stable_sort_code.h | 7 +- code_examples/algorithms/swap_calling_code.h | 2 +- code_examples/algorithms/swap_ranges_code.h | 2 +- .../algorithms/transform_inclusive_code.h | 2 +- .../algorithms/transform_reduce_code.h | 2 +- .../algorithms/uninitialized_constr_code.h | 17 +- .../algorithms/uninitialized_copy_code.h | 3 +- code_examples/ranges/borrowed_optin_code.h | 4 +- code_examples/ranges/projected_type_code.h | 4 +- code_examples/ranges/stateful_code.h | 22 ++ code_examples/theory/deduction_algorithm.h | 8 + code_examples/theory/deduction_concepts.h | 5 + code_examples/theory/floating_point.h | 9 + .../theory/floating_point_ordering.h | 19 ++ .../theory/integral_conversions_different_a.h | 5 + .../theory/integral_conversions_different_b.h | 5 + .../theory/integral_conversions_different_c.h | 5 + .../theory/integral_conversions_problems.h | 9 + .../theory/integral_conversions_same.h | 5 + code_examples/theory/integral_promotions.h | 5 + code_examples/theory/references.h | 15 + code_examples/theory/safe_compare.h | 11 + code_examples/theory/safe_in_range.h | 6 + code_examples/theory/safe_ssize.h | 8 + code_examples/views/elements_code.h | 8 +- code_examples/views/keys_values_code.h | 2 +- packages/color_blocks.tex | 28 -- packages/custom_commands/CC.tex | 4 + packages/custom_commands/ExternalLink.tex | 22 ++ packages/custom_commands/circled.tex | 3 + packages/custom_commands/codebox.tex | 26 ++ .../constraints.tex} | 22 -- .../cpp.tex} | 14 +- packages/custom_commands/cppversions.tex | 18 ++ packages/custom_commands/version.tex | 1 + packages/fonts.tex | 6 +- packages/glossary_and_index.tex | 4 - packages/hyperref.tex | 9 + packages/packages.tex | 20 ++ 141 files changed, 1230 insertions(+), 1054 deletions(-) delete mode 100644 chapters/04_ranges_in_depth.tex create mode 100644 chapters/04_views_00_main.tex create mode 100644 chapters/04_views_01_intro_ranges.tex create mode 100644 chapters/04_views_02_the_views.tex delete mode 100644 chapters/XX_index.tex delete mode 100644 chapters/Y4_TODO.tex delete mode 100644 chapters/Y4_making_your_own.tex delete mode 100644 chapters/Y5_parallel_algorithms.tex create mode 100644 code_examples/algorithms/adjacent_difference_par_code.h create mode 100644 code_examples/algorithms/shift_code.h create mode 100644 code_examples/algorithms/shift_move_code.h create mode 100644 code_examples/ranges/stateful_code.h create mode 100644 code_examples/theory/deduction_algorithm.h create mode 100644 code_examples/theory/deduction_concepts.h create mode 100644 code_examples/theory/floating_point.h create mode 100644 code_examples/theory/floating_point_ordering.h create mode 100644 code_examples/theory/integral_conversions_different_a.h create mode 100644 code_examples/theory/integral_conversions_different_b.h create mode 100644 code_examples/theory/integral_conversions_different_c.h create mode 100644 code_examples/theory/integral_conversions_problems.h create mode 100644 code_examples/theory/integral_conversions_same.h create mode 100644 code_examples/theory/integral_promotions.h create mode 100644 code_examples/theory/references.h create mode 100644 code_examples/theory/safe_compare.h create mode 100644 code_examples/theory/safe_in_range.h create mode 100644 code_examples/theory/safe_ssize.h delete mode 100644 packages/color_blocks.tex create mode 100644 packages/custom_commands/CC.tex create mode 100644 packages/custom_commands/ExternalLink.tex create mode 100644 packages/custom_commands/circled.tex create mode 100644 packages/custom_commands/codebox.tex rename packages/{local.tex => custom_commands/constraints.tex} (55%) rename packages/{code_blocks.tex => custom_commands/cpp.tex} (76%) create mode 100644 packages/custom_commands/cppversions.tex create mode 100644 packages/custom_commands/version.tex delete mode 100644 packages/glossary_and_index.tex create mode 100644 packages/hyperref.tex create mode 100644 packages/packages.tex diff --git a/LICENSE.md b/LICENSE.md index 5dbf94c..6cce485 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -(c) RNDr. Šimon Tóth 2022 (business@simontoth.eu) +(c) RNDr. Šimon Tóth 2022-2023 (business@simontoth.eu) # Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License diff --git a/book.tex b/book.tex index 69bce2d..08e9dee 100644 --- a/book.tex +++ b/book.tex @@ -1,53 +1,21 @@ -%\documentclass[10pt,b5paper,svgnames,table,twoside]{memoir} -%\documentclass[11pt,a4paper,svgnames,table,twoside]{memoir} \documentclass[11pt,svgnames,table,a4paper]{book} -%\usepackage{showframe} -%\checkandfixthelayout - -\newcommand{\version}{0.3.0} -\usepackage{hyperref} -\hypersetup{ - colorlinks=true, - linkcolor=blue, - filecolor=magenta, - urlcolor=cyan, - pdftitle={Standard C++ Algorithms}, - pdfpagemode=FullScreen, - } -\usepackage{enumitem} -\newcommand*\circled[1]{\tikz[baseline=(char.base)]{ - \node[shape=circle,draw,inner sep=1pt] (char) {#1};}} - -\input{packages/fonts} -\input{packages/color_blocks} -\input{packages/code_blocks} -\input{packages/glossary_and_index} -\input{packages/local} - -\title{The 114 C++ standard algorithms\\and other related topics} - -\author{RNDr. Šimon Tóth} -\date{2022} +\input{packages/packages} \begin{document} \frontmatter \input{chapters/00_title_page} - -\input{chapters/01_preface} % Reviewed +\input{chapters/01_preface} \mainmatter \tableofcontents - -\input{chapters/02_introduction} % In review -\input{chapters/03_algorithms_00_main} % In review -\input{chapters/04_ranges_in_depth} % In review - -\input{chapters/90_theory} % TODO + +\input{chapters/02_introduction} +\input{chapters/03_algorithms_00_main} +\input{chapters/04_views_00_main} +\input{chapters/90_theory} \appendix -\input{chapters/XX_index} - -% Section on iterator invalidation +\printindex \backmatter diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index ce7d96f..402505f 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -23,7 +23,7 @@ \parindent 0pt \parskip \baselineskip \vfill -\textcopyright{} 2022 Šimon Tóth \\ +\textcopyright{} 2022-2023 Šimon Tóth \\ All rights reserved. This work may be distributed and/or modified under the conditions of the CC-BY-NC-SA license. diff --git a/chapters/01_preface.tex b/chapters/01_preface.tex index b273196..52d5d3f 100644 --- a/chapters/01_preface.tex +++ b/chapters/01_preface.tex @@ -30,10 +30,16 @@ \section*{Why cc-by-sa-nc} Explicitly, any personal use is permitted. For example, you can read, print, or share the book with your friends. -If you want to use this content where you are unsure whether you fit within the Creative Commons commercial definition\footnote{primarily intended for or directed toward commercial advantage or monetary compensation}, feel free to contact me on \href{https://twitter.com/SimonToth83}{Twitter}, \href{https://cz.linkedin.com/in/simontoth}{LinkedIn} or by \href{mailto:business@simontoth.eu}{email} (my DMs are always open). +If you want to use this content where you are unsure whether you fit within the Creative Commons commercial definition\footnote{primarily intended for or directed toward commercial advantage or monetary compensation}, feel free to contact me on \href{https://hachyderm.io/@simontoth}{Mastodon}, \href{https://cz.linkedin.com/in/simontoth}{LinkedIn} or by \href{mailto:business@simontoth.eu}{email} (my DMs are always open). \section*{Book status} -The book is currently in pre-release. +This book is currently content-complete (upto and including \CC20). -To keep up with the changes, visit the hosting repository: \url{https://github.com/HappyCerberus/book-cpp-algorithms}. Internal changelog will be kept after version 1.0 is released. \ No newline at end of file +To keep up with the changes, visit the hosting repository: \url{https://github.com/HappyCerberus/book-cpp-algorithms}. + +\subsection*{Changelog} + +\begin{enumerate} + \item[1.0] First complete release +\end{enumerate} \ No newline at end of file diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 5420c20..50599c2 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -11,33 +11,33 @@ \section{History of standard \texorpdfstring{\CC}{C++} algorithms} The \CC98 standard introduced most of the algorithms. However, it was the \CC11 standard with its introduction of lambdas that made algorithms worthwhile. Before lambdas, the time investment of writing a custom function object made the usefulness of algorithms dubious. \raggedbottom -\begin{box-nobreak} +\begin{codebox}[]{\href{https://godbolt.org/z/73r9n1WYM}{\ExternalLink}} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a custom function object, calculating the number of elements and their sum. \tcblower \cppfile{code_examples/introduction/history_cc98_code.h} -\end{box-nobreak} +\end{codebox} -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/zv4fWbT3z}{\ExternalLink}} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm with a capturing lambda, calculating the number of elements and their sum. \tcblower \cppfile{code_examples/introduction/history_cc11_code.h} -\end{box-note} +\end{codebox} The \CC17 standard introduced parallel algorithms that provide an easy way to speed up processing with minimal effort. All you need to do is to specify the desired execution model, and the library will take care of parallelizing the execution. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/1nK5944K7}{\ExternalLink}} \footnotesize Example of \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm using unsequenced parallel execution model. Note that counters are now shared state and need to be \cpp{std::atomic}\index{\cpp{std::atomic}} or protected by a \cpp{std::mutex}\index{\cpp{std::mutex}}. \tcblower \cppfile{code_examples/introduction/history_cc17_code.h} -\end{box-note} +\end{codebox} Finally, the \CC20 standard introduced a significant re-design in the form of ranges and views. Range versions of algorithms can now operate on ranges instead of \cpp{begin} and \cpp{end} iterators and views provide lazily evaluated versions of algorithms and utilities. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/57TGnzzxc}{\ExternalLink}} \footnotesize Example of the range version of the \cpp{std::for_each}\index{\cpp{std::for_each}} algorithm. \tcblower \cppfile{code_examples/introduction/history_cc20_code.h} -\end{box-note} +\end{codebox} As of the time of writing, the \CC23 standard is not finalized. However, we already know that it will introduce more range algorithms, more views and the ability to implement custom views. @@ -51,19 +51,19 @@ \section{Iterators and ranges} To reference the entire content of a data structure, we can use the \cpp{begin()} and \cpp{end()} methods that return an iterator to the first element and an iterator one past the last element, respectively. Hence, the range \cpp{[begin, end)} contains all data structure elements. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/Wj4YjE51v}{\ExternalLink}} \footnotesize Example of specifying a range using two iterators. \tcblower \cppfile{code_examples/introduction/iterators_code.h} -\end{box-note} +\end{codebox} Sentinels follow the same idea. However, they do not need to be of an iterator type. Instead, they only need to be comparable to an iterator. The exclusive end of the range is then the first iterator that compares equal to the sentinel. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/qKrMo7scn}{\ExternalLink}} \footnotesize Example of specifying a range using an iterator and custom sentinel. The sentinel will compare \cpp{true} with iterators at least the given distance from the start iterator, therefore defining a range with the specified number of elements. \tcblower \cppfile{code_examples/introduction/sentinels_code.h} -\end{box-note} +\end{codebox} \subsection{Iterator categories} @@ -82,11 +82,11 @@ \subsection{Iterator categories} \textit{arrays, e.g. std::vector}\index{\cpp{std::vector}} \end{description} -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/dcaMEdnoM}{\ExternalLink}} \footnotesize Example demonstrating the difference between a random access iterator provided by \cpp{std::vector} and a bidirectional iterator provided by \cpp{std::list}. \tcblower \cppfile{code_examples/introduction/categories_code.h} -\end{box-note} +\end{codebox} \subsection{Range categories} @@ -128,19 +128,19 @@ \section{A simpler mental model for iterators} Ranges passed in as arguments are usually apparent, typically specified by pair of iterators. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/zTh9rn5E4}{\ExternalLink}} \footnotesize Example with two ranges passed in as an argument. The input range is fully specified, and the end iterator for the output range is implied from the number of elements in the input range. \tcblower \cppfile{code_examples/introduction/mental_range_code.h} -\end{box-note} +\end{codebox} The returned range can also be evident from the semantics of the algorithm. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/escqfWsnE}{\ExternalLink}} \footnotesize Example of \cpp{std::is_sorted_until} that returns an iterator to the first out-of-order element, which can also be thought as the end iterator for a maximal sorted sub-range. \tcblower \cppfile{code_examples/introduction/mental_sorted_code.h} -\end{box-note} +\end{codebox} The benefit of thinking about the returned value as the end iterator of a range is that it removes the potential for corner cases. For example, what if the algorithm doesn't find any element out of order? The returned value will be the end iterator of the source range, meaning that the range returned is simply the entire source range. @@ -148,16 +148,16 @@ \section{A simpler mental model for iterators} In some cases, a single returned iterator denotes multiple ranges. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/YEGKPToz7}{\ExternalLink}} \footnotesize Example of \cpp{std::lower_bound} that splits the range into two sub-ranges. \tcblower \cppfile{code_examples/introduction/mental_two_code.h} -\end{box-note} +\end{codebox} Even when the algorithm returns an iterator to a specific element, it might be worth considering the implied range. -\begin{box-note} +\begin{codebox}[]{\href{https://godbolt.org/z/Er15W6K5W}{\ExternalLink}} \footnotesize Example of \cpp{std::find} establishing a prefix range that doesn't contain the searched-for element. \tcblower \cppfile{code_examples/introduction/mental_find_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index 2acd028..0c7e8b3 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -1,6 +1,6 @@ \section{Introducing the algorithms} -In this chapter, we introduce each of the 114 standard algorithms. The groups of algorithms are arbitrary and mainly introduced for presentation clarity. Therefore, you might correctly argue that a specific algorithm would be better suited to reside in a different group. +In this chapter, we introduce each of the standard algorithms. The groups of algorithms are arbitrary and mainly introduced for presentation clarity. Therefore, you might correctly argue that a specific algorithm would be better suited to reside in a different group. Before we start, we will use the \cpp{std::for_each} and \cpp{std::for_each_n} algorithms to demonstrate this chapter's structure for each algorithm. @@ -38,35 +38,35 @@ \subsection{\texorpdfstring{\cpp{std::for_each}}{\texttt{std::for\_each}}} \circled{4} The C++11 standard introduced the range-based for loop, which mostly replaced the uses of \cpp{std::for_each}. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/b6qEoonno}{\ExternalLink}} \footnotesize Example of a range loop over all elements of a \cpp{std::vector}. \tcblower \cppfile{code_examples/algorithms/for_each_code_range.h} -\end{box-note} +\end{codebox} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/M38M379To}{\ExternalLink}} \footnotesize Example of a \cpp{std::for_each} loop over all elements of a \cpp{std::vector}. \tcblower \cppfile{code_examples/algorithms/for_each_code_simple.h} -\end{box-note} +\end{codebox} However, there are still a few corner cases when using \cpp{std::for_each} is preferable. The first case is straightforward parallelization. Invoking an expensive operation for each element in parallel is trivial with \cpp{std::for_each}. As long as the operations are independent, there is no need for synchronization primitives. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/3bq4xT3G9}{\ExternalLink}} \footnotesize Example of a parallel \cpp{std::for_each} invoking a method on each element independently in parallel. \tcblower \cppfile{code_examples/algorithms/for_each_code_parallel.h} -\end{box-note} +\end{codebox} Second, the range version can provide a more concise and explicit syntax in some cases because of the projection support introduced in C++20. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/sfxWa4T5f}{\ExternalLink}} \footnotesize Example of the range version of \cpp{std::ranges::for_each} utilizing a projection to invoke the method \cpp{getValue()} (line 13) on each element and summing the resulting values using a lambda (line 12). \tcblower \cppfile{code_examples/algorithms/for_each_code_cpp20.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} \index{\cpp{std::for_each_n}} @@ -95,10 +95,12 @@ \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} \circled{4} While \cpp{std::for_each} operates on the entire range, the interval $[begin, end)$, \cpp{std::for_each_n} operates on the range $[first, first+n)$. Importantly, because the algorithm does not have access to the end iterator of the source range, it does no out-of-bounds checking, and it is the responsibility of the caller to ensure that the range $[first, first+n)$ is valid. -\begin{box-note} +\raggedbottom + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1hfje76Yq}{\ExternalLink}} \footnotesize Example demonstrating multiple uses of \cpp{std::for_each_n}. \tcblower \cppfile{code_examples/algorithms/for_each_n_code.h} -\end{box-note} +\end{codebox} Sending invitations to the \texttt{MAIN\_SEATS} top players is done in parallel (lines 4-7). Then all users' scores are stored in chunks of \texttt{PAGE\_SIZE} records (lines 13-15). Note that calculating the remaining number of elements (line 12) and jumping ahead by the number of stored elements (line 17) requires a random access iterator (in this case, provided by \cpp{std::vector}). \ No newline at end of file diff --git a/chapters/03_algorithms_02_swaps.tex b/chapters/03_algorithms_02_swaps.tex index a70fe58..bdf207d 100644 --- a/chapters/03_algorithms_02_swaps.tex +++ b/chapters/03_algorithms_02_swaps.tex @@ -11,12 +11,11 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} Correctly calling swap requires pulling the default std::swap version to the local scope. To read more on why this is needed check out the theory chapter of this book, specifically the section on ADL (\ref{theory:adl}). -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/Yzvzrb4rY}{\ExternalLink}} \footnotesize Example of correctly calling \cpp{std::swap}. \tcblower \cppfile{code_examples/algorithms/swap_calling_code.h} -\end{box-note} -\newpage +\end{codebox} The C++20 rangified version of swap removes this complexity, and it will: @@ -26,11 +25,11 @@ \subsection{\texorpdfstring{\cpp{std::swap}}{\texttt{std::swap}}} \item if the parameters are also not arrays, it will default to a move-swap \end{itemize} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/8GxdMxsan}{\ExternalLink}} \footnotesize Example of specializing and calling \cpp{std::ranges::swap}. \tcblower \cppfile{code_examples/algorithms/swap_range_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::iter_swap}}{\texttt{std::iter\_swap}}} \index{\cpp{std::iter_swap}} @@ -41,19 +40,19 @@ \subsection{\texorpdfstring{\cpp{std::iter_swap}}{\texttt{std::iter\_swap}}} \constraints{(\cpp{forward_iterator}, \cpp{forward_iterator}) (non-range)}{}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/77enEEh4c}{\ExternalLink}} \footnotesize Example demonstrating the use \cpp{std::iter_swap} in a generic two-way partition algorithm. The algorithm uses concepts to constrain the acceptable types of its arguments. \tcblower \cppfile{code_examples/algorithms/iter_swap_partition_code.h} -\end{box-note} +\end{codebox} The range version extends the functionality to other dereferenceable objects. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/bxeP3PPaE}{\ExternalLink}} \footnotesize Example demonstrating the use of range version of \cpp{std::ranges::iter_swap} to swap the values pointed to by two instances of \cpp{std::unique_ptr}. \tcblower \cppfile{code_examples/algorithms/iter_swap_unique_ptr_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::swap_ranges}}{\texttt{std::swap\_ranges}}} \index{\cpp{std::swap_ranges}} @@ -62,8 +61,8 @@ \subsection{\texorpdfstring{\cpp{std::swap_ranges}}{\texttt{std::swap\_ranges}}} \cppversions{\texttt{swap\_ranges}}{\CC98}{\CC20}{\CC17}{\CC20} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aEPe66f1E}{\ExternalLink}} \footnotesize Example of swapping the first three elements of an array with the last three elements using \cpp{std::swap_ranges}. Note the reversed order of elements due to the use of \cpp{rbegin}. \tcblower \cppfile{code_examples/algorithms/swap_ranges_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 82356a3..4351d54 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -14,11 +14,11 @@ \section{Sorting} Since C++20 introduced the spaceship operator, user-defined types can easily access the default version of lexicographical ordering. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/7PjY8fc1G}{\ExternalLink}} \footnotesize Example of three approaches to implementing lexicographical comparison for a custom type. \tcblower \cppfile{code_examples/theory/comparable_code.h} -\end{box-note} +\end{codebox} The default lexicographical ordering (line 14) works recursively. It starts with the object’s bases first, left-to-right, depth-first and then non-static members in declaration order (processing arrays element by element, left-to-right). @@ -41,19 +41,19 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare}}{\texttt{std::lex \constraints{(\cpp{input_range}, \cpp{input_range})}{(\cpp{forward_range}, \cpp{forward_range})}{\cpp{operator<}}{\cpp{strict_weak_ordering}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/YGW4ET1oa}{\ExternalLink}} \footnotesize Example of using \cpp{lexicographical_compare} and the built-in less than operator to compare vectors of integers. \tcblower \cppfile{code_examples/algorithms/lexicographical_compare_code.h} -\end{box-note} +\end{codebox} Because the standard containers already offer a built-in lexicographical comparison, the algorithm mainly finds use for comparing raw C arrays and in cases when we need to specify a custom comparator. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/djs6TGsEs}{\ExternalLink}} \footnotesize Example of using \cpp{lexicographical_compare} for C-style arrays and customizing the comparator. \tcblower \cppfile{code_examples/algorithms/lexicographical_compare_useful_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\texttt{std::lexicographical\_compare\_three\_way}}} \index{\cpp{std::lexicographical_compare_three_way}} @@ -70,11 +70,11 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\textt \cppversions{\texttt{lex\dots three\_way}}{\CC20}{\CC20}{N/A}{N/A} \constraints{\texttt{(input\_range, input\_range)}}{}{\texttt{operator<=>}}{\texttt{strong\_ordering}, \texttt{weak\_ordering}, \texttt{partial\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vrEqPaEEz}{\ExternalLink}} \footnotesize Example of using \cpp{std::lexicographical_compare_three_way}. \tcblower \cppfile{code_examples/algorithms/lexicographical_compare_three_way_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} \index{\cpp{std::sort}} @@ -86,19 +86,19 @@ \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} Due to the $O(n*logn)$ complexity guarantee, \cpp{std::sort} only operates on \cpp{random_access} ranges. Notably, \cpp{std::list} offers a method with an approximate $n*logn$ complexity. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vef61TWj9}{\ExternalLink}} \footnotesize Basic example of using \cpp{std::sort} and \cpp{std::list::sort}. \tcblower \cppfile{code_examples/algorithms/sort_code.h} -\end{box-note} +\end{codebox} With C++20, we can take advantage of projections to sort by a method or member: -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/4aGenq9b6}{\ExternalLink}} \footnotesize Example of using a projection in conjunction with a range algorithm. The algorithm will sort the elements based on the values obtained by invoking the method \cpp{value} on each element. \tcblower \cppfile{code_examples/algorithms/sort_projection_code.h} -\end{box-note} +\end{codebox} Before C++14, you would have to fully specify the type of the comparator, i.e. \cpp{std::greater{}}. The type erased variant \cpp{std::greater<>{}} relies on type deduction to determine the parameter types. Projections accept an unary invocable, including pointers to members and member functions. @@ -112,11 +112,11 @@ \subsection{\texorpdfstring{\cpp{std::stable_sort}}{\texttt{std::stable\_sort}}} If additional memory is available, stable\_sort remains $O(n*logn)$. However, if it fails to allocate, it will degrade to an $O(n*logn*logn)$ algorithm. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TKx8qP8bK}{\ExternalLink}} \footnotesize Example of re-sorting a range using \cpp{std::stable_sort}, resulting in a guaranteed order of elements. \tcblower \cppfile{code_examples/algorithms/stable_sort_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_sorted}}{\texttt{std::is\_sorted}}} \index{\cpp{std::is_sorted}} @@ -126,11 +126,11 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted}}{\texttt{std::is\_sorted}}} \cppversions{\texttt{is\_sorted}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\cpp{std::less}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/T3n9bfqdM}{\ExternalLink}} \footnotesize Example of testing a range using \cpp{std::is_sorted}. \tcblower \cppfile{code_examples/algorithms/is_sorted_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\_until}}} \index{\cpp{std::is_sorted_until}} @@ -140,11 +140,11 @@ \subsection{\texorpdfstring{\cpp{std::is_sorted_until}}{\texttt{std::is\_sorted\ \cppversions{\texttt{is\_sorted\_until}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\texttt{std::less}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1dvboE6b1}{\ExternalLink}} \footnotesize Example of testing a range using \cpp{std::is_sorted_until}. \tcblower \cppfile{code_examples/algorithms/is_sorted_until_code.h} -\end{box-note} +\end{codebox} Note that because of the behaviour of \cpp{std::is_sorted_until}, the following is always true:\\ \begin{small}\cpp{std::is_sorted(r.begin(), std::is_sorted_until(r.begin(), r.end()))}\end{small} @@ -160,43 +160,43 @@ \subsection{\texorpdfstring{\cpp{std::partial_sort}}{\texttt{std::partial\_sort} The benefit of using a partial sort is faster runtime — approximately $O(n*logk)$, where k is the number of elements sorted. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/j6xjM4GnT}{\ExternalLink}} \footnotesize Example of using \cpp{std::partial_sort} to sort the first three elements of a range. \tcblower \cppfile{code_examples/algorithms/partial_sort_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::partial_sort_copy}}{\texttt{std::partial\_sort\_copy}}} \index{\cpp{std::partial_sort_copy}} -The \cpp{std::partial_sort_copy} algorithm has the same behaviour as \cpp{std::partial_sort}; however, it does not operate inline. Instead, the algorithm writes the results to a second range. +The \cpp{std::partial_sort_copy} algorithm has the same behaviour as \linebreak\cpp{std::partial_sort}; however, it does not operate inline. Instead, the algorithm writes the results to a second range. \cppversions{\texttt{partial\_sort\_copy}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\cpp{input_range -> random_access_range}}{\cpp{forward_range -> random_access_range}}{\cpp{operator<}}{\texttt{strict\_weak\_ordering}} The consequence of writing output to a second range is that the source range does not have to be mutable nor provide random access. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vjKc5nY31}{\ExternalLink}} \footnotesize Example of using \cpp{std::partial_sort_copy} to iterate over ten integers read from standard input and storing the top three values in sorted order. \tcblower \cppfile{code_examples/algorithms/partial_sort_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{qsort}}{\texttt{qsort}} - C standard library} \index{\cpp{qsort}} Because the C standard library is part of the C++ standard library, we also have access to \cpp{qsort}. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Y4E63aThP}{\ExternalLink}} \footnotesize Example of using \cpp{qsort} to sort an array of integers. \tcblower \cppfile{code_examples/algorithms/qsort_code.h} -\end{box-note} +\end{codebox} -I would strongly recommend avoiding \cpp{qsort}, as \cpp{std::sort} and \cpp{std::ranges::sort} should be a better choice in every situation. Moreover, \cpp{qsort} is only valid for trivially copyable types, and those will correctly optimize to \cpp{memcpy}/\cpp{memmove} operations even when using \cpp{std::sort}. +I would strongly recommend avoiding \cpp{qsort}, as \cpp{std::sort} and \cpp{std::ranges::sort} should be a better choice in every situation. Moreover, \cpp{qsort} is only valid for trivially copyable types, and those will correctly optimize to \cpp{memcpy} / \cpp{memmove} operations even when using \cpp{std::sort}. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nT9GPh8Tq}{\ExternalLink}} \footnotesize Example of using \cpp{std::sort} to achieve the same result as in the previous example. \tcblower \cppfile{code_examples/algorithms/qsort_not_code.h} -\end{box-nobreak} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_04_partitioning.tex b/chapters/03_algorithms_04_partitioning.tex index 26460ce..4d0b1a4 100644 --- a/chapters/03_algorithms_04_partitioning.tex +++ b/chapters/03_algorithms_04_partitioning.tex @@ -12,11 +12,11 @@ \subsection{\texorpdfstring{\cpp{std::partition}}{\texttt{std::partition}}} \cppversions{\texttt{partition}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/9P8eWKfsd}{\ExternalLink}} \footnotesize Example of using \cpp{std::partition} to process exam results. \tcblower \cppfile{code_examples/algorithms/partition_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::stable_partition}}{\texttt{std::stable\_partition}}} \index{\cpp{std::stable_partition}} @@ -29,11 +29,11 @@ \subsection{\texorpdfstring{\cpp{std::stable_partition}}{\texttt{std::stable\_pa \constraints{\texttt{bidirectional\_range}}{\texttt{bidirectional\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/zjeczYqK8}{\ExternalLink}} \footnotesize Example of using \cpp{std::stable_partition} to move selected items to the beginning of a list. \tcblower \cppfile{code_examples/algorithms/stable_partition_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitioned}}} \index{\cpp{std::is_partitioned}} @@ -45,11 +45,11 @@ \subsection{\texorpdfstring{\cpp{std::is_partitioned}}{\texttt{std::is\_partitio Note that a sorted range is always partitioned for any possible value (with a different partition point). -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nKecEhs3b}{\ExternalLink}} \footnotesize Example of using \cpp{std::is_partitioned}. \tcblower \cppfile{code_examples/algorithms/is_partitioned_code.h} -\end{box-nobreak} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_copy}}} \index{\cpp{std::partition_copy}} @@ -59,11 +59,11 @@ \subsection{\texorpdfstring{\cpp{std::partition_copy}}{\texttt{std::partition\_c \cppversions{\texttt{partition\_copy}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> (output\_iterator, output\_iterator)}}{\texttt{forward\_range -> (forward\_iterator, forward\_iterator)}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nzrbczh4j}{\ExternalLink}} \footnotesize Example of using \cpp{std::partition_copy} to copy even elements into one range and odd elements into another range. \tcblower \cppfile{code_examples/algorithms/partition_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::nth_element}}{\texttt{std::nth\_element}}} \index{\cpp{std::nth_element}} @@ -80,8 +80,8 @@ \subsection{\texorpdfstring{\cpp{std::nth_element}}{\texttt{std::nth\_element}}} However, note that the standard only mandates average $O(n)$ complexity, and \cpp{std::nth_element} implementations can have high overhead, so always test to determine which provides better performance for your use case. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/n7T3h5sM8}{\ExternalLink}} \footnotesize Example of using \cpp{std::nth_element}. \tcblower \cppfile{code_examples/algorithms/nth_element_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index c865974..2fc9309 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -24,19 +24,19 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \item if no such element exists, both algorithms return the end iterator \end{itemize} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/cbz464eWe}{\ExternalLink}} \footnotesize Example of using \cpp{std::lower_bound} and \cpp{std::upper_bound} to divide a sorted range into three parts: lower than the bottom threshold, between the bottom and upper threshold and higher than the upper threshold. \tcblower \cppfile{code_examples/algorithms/lower_bound_code.h} -\end{box-note} +\end{codebox} While the algorithms will operate on any \cpp{forward_range}, the logarithmic divide and conquer behaviour is only available for \cpp{random_access_range}. Data structures like \cpp{std::set}, \cpp{std::multiset}, \cpp{std::map} and \cpp{std::multimap} offer their $O(logn)$ implementations of lower and upper bound as methods. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/o9Wdvzno9}{\ExternalLink}} \footnotesize Example of using \cpp{lower_bound} and \cpp{upper_bound} methods on a \cpp{std::multiset}. \tcblower \cppfile{code_examples/algorithms/lower_bound_set_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} \index{\cpp{std::equal_range}} @@ -48,11 +48,11 @@ \subsection{\texorpdfstring{\cpp{std::equal_range}}{\texttt{std::equal\_range}}} Because the lower bound returns the first element for which \texttt{elem >= value} and the upper bound returns the first element for which \texttt{value < elem}, the result is a range \texttt{[lb, ub)} of elements equal to the value. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/bhW73YG1b}{\ExternalLink}} \footnotesize Example of using \cpp{std::equal_range}. \tcblower \cppfile{code_examples/algorithms/equal_range_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_point}}} \index{\cpp{std::partition_point}} @@ -64,11 +64,11 @@ \subsection{\texorpdfstring{\cpp{std::partition_point}}{\texttt{std::partition\_ \cpp{std::partition_point} will return the first element that does not satisfy the provided predicate. This algorithm only requires the range to be partitioned (with respect to the predicate). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/WTzs4zYTq}{\ExternalLink}} \footnotesize Example of using \cpp{std::partition_point}. \tcblower \cppfile{code_examples/algorithms/partition_point_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_search}}} \index{\cpp{std::binary_search}} @@ -80,27 +80,27 @@ \subsection{\texorpdfstring{\cpp{std::binary_search}}{\texttt{std::binary\_searc Using \cpp{std::binary_search} is equivalent to calling \cpp{std::equal_range} and checking whether the returned is non-empty; however, \cpp{std::binary_search} offers a single lookup performance, where \cpp{std::equal_range} does two lookups to determine the lower and upper bounds. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7fP71Kjvx}{\ExternalLink}} \footnotesize Example of using \cpp{std::binary_search} with an equivalent check using \cpp{std::equal_range}. \tcblower \cppfile{code_examples/algorithms/binary_search_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{bsearch} - C standard library}{\texttt{bsearch} - C standard library}} \index{\cpp{bsearch}} From the C standard library, C++ inherits \cpp{bsearch}. This algorithm returns one of the elements equal to the provided key, or \cpp{nullptr} if none such element is found. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/sEEashheY}{\ExternalLink}} \footnotesize Example of using \cpp{bsearch}. \tcblower \cppfile{code_examples/algorithms/bsearch_code.h} -\end{box-note} +\end{codebox} As with \cpp{qsort}, there is effectively no reason to use \cpp{bsearch} in C++ code. Depending on the specific use case, one of the previously mentioned algorithms should be a suitable replacement. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/9ePnd7vGc}{\ExternalLink}} \footnotesize Example demonstrating alternatives to \cpp{bsearch}. \tcblower \cppfile{code_examples/algorithms/bsearch_alternatives_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_06_linear_sorted.tex b/chapters/03_algorithms_06_linear_sorted.tex index ffbd795..d8c8a29 100644 --- a/chapters/03_algorithms_06_linear_sorted.tex +++ b/chapters/03_algorithms_06_linear_sorted.tex @@ -10,11 +10,11 @@ \subsection{\texorpdfstring{\cpp{std::includes}}{\texttt{std::includes}}} \cppversions{\texttt{includes}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/od9oa6xEP}{\ExternalLink}} \footnotesize Example of using \cpp{std::includes} to check whether a string contains all English (lower-case) letters. \tcblower \cppfile{code_examples/algorithms/includes_code.h} -\end{box-note} +\end{codebox} The example uses \cpp{std::iota} to generate the lower-case letters (line 2), which requires the destination vector to be pre-allocated (line 1). @@ -28,19 +28,19 @@ \subsection{\texorpdfstring{\cpp{std::merge}}{\texttt{std::merge}}} The merge operation is stable. Equal elements from the first range will be ordered before equal elements from the second range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fj3rzovKf}{\ExternalLink}} \footnotesize Example of using \cpp{std::merge}. \tcblower \cppfile{code_examples/algorithms/merge_code.h} -\end{box-note} +\end{codebox} The parallel version requires the output to be a forward range (represented by a \cpp{forward_iterator}). Therefore, we cannot use wrappers like \cpp{std::back_inserter} and must preallocate the output range to sufficient capacity. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/oEPTe7GnY}{\ExternalLink}} \footnotesize Example of parallel \cpp{std::merge}. \tcblower \cppfile{code_examples/algorithms/merge_par_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::inplace_merge}}{\texttt{std::inplace\_merge}}} \index{\cpp{std::inplace_merge}} @@ -52,11 +52,11 @@ \subsection{\texorpdfstring{\cpp{std::inplace_merge}}{\texttt{std::inplace\_merg When using the iterator-based interface, the middle iterator (i.e. the iterator to the first element of the second sub-range) is the second argument. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/8Ge81Gojr}{\ExternalLink}} \footnotesize Example of using \cpp{std::inplace_merge}. \tcblower \cppfile{code_examples/algorithms/inplace_merge_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{std::unique}, \texttt{std::unique\_copy}}} \index{\cpp{std::unique}} @@ -69,19 +69,19 @@ \subsection{\texorpdfstring{\cpp{std::unique}, \cpp{std::unique_copy}}{\texttt{s Because unique works in-place and cannot resize the underlying range, it leaves the end of the range with unspecified values and returns an iterator to the beginning of this sub-range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7bcKTfe9b}{\ExternalLink}} \footnotesize Example of using \cpp{std::unique}. \tcblower \cppfile{code_examples/algorithms/unique_code.h} -\end{box-note} +\end{codebox} The \cpp{std::unique_copy} is a variant of \cpp{std::unique} that outputs the unique values to a second range. \cppversions{\texttt{unique\_copy}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TMP7ec6P5}{\ExternalLink}} \footnotesize Example of using \cpp{std::unique_copy}. \tcblower \cppfile{code_examples/algorithms/unique_copy_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_07_sets.tex b/chapters/03_algorithms_07_sets.tex index e567d8e..2393957 100644 --- a/chapters/03_algorithms_07_sets.tex +++ b/chapters/03_algorithms_07_sets.tex @@ -10,19 +10,19 @@ \subsection{\texorpdfstring{\cpp{std::set_difference}}{\texttt{std::set\_differe \cppversions{\texttt{set\_difference}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::max(M-N, 0)} such elements from the first range. - -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/WMz45WbW6}{\ExternalLink}} \footnotesize Example of using \cpp{std::set_difference}. \tcblower \cppfile{code_examples/algorithms/set_difference_code.h} -\end{box-note} +\end{codebox} + +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::max(M-N,0)} such elements from the first range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/YdPnxPh88}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_difference} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_difference_equal_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::set\_symmetric\_difference}}} \index{\cpp{std::set_symmetric_difference}} @@ -32,19 +32,19 @@ \subsection{\texorpdfstring{\cpp{std::set_symmetric_difference}}{\texttt{std::se \cppversions{\texttt{set\_sym\dots diff\dots}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::abs(M-N)} such elements from the corresponding range. That is, if $M>N$, $M-N$ elements will be copied from the first range; otherwise, $N-M$ elements will be copied from the second range. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/PrTn6voGn}{\ExternalLink}} + \footnotesize Example of using \cpp{std::set_symmetric_difference}. + \tcblower + \cppfile{code_examples/algorithms/set_symmetric_difference_code.h} +\end{codebox} -\begin{box-nobreak} -\footnotesize Example of using \cpp{std::set_symmetric_difference}. -\tcblower -\cppfile{code_examples/algorithms/set_symmetric_difference_code.h} -\end{box-nobreak} +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the last \cpp{std::abs(M-N)} such elements from the corresponding range. That is, if $M>N$, $M-N$ elements will be copied from the first range; otherwise, $N-M$ elements will be copied from the second range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7hGacnfWe}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_symmetric_difference} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_symmetric_difference_equal_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::set_union}}{\texttt{std::set\_union}}} \index{\cpp{std::set_union}} @@ -54,19 +54,19 @@ \subsection{\texorpdfstring{\cpp{std::set_union}}{\texttt{std::set\_union}}} \cppversions{\texttt{set\_union}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain $M$ elements from the first range, followed by the last \cpp{std::max(N-M,0)} elements from the second range. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/anvj66jfT}{\ExternalLink}} + \footnotesize Example of using \cpp{std::set_union}. + \tcblower + \cppfile{code_examples/algorithms/set_union_code.h} +\end{codebox} -\begin{box-note} -\footnotesize Example of using \cpp{std::set_union}. -\tcblower -\cppfile{code_examples/algorithms/set_union_code.h} -\end{box-note} +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain $M$ elements from the first range, followed by the last \cpp{std::max(N-M,0)} elements from the second range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/KbqGWbzP6}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_union} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_union_equal_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::set_intersection}}{\texttt{std::set\_intersection}}} \index{\cpp{std::set_intersection}} @@ -76,16 +76,16 @@ \subsection{\texorpdfstring{\cpp{std::set_intersection}}{\texttt{std::set\_inter \cppversions{\texttt{set\_intersection}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_range) -> output\_iterator}}{\texttt{(forward\_range, forward\_range) -> forward\_iterator}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the first \cpp{std::min(M,N)} elements from the first range. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/h3fqn9K6v}{\ExternalLink}} + \footnotesize Example of using \cpp{std::set_intersection}. + \tcblower + \cppfile{code_examples/algorithms/set_intersection_code.h} +\end{codebox} -\begin{box-note} -\footnotesize Example of using \cpp{std::set_intersection}. -\tcblower -\cppfile{code_examples/algorithms/set_intersection_code.h} -\end{box-note} +For equivalent elements, where the first range contains $M$ such elements and the second range contains $N$ such elements, the result will contain the first \cpp{std::min(M,N)} elements from the first range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/seK3YWqTo}{\ExternalLink}} \footnotesize Example demonstrating \cpp{std::set_intersection} behaviour when equivalent elements are present. \tcblower \cppfile{code_examples/algorithms/set_intersection_equal_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_08_transformations.tex b/chapters/03_algorithms_08_transformations.tex index 87b632a..141fb62 100644 --- a/chapters/03_algorithms_08_transformations.tex +++ b/chapters/03_algorithms_08_transformations.tex @@ -9,42 +9,16 @@ \subsection{\texorpdfstring{\cpp{std::transform}}{\texttt{std::transform}}} \cppversions{\texttt{transform}}{\CC98}{\CC20}{\CC17}{\CC20} -% Mention lazy version - \constraints{\texttt{input\_range -> output\_iterator}\newline\texttt{(input\_range, input\_iterator) -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}\newline\texttt{(forward\_range, forward\_iterator) -> forward\_iterator}}{N/A}{\texttt{unary\_functor}\newline\texttt{binary\_functor}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6W38nMh6b}{\ExternalLink}} \footnotesize Example of unary and binary version of \cpp{std::transform}. Note that the output iterator can be one of the input ranges' begin iterator (line 4 and 12). \tcblower \cppfile{code_examples/algorithms/transform_code.h} -\end{box-note} +\end{codebox} Note that \cpp{std::transform} does not guarantee strict left-to-right evaluation. If that is required, use \cpp{std::for_each} instead. -\subsection{\texorpdfstring{\cpp{std::adjacent_difference}}{\texttt{std::adjacent\_difference}}} -\index{\cpp{std::adjacent_difference}} - -The \cpp{std::adjacent_difference} is a numerical algorithm, which might make it seem out of place in this section; however, it is essentially a binary transformation algorithm that operates on adjacent elements of the single source range. - -Unlike \cpp{std::transform}, \cpp{std::adjacent_difference} guarantees left-to-right application. Furthermore, because it operates on input ranges, the algorithm internally stores a copy of the last read value for use as the left operand in the next step. - -\cppversions{\texttt{adjacent\_difference}}{\CC98}{\CC20}{\CC17}{N/A} -\constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator -}}{\texttt{binary\_functor}} - -\begin{box-note} -\footnotesize Example of the default version of \cpp{std::adjacent_difference}, which will calculate the difference of adjacent elements, with the first element copied. -\tcblower -\cppfile{code_examples/algorithms/adjacent_difference_code.h} -\end{box-note} - -\begin{box-note} -\footnotesize Example of more inventive use of \cpp{std::adjacent_difference} to generate the Fibonacci sequence. -\tcblower -\cppfile{code_examples/algorithms/adjacent_difference_extra_code.h} -\end{box-note} - -Note that the \cpp{std::next(data.begin())} for the output range is critical here. The \cpp{std::adjacent_difference} algorithm will read each element only once and remembers the previously read value for the left argument. The \cpp{std::next} ensures that we generate one element ahead of either argument. - \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std::remove}, \texttt{std::remove\_if}}} \index{\cpp{std::remove}} \index{\cpp{std::remove_if}} @@ -56,11 +30,11 @@ \subsection{\texorpdfstring{\cpp{std::remove}, \cpp{std::remove_if}}{\texttt{std \cppversions{\texttt{remove, remove\_if}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/eb56eshTq}{\ExternalLink}} \footnotesize Example of using \cpp{std::remove} and \cpp{std::remove_if}. \tcblower \cppfile{code_examples/algorithms/remove_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::replace}, \cpp{std::replace_if}}{\texttt{std::replace}, \texttt{std::replace\_if}}} \index{\cpp{std::replace}} @@ -71,11 +45,11 @@ \subsection{\texorpdfstring{\cpp{std::replace}, \cpp{std::replace_if}}{\texttt{s \cppversions{\texttt{replace, replace\_if}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aPrWhxxsj}{\ExternalLink}} \footnotesize Example of using \cpp{std::replace} and \cpp{std::replace_if}. \tcblower \cppfile{code_examples/algorithms/replace_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} \index{\cpp{std::reverse}} @@ -87,19 +61,19 @@ \subsection{\texorpdfstring{\cpp{std::reverse}}{\texttt{std::reverse}}} Note that using \cpp{std::reverse} is only a reasonable solution if the range must be mutated because bidirectional ranges already support reverse iteration. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/onsj6dvqK}{\ExternalLink}} \footnotesize Example of using \cpp{std::reverse} and reverse iteration, provided by bidirectional ranges. \tcblower \cppfile{code_examples/algorithms/reverse_code.h} -\end{box-note} +\end{codebox} C-style arrays and C-style strings can be adapted using \cpp{std::span} and \texttt{std::string\-\_view} to allow reverse iteration. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/YW8sEa4c3}{\ExternalLink}} \footnotesize Example of using \cpp{std::span} and \cpp{std::string_view} to addapt C-style constructs for reverse iteration. \tcblower \cppfile{code_examples/extras/span_stringview_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \index{\cpp{std::rotate}} @@ -109,11 +83,36 @@ \subsection{\texorpdfstring{\cpp{std::rotate}}{\texttt{std::rotate}}} \cppversions{\texttt{rotate}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/dP7TMfjKv}{\ExternalLink}} \footnotesize Example of using \cpp{std::rotate}. \tcblower \cppfile{code_examples/algorithms/rotate_code.h} -\end{box-note} +\end{codebox} + +\subsection{\texorpdfstring{\cpp{std::shift_left}, \cpp{std::shift_right}}{\texttt{std::shift\_left}, \texttt{std::shift\_right}}} +\index{\cpp{std::shift_left}} +\index{\cpp{std::shift_right}} + +The \cpp{std::shift_left} and \cpp{std::shift_right} algorithms move elements in the provided range by the specified amount of positions. +However, unlike std::rotate, the shift doesn't wrap around. + +\cppversions{\texttt{shift\_left}}{\CC20}{\CC20}{\CC20}{\CC20} +\cppversions{\texttt{shift\_right}}{\CC20}{\CC20}{\CC20}{\CC20} +\constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{}{} + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/5Tjh3M5Kv}{\ExternalLink}} +\footnotesize Example of using \cpp{std::shift_left} and \cpp{std::shift_right} with a trivially copyable type. +\tcblower +\cppfile{code_examples/algorithms/shift_code.h} +\end{codebox} + +One way to think about these algorithms is that they "make space" for the requested number of new elements. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/sbY3fxYrs}{\ExternalLink}} +\footnotesize Example of using \cpp{std::shift_right} to make space for four new elements. Note that \texttt{'d'} wasn't moved as there is no place to move it to, and in the context of "making space" will be overwritten anyway. +\tcblower +\cppfile{code_examples/algorithms/shift_move_code.h} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \index{\cpp{std::shuffle}} @@ -123,11 +122,11 @@ \subsection{\texorpdfstring{\cpp{std::shuffle}}{\texttt{std::shuffle}}} \cppversions{\texttt{shuffle}}{\CC11}{N/A}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{}{} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/PE3bM8Gof}{\ExternalLink}} \footnotesize Example of using \cpp{std::shuffle} to shuffle a deck of cards. \tcblower \cppfile{code_examples/algorithms/shuffle_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::next_permutation}, \cpp{std::prev_permutation}}{\texttt{std::next\_permutation}, \texttt{std::prev\_permutation}}} \index{\cpp{std::next_permutation}} @@ -139,11 +138,11 @@ \subsection{\texorpdfstring{\cpp{std::next_permutation}, \cpp{std::prev_permutat \cppversions{\texttt{prev\_permutation}}{\CC98}{\CC20}{N/A}{\CC20} \constraints{\texttt{bidirectional\_range}}{}{\texttt{operator <}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/shfGz3brv}{\ExternalLink}} \footnotesize Example of using \cpp{std::next_permutation} to iterate over all permutations of three unique elements. \tcblower \cppfile{code_examples/algorithms/next_permutation_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_permutation}}{\texttt{std::is\_permutation}}} \index{\cpp{std::is_permutation}} @@ -153,8 +152,8 @@ \subsection{\texorpdfstring{\cpp{std::is_permutation}}{\texttt{std::is\_permutat \cppversions{\texttt{is\_permutation}}{\CC11}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/axqfP6aaT}{\ExternalLink}} \footnotesize Example of using \cpp{std::is_permutation} to validate a simple sort implementation. \tcblower \cppfile{code_examples/algorithms/is_permutation_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_09_left_folds.tex b/chapters/03_algorithms_09_left_folds.tex index 61c0da8..d9dc450 100644 --- a/chapters/03_algorithms_09_left_folds.tex +++ b/chapters/03_algorithms_09_left_folds.tex @@ -1,9 +1,13 @@ \section{Left folds} -Left folds are one of the two big groups of numerical algorithms. Folds operate in a strict order, "folding in" one element at a time by evaluating \cpp{acc = fold_op(acc, el)} for each element. For left folds, the direction of operation is left to right. However, we can easily change the direction for bidirectional ranges since those support reverse iteration. +Left folds are one of the two big groups of numerical algorithms. +Folds operate in a strict order, "folding in" one element at a time by evaluating \cpp{acc=fold_op(acc,el)} for each element. +For left folds, the direction of operation is left to right. Because of the strictly linear operation, none of the left-fold algorithms supports a parallel version. +When using numerical algorithms, it's worth understanding the behaviour of numerical types in C++, notably the interactions between silent implicit conversions and template type deduction rules. You can read more on this in the theory chapter in the section \ref{theory:numerics}. + \subsection{\texorpdfstring{\cpp{std::accumulate}}{\texttt{std::accumulate}}} \index{\cpp{std::accumulate}} @@ -12,20 +16,19 @@ \subsection{\texorpdfstring{\cpp{std::accumulate}}{\texttt{std::accumulate}}} \cppversions{\texttt{accumulate}}{\CC98}{\CC20}{N/A}{N/A} \constraints{\texttt{input\_range}}{}{\texttt{operator +}}{\texttt{binary\_functor}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/3decPenW6}{\ExternalLink}} \footnotesize Example of using \cpp{std::accumulate}. \tcblower \cppfile{code_examples/algorithms/accumulate_code.h} -\end{box-note} +\end{codebox} -Note that the algorithm accepts an initial value that also dictates the accumulator type. -% TODO: link to chapter about numerical conversions and promotions +While left folds operate strictly left-to-right, we can mitigate this limitation by utilizing reverse iterators—note, of course, that this requires at least a bidirectional range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6verMGo6P}{\ExternalLink}} \footnotesize Example of using \cpp{std::accumulate} as a right fold. \tcblower \cppfile{code_examples/algorithms/accumulate_right_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::inner_product}}{\texttt{std::inner\_product}}} \index{\cpp{std::inner_product}} @@ -37,21 +40,21 @@ \subsection{\texorpdfstring{\cpp{std::inner_product}}{\texttt{std::inner\_produc The default version uses \cpp{operator*} for the reduction and \cpp{operator+} for the fold operation. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/j9M7eGxnd}{\ExternalLink}} \footnotesize Example of using \cpp{std::inner_product}. \tcblower \cppfile{code_examples/algorithms/inner_product_code.h} -\end{box-note} +\end{codebox} Because the algorithm only uses the ranges as input, there is no issue with overlapping ranges. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1G6Yc3rhn}{\ExternalLink}} \footnotesize Example of using \cpp{std::inner_product} on a single range to calculate sum of absolute differences between elements. \tcblower \cppfile{code_examples/algorithms/inner_product_one_code.h} -\end{box-note} +\end{codebox} -\subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial_sum}}} +\subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial\_sum}}} \index{\cpp{std::partial_sum}} The \cpp{std::partial_sum} algorithm operates as a left fold. However, it doesn't reduce the range into a single value. Instead, the algorithm writes each partial result to the output range. @@ -59,12 +62,44 @@ \subsection{\texorpdfstring{\cpp{std::partial_sum}}{\texttt{std::partial_sum}}} \cppversions{\texttt{partial\_sum}}{\CC98}{\CC20}{N/A}{N/A} \constraints{\texttt{input\_range -> output\_iterator}}{}{\texttt{operator+}}{\texttt{binary\_functor}} -The output iterator is permitted to be the input ranges’ begin iterator. +The output iterator is permitted to be the input ranges' begin iterator. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/G83s3edax}{\ExternalLink}} \footnotesize Example of using \cpp{std::partial_sum}. \tcblower \cppfile{code_examples/algorithms/partial_sum_code.h} -\end{box-note} +\end{codebox} + +\subsection{\texorpdfstring{\cpp{std::adjacent_difference}}{\texttt{std::adjacent\_difference}}} +\index{\cpp{std::adjacent_difference}} + +The \cpp{std::adjacent_difference} is a numerical algorithm, which is the odd one out as it provides both a strict left-to-right variant, but at the same time also supports parallel execution, where it behaves like a generalized reduction (we will talk about those in the next section). + +The algorithm operates similarly to the \cpp{std::transform} algorithm, operating on each pair of consecutive elements in the range. However, unlike \cpp{std::transform}, it does guarantee left-to-right execution. -% TODO: stateful functors \ No newline at end of file +The algorithm also supports input ranges, which is achieved by internally storing the copy of the last right operand to be reused as the left operand for the subsequent reduction step. + +\cppversions{\texttt{adjacent\_difference}}{\CC98}{\CC20}{\CC17}{N/A} +\constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{operator -}}{\texttt{binary\_functor}} + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6vPea76qr}{\ExternalLink}} +\footnotesize Example of the default version of \cpp{std::adjacent_difference}, which will calculate the difference of adjacent elements, with the first element copied. +\tcblower +\cppfile{code_examples/algorithms/adjacent_difference_code.h} +\end{codebox} + +The left-to-right operation can be exploited for generative use cases. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fn18zEP4e}{\ExternalLink}} +\footnotesize Example of more inventive use of \cpp{std::adjacent_difference} to generate the Fibonacci sequence. +\tcblower +\cppfile{code_examples/algorithms/adjacent_difference_extra_code.h} +\end{codebox} + +For parallel overloads, the input and output ranges cannot overlap. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7hYj7qaTb}{\ExternalLink}} +\footnotesize Example of the parallel \cpp{std::adjacent_difference} overload. +\tcblower +\cppfile{code_examples/algorithms/adjacent_difference_par_code.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_10_general_reductions.tex b/chapters/03_algorithms_10_general_reductions.tex index 398abe6..6c9bff3 100644 --- a/chapters/03_algorithms_10_general_reductions.tex +++ b/chapters/03_algorithms_10_general_reductions.tex @@ -16,36 +16,36 @@ \subsection{\texorpdfstring{\cpp{std::reduce}}{\texttt{std::reduce}}} Note that while we have access to a sequenced execution policy (i.e. \newline\cpp{std::execution::seq}), this does not make \cpp{std::reduce} sequenced in a left-fold sense. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qxWsoM4Mf}{\ExternalLink}} \footnotesize Example of using \cpp{std::reduce} with and without an execution policy. \tcblower \cppfile{code_examples/algorithms/reduce_code.h} -\end{box-note} +\end{codebox} On top of the \cpp{std::accumulate} equivalents, we get one more overload that does away with the initial accumulator value, which removes the potential for using the wrong literal. Instead, the accumulator will be of the type of the ranges’ elements and will be value initialised. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/5PoEcEvd7}{\ExternalLink}} \footnotesize Example of using \cpp{std::reduce} without specifying the initial value of the accumulator. \tcblower \cppfile{code_examples/algorithms/reduce_noinit_code.h} -\end{box-note} +\end{codebox} The initial value of the accumulator will be “Quack” (line 2). Adding the other two ducks (line 8), we end up with “QuackQuackQuack”. \subsection{\texorpdfstring{\cpp{std::transform_reduce}}{\texttt{std::transform\_reduce}}} \index{\cpp{std::transform_reduce}} -The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \texttt{std::inner\-\_product}. On top of the two-range variant, the algorithm also provides a unary overload. +The \cpp{std::transform_reduce} algorithm is the generalised counterpart to \linebreak\cpp{std::inner_product}. On top of the two-range variant, the algorithm also provides a unary overload. \cppversions{\texttt{transform\_reduce}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{N/A}{\texttt{(binary\_functor, unary\_functor)}} \constraints{\texttt{(input\_range, input\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{\texttt{(std::plus<>(), std::multiplies<>())}}{\texttt{(binary\_functor, binary\_functor)}} -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/P8ohM19bW}{\ExternalLink}} \footnotesize Example of using the unary version of \cpp{std::transform_reduce} to calculate a sum of squares and the binary version to calculate a sum of elements multiplied by coefficients. \tcblower \cppfile{code_examples/algorithms/transform_reduce_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::inclusive_scan}, \cpp{std::exclusive_scan}}{\texttt{std::inclusive\_scan}, \texttt{std::exclusive\_scan}}} \index{\cpp{std::inclusive_scan}} @@ -60,19 +60,19 @@ \subsection{\texorpdfstring{\cpp{std::inclusive_scan}, \cpp{std::exclusive_scan} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{\texttt{std::plus<>()}}{\texttt{binary\_functor}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/3hKaoczE3}{\ExternalLink}} \footnotesize Example of using \cpp{std::inclusive_scan}. \tcblower \cppfile{code_examples/algorithms/inclusive_scan_code.h} -\end{box-note} +\end{codebox} Consequently, because the first element generated by \cpp{std::exclusive_scan} is the sum of zero elements, we must specify an initial value of the accumulator, which will be the value of the first generated element. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/eEc1PTv8G}{\ExternalLink}} \footnotesize Example of using \cpp{std::exclusive_scan}. \tcblower \cppfile{code_examples/algorithms/exclusive_scan_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::transform_inclusive_scan},\newline\cpp{std::transform_exclusive_scan}}{\texttt{std::transform\_inclusive\_scan},\newline \texttt{std::transform\_exclusive\_scan}}} \index{\cpp{std::transform_inclusive_scan}} @@ -84,8 +84,8 @@ \subsection{\texorpdfstring{\cpp{std::transform_inclusive_scan},\newline\cpp{std \cppversions{\texttt{transform\_exclu\dots}}{\CC17}{\CC20}{\CC17}{N/A} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{N/A}{\texttt{(binary\_functor, unary\_functor)}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/oxdjMjY1c}{\ExternalLink}} \footnotesize Example of using \cpp{std::transform_inclusive_scan} to accumulate absolute values of elements. \tcblower \cppfile{code_examples/algorithms/transform_inclusive_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_11_boolean.tex b/chapters/03_algorithms_11_boolean.tex index f0d1024..2490a8e 100644 --- a/chapters/03_algorithms_11_boolean.tex +++ b/chapters/03_algorithms_11_boolean.tex @@ -35,9 +35,8 @@ \subsection{\texorpdfstring{\cpp{std::all_of}, \cpp{std::any_of}, \cpp{std::none \end{tabular} \end{center} - -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Kf1319e3j}{\ExternalLink}} \footnotesize Example demonstrating all three boolean reduction algorithms. \tcblower \cppfile{code_examples/algorithms/all_of_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_12_generators.tex b/chapters/03_algorithms_12_generators.tex index 89c2a15..f39c298 100644 --- a/chapters/03_algorithms_12_generators.tex +++ b/chapters/03_algorithms_12_generators.tex @@ -15,26 +15,26 @@ \subsection{\texorpdfstring{\cpp{std::fill}, \cpp{std::generate}}{\texttt{std::f The generator provided to \cpp{std::generate} can be a non-regular function since \cpp{std::generate} guarantees strict left-to-right evaluation. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6dnTG7Mqo}{\ExternalLink}} \footnotesize Example of using \cpp{std::fill} and \cpp{std::generate}. \tcblower \cppfile{code_examples/algorithms/generate_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::fill_n}, \cpp{std::generate_n}}{\texttt{std::fill\_n}, \texttt{std::generate\_n}}} \index{\cpp{std::fill_n}} \index{\cpp{std::generate_n}} -The \cpp{std::fill_n} and \cpp{std::generate_n} are variants of \cpp{std::fill} and \cpp{std::generate} that operate on ranges specified using the start iterator and number of elements. This behaviour allows the algorithms to be used with iterator adapters, such as \cpp{std::back_inserter}. +The \cpp{std::fill_n} and \cpp{std::generate_n} are variants of \cpp{std::fill} and \newline\cpp{std::generate} that operate on ranges specified using the start iterator and number of elements. This behaviour allows the algorithms to be used with iterator adapters, such as \cpp{std::back_inserter}. \cppversions{\texttt{fill\_n, generate\_n}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{output\_iterator}}{\texttt{forward\_iterator}}{N/A}{\texttt{generator}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qGcdezEaG}{\ExternalLink}} \footnotesize Example of using \cpp{std::fill_n} and \cpp{std::generate_n}. \tcblower \cppfile{code_examples/algorithms/fill_n_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} \index{\cpp{std::iota}} @@ -44,18 +44,18 @@ \subsection{\texorpdfstring{\cpp{std::iota}}{\texttt{std::iota}}} \cppversions{\texttt{iota}}{\CC11}{\CC20}{N/A}{\CC23} \constraints{\texttt{forward\_range}}{}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/19WaoodvM}{\ExternalLink}} \footnotesize Example of using \cpp{std::iota}. \tcblower \cppfile{code_examples/algorithms/iota_code.h} -\end{box-note} +\end{codebox} Notably, the \cpp{std::iota} algorithm is also an outlier in the support added with the C++20 standard. The \cpp{std::iota} algorithm did not receive a range version. However, we do have access to an iota view. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qnz9hWxh8}{\ExternalLink}} \footnotesize Example of using both finite and infinite \cpp{std::views::iota}. \tcblower \cppfile{code_examples/algorithms/iota_view_code.h} -\end{box-nobreak} +\end{codebox} Here we take advantage of the finite view constructor \cpp{std::views::iota(1,10)} to establish the output size (line 3), which allows us to use the infinite view \texttt{std::views\-::iota(5)} for the second parameter. Functionally, we could swap even the second view for a finite one. However, this would impose an additional (and unnecessary) boundary check. diff --git a/chapters/03_algorithms_13_copies_moves.tex b/chapters/03_algorithms_13_copies_moves.tex index 2b2e5bd..6e6534d 100644 --- a/chapters/03_algorithms_13_copies_moves.tex +++ b/chapters/03_algorithms_13_copies_moves.tex @@ -15,27 +15,27 @@ \subsection{\texorpdfstring{\cpp{std::copy}, \cpp{std::move}}{\texttt{std::copy} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/d5c855Gcv}{\ExternalLink}} \footnotesize Example of a non-overlapping and permitted overlapping case of \cpp{std::copy}. \tcblower \cppfile{code_examples/algorithms/copy_code.h} -\end{box-note} +\end{codebox} Move operates identically, except it casts each element to an rvalue before the assignment, turning copies into moves. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/cP1P3e6q3}{\ExternalLink}} \footnotesize Example of using \cpp{std::move}. \tcblower \cppfile{code_examples/algorithms/move_code.h} -\end{box-note} +\end{codebox} Significantly, whether std::move will move depends on the underlying element type. If the underlying type is copy-only, std::move will behave identically to std::copy. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/9ejzxEr8K}{\ExternalLink}} \footnotesize Example of using \cpp{std::move} with a copy-only type. \tcblower \cppfile{code_examples/algorithms/move_nomove_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{\texttt{std::copy\_backward}, \texttt{std::move\_backward}}} \index{\cpp{std::copy_backward}} @@ -50,11 +50,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_backward}, \cpp{std::move_backward}}{ The output iterator cannot be within \cpp{(first, last]} and will be treated as the end iterator for the destination range, meaning that the algorithm will write the first value to \cpp{std::prev(end)}. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TThv49Gqa}{\ExternalLink}} \footnotesize Example of a non-overlapping and permitted overlapping case of \cpp{std::copy_backward}. \tcblower \cppfile{code_examples/algorithms/copy_backward_code.h} -\end{box-nobreak} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} \index{\cpp{std::copy_n}} @@ -66,11 +66,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_n}}{\texttt{std::copy\_n}}} The algorithm cannot check whether the requested count is valid and does not go out of bounds, so this burden is on the caller. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/KGYab3WEr}{\ExternalLink}} \footnotesize Example of using \cpp{std::copy_n}. \tcblower \cppfile{code_examples/algorithms/copy_n_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::copy_if}, \cpp{std::remove_copy}, \cpp{std::remove_copy_if}}{\texttt{std::copy\_if}, \texttt{std::remove\_copy}, \texttt{std::remove\_copy\_if}}} \index{\cpp{std::copy_if}} @@ -87,11 +87,11 @@ \subsection{\texorpdfstring{\cpp{std::copy_if}, \cpp{std::remove_copy}, \cpp{std The \cpp{std::remove_copy} algorithm will copy elements that do not match the provided value. The \cpp{std::copy_if} and \cpp{std::remove_copy_if} algorithms will copy elements based on a predicate, with \cpp{std::copy_if} copying elements for which the predicate returns true and \cpp{std::remove_copy_if} copying elements for which the predicate returns false. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/vY6bTcqsr}{\ExternalLink}} \footnotesize Example demonstrating differences between \cpp{std::copy_if}, \cpp{std::remove_copy} and \cpp{std::remove_copy_if}. \tcblower \cppfile{code_examples/algorithms/copy_if_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::sample}}{\texttt{std::sample}}} \index{\cpp{std::sample}} @@ -103,11 +103,11 @@ \subsection{\texorpdfstring{\cpp{std::sample}}{\texttt{std::sample}}} The two domains of this algorithm are due to the stable nature of the sampling, maintaining the order of elements from the source range. This feature requires either the input range to be at least a forward range or the destination range needs to be a random-access range. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/75WPEfjM9}{\ExternalLink}} \footnotesize Example of using \cpp{std::sample}. \tcblower \cppfile{code_examples/algorithms/sample_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::replace_copy}, \cpp{std::replace_copy_if}}{\texttt{std::replace\_copy}, \texttt{std::replace\_copy\_if}}} \index{\cpp{std::replace_copy}} @@ -121,11 +121,11 @@ \subsection{\texorpdfstring{\cpp{std::replace_copy}, \cpp{std::replace_copy_if}} \cppversions{\texttt{replace\_copy\_if}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{input\_range -> output\_iterator}}{\texttt{forward\_range -> forward\_iterator}}{N/A}{\texttt{unary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TPTq4q8rr}{\ExternalLink}} \footnotesize Example of using \cpp{std::replace_copy} and \cpp{std::replace_copy_if}. \tcblower \cppfile{code_examples/algorithms/replace_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::reverse_copy}}{\texttt{std::reverse\_copy}}} \index{\cpp{std::reverse_copy}} @@ -137,11 +137,11 @@ \subsection{\texorpdfstring{\cpp{std::reverse_copy}}{\texttt{std::reverse\_copy} Not to be confused with the \cpp{std::copy_backwards}, which copies elements in the original order. The \cpp{std::reverse_copy} does not permit the source, and the destination ranges to overlap. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1YM9WevcT}{\ExternalLink}} \footnotesize Example of using \cpp{std::reverse_copy}. \tcblower \cppfile{code_examples/algorithms/reverse_copy_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::rotate_copy}}{\texttt{std::rotate\_copy}}} \index{\cpp{std::rotate_copy}} @@ -153,8 +153,8 @@ \subsection{\texorpdfstring{\cpp{std::rotate_copy}}{\texttt{std::rotate\_copy}}} The input and output ranges cannot overlap. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/MWf5q3r9d}{\ExternalLink}} \footnotesize Example of using \cpp{std::rotate_copy}. \tcblower \cppfile{code_examples/algorithms/rotate_copy_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_14_uninitialized.tex b/chapters/03_algorithms_14_uninitialized.tex index 3869cb4..40c42de 100644 --- a/chapters/03_algorithms_14_uninitialized.tex +++ b/chapters/03_algorithms_14_uninitialized.tex @@ -10,16 +10,16 @@ \subsection{\texorpdfstring{\cpp{std::construct_at}, \cpp{std::destroy_at}}{\tex \index{\cpp{std::construct_at}} \index{\cpp{std::destroy_at}} -The \cpp{std::construct_at} and \cpp{std::destroy_at} algorithms will construct/destroy a single element at a given address. If additional arguments are specified, \cpp{std::construct_at} will forward these to the objects’ constructor. +The \cpp{std::construct_at} and \cpp{std::destroy_at} algorithms will construct/destroy a single element at a given address. If additional arguments are specified, \newline\cpp{std::construct_at} will forward these to the objects’ constructor. \cppversions{\texttt{construct\_at}}{\CC20}{\CC20}{N/A}{\CC20} \cppversions{\texttt{destroy\_at}}{\CC17}{\CC20}{N/A}{\CC20} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fKMdoYd5r}{\ExternalLink}} \footnotesize Example of using \cpp{std::create_at} to create a \cpp{std::string} object using the arguments eight and ‘X’, which results in a string filled with eight copies of the X character. \tcblower \cppfile{code_examples/algorithms/create_at_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\cpp{std::uninitialized_value_construct},\newline\cpp{std::uninitialized_fill}, \newline\cpp{std::destroy}}{\texttt{std::uninitialized\_default\_construct},\newline\texttt{std::uninitialized\_value\_construct},\newline\texttt{std::uninitialized\_fill}, \newline\texttt{std::destroy}}} \index{\cpp{std::uninitialized_default_construct}} @@ -36,12 +36,11 @@ \subsection{\texorpdfstring{\cpp{std::uninitialized_default_construct},\newline\ \constraints{\texttt{forward\_range\newline forward\_iterator (counted)}}{}{}{} % Cleanup the counted variant situation -\begin{box-note} -\footnotesize -Example demonstrating the use of the counted variants of the uninitialized construction algorithm and the \cpp{std::destroy_n} algorithms. Note that for \cpp{std::string}, there is no difference between default and value construction. +\begin{codebox}[]{\href{https://compiler-explorer.com/z/snnhY37xM}{\ExternalLink}} +\footnotesize Example demonstrating the use of the counted variants of the uninitialized construction algorithm and the \cpp{std::destroy_n} algorithms. Note that for \cpp{std::string}, there is no difference between default and value construction. \tcblower \cppfile{code_examples/algorithms/uninitialized_constr_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::uninitialized_copy}, \cpp{std::uninitalized_move}}{\texttt{std::uninitialized\_copy}, \texttt{std::uninitalized\_move}}} \index{\cpp{std::uninitialized_copy}} @@ -54,18 +53,18 @@ \subsection{\texorpdfstring{\cpp{std::uninitialized_copy}, \cpp{std::uninitalize \constraints{\texttt{input\_range -> forward\_iterator\newline input\_iterator -> forward\_iterator (counted)}}{\texttt{forward\_range -> forward\_iterator\newline forward\_iterator -> forward\_iterator (counted)}}{}{} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/a9v4Tn46z}{\ExternalLink}} \footnotesize Example of using \cpp{std::uninitialized_copy} and \cpp{std::uninitialized_move}. \tcblower \cppfile{code_examples/algorithms/uninitialized_copy_code.h} -\end{box-note} +\end{codebox} \subsubsection{Transactional behaviour} The main benefit of using the uninitialized memory algorithms is that they correctly handle transactional behaviour. Transactionality is important in cases where the constructor of an object can throw. If one of the objects fails to construct, the algorithms will correctly roll back by destructing already constructed objects. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/6bP3P4ecK}{\ExternalLink}} \footnotesize Example demonstrating the roll-back behaviour of uninitialized algorithms when the third invocation of the constructor throws. Note that the exception is re-thrown after the partial work is rolled back. \tcblower \cppfile{code_examples/algorithms/transactional_code.h} -\end{box-note} +\end{codebox} diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index d7dd349..89526b4 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -20,21 +20,21 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std The \cpp{std::make_heap} algorithm reorders elements in the given range such that the elements maintain the max-heap property, that is, the element at index $i$ compares greater or equal to the elements at indexes $2i+1$ and $2i+2$. -\begin{box-nobreak} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aYeTrofdK}{\ExternalLink}} \footnotesize Example of using \cpp{std::make_heap} to construct a max-heap and a min-heap (using a custom comparator). \tcblower \cppfile{code_examples/algorithms/make_heap_code.h} -\end{box-nobreak} +\end{codebox} The \cpp{std::push_heap} and \cpp{std::pop_heap} algorithms simulate push and pop operations for the max-heap data structure. However, because they operate on top of a range, they cannot manipulate the underlying data structure. Therefore, they use the last element of the range as the input/output. The \cpp{std::push_heap} algorithm will insert the last element of the range into the heap, and \cpp{std::pop_heap} will extract the maximum element to the last position of the range. As previously mentioned, both operations have logarithmic complexity. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/h34Eh5T7a}{\ExternalLink}} \footnotesize Example of using \cpp{std::push_heap} and \cpp{std::pop_heap}. \tcblower \cppfile{code_examples/algorithms/push_heap_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \index{\cpp{std::sort_heap}} @@ -44,11 +44,11 @@ \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \cppversions{\texttt{sort\_heap}}{\CC98}{\CC20}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/odrjGK7xb}{\ExternalLink}} \footnotesize Example of using \cpp{std::sort_heap}. \tcblower \cppfile{code_examples/algorithms/sort_heap_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\texttt{std::is\_heap}, \texttt{std::is\_heap\_until}}} \index{\cpp{std::is_heap}} @@ -63,31 +63,31 @@ \subsection{\texorpdfstring{\cpp{std::is_heap}, \cpp{std::is_heap_until}}{\textt The two algorithms follow the same logic as \cpp{std::is_sorted} and \texttt{std::is\-\_sorted\-\_until}, returning a boolean and an iterator to the first out-of-order element respectively. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/oz41sxfWb}{\ExternalLink}} \footnotesize Example of using \cpp{std::is_heap} and \cpp{std::is_heap_until}. \tcblower \cppfile{code_examples/algorithms/is_heap_code.h} -\end{box-note} +\end{codebox} \subsection{Comparison with \texorpdfstring{\cpp{std::priority_queue}}{\texttt{std::priority\_queue}}} \index{\cpp{std::priority_queue}} As mentioned at the beginning of this section, the heap algorithms provide effectively the same functionality as \cpp{std::priority_queue}. To demonstrate the differences, let's look at two versions of a topk algorithm: an algorithm that takes a range and returns the top k elements in sorted order as a \cpp{std::vector}. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/TvPv3sWsj}{\ExternalLink}} \footnotesize Example of implementing a topk algorithm using a \cpp{std::priority_queue}. \tcblower \cppfile{code_examples/extras/priority_queue_queue_code.h} -\end{box-note} +\end{codebox} The example relies on \CC20 concepts to provide a generic interface and should therefore work with any range on input, returning a \cpp{std::vector} of the same element type. When using the priority queue, we can utilize the simple \cpp{push()} and \cpp{pop()} interface provided (lines 12 and 14). However, extracting all data from the queue is only possible by repeatedly applying \cpp{pop()} until the queue is empty (line 20). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/EYhGcr8qj}{\ExternalLink}} \footnotesize Example of implementing a topk algorithm using heap algorithms. \tcblower \cppfile{code_examples/extras/priority_queue_heap_code.h} -\end{box-note} +\end{codebox} When using the heap algorithms, we need to manually manage the underlying data structure (lines 9-10 and 13–14). However, we do not need to extract the data, and on top of that, we could omit the final \cpp{std::sort_heap} (line 20) if we do not need the top k elements in sorted order. \ No newline at end of file diff --git a/chapters/03_algorithms_16_searching.tex b/chapters/03_algorithms_16_searching.tex index c66e014..53bba73 100644 --- a/chapters/03_algorithms_16_searching.tex +++ b/chapters/03_algorithms_16_searching.tex @@ -1,7 +1,7 @@ \section{Search and compare algorithms} The search and compare category provides straightforward linear (when compared against a single value) and quadratic (when compared against a range) complexity algorithms. - +\raggedbottom \subsection{\texorpdfstring{\cpp{std::find}, \cpp{std::find_if}, \cpp{std::find_if_not}}{\texttt{std::find}, \texttt{std::find\_if}, \texttt{std::find\_if\_not}}} \index{\cpp{std::find}} \index{\cpp{std::find_if}} @@ -14,21 +14,19 @@ \subsection{\texorpdfstring{\cpp{std::find}, \cpp{std::find_if}, \cpp{std::find_ \constraints{\texttt{input\_range}}{\texttt{forward\_range}}{\texttt{operator==} (\texttt{find})}{\texttt{unary\_predicate}} -A typical example of searching by value is finding delimiters: - -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/P993bPz39}{\ExternalLink}} \footnotesize Example of utilizing \cpp{std::find} to find delimiters in a string. \tcblower \cppfile{code_examples/algorithms/find_code.h} -\end{box-note} +\end{codebox} If we want to search for categories of elements, we can use \cpp{std::find_if} and \cpp{std::find_if_not} since these two variants search using a predicate. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/n9vTGsE4T}{\ExternalLink}} \footnotesize Example of utilizing \cpp{std::find_if_not} to find leading and trailing whitespace. \tcblower \cppfile{code_examples/algorithms/find_if_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::adjacent_find}}{\texttt{std::adjacent\_find}}} \index{\cpp{std::adjacent_find}} @@ -40,11 +38,11 @@ \subsection{\texorpdfstring{\cpp{std::adjacent_find}}{\texttt{std::adjacent\_fin If the algorithm finds a pair of elements, it will return an iterator to the first of the two elements (end iterator otherwise). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Paao79a68}{\ExternalLink}} \footnotesize Example of using \cpp{std::adjacent_find} to find the first pair of equal elements and the first pair of elements that sum up to more than ten. \tcblower \cppfile{code_examples/algorithms/adjacent_find_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::search_n}}{\texttt{std::search\_n}}} \index{\cpp{std::search_n}} @@ -56,11 +54,11 @@ \subsection{\texorpdfstring{\cpp{std::search_n}}{\texttt{std::search\_n}}} The interface to \cpp{std::search_n} can be a bit confusing. The algorithm accepts the number of instances and the value to search for as two consecutive arguments, followed by an optional custom comparator function. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/q737jor9n}{\ExternalLink}} \footnotesize Example of using \cpp{std::search_n} to find two consecutive elements equal to $3$, three elements equal to $3$ (in modulo $5$ arithmetic) and finally, two elements equal to $0$. \tcblower \cppfile{code_examples/algorithms/search_n_code.h} -\end{box-note} +\end{codebox} Note that \cpp{std::search_n} is one exception to the \cpp{_n} naming scheme. @@ -74,11 +72,11 @@ \subsection{\texorpdfstring{\cpp{std::find_first_of}}{\texttt{std::find\_first\_ Note that we are shifting from linear search to $O(m*n)$ time complexity since, for each element of the first range, we need to compare it to all elements in the second range (worst case). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/YnTGErxTo}{\ExternalLink}} \footnotesize Example of using \cpp{std::find_first_of}. \tcblower \cppfile{code_examples/algorithms/find_first_of_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std::search}, \texttt{std::find\_end}}} \index{\cpp{std::search}} @@ -90,24 +88,24 @@ \subsection{\texorpdfstring{\cpp{std::search}, \cpp{std::find_end}}{\texttt{std: \cppversions{\texttt{search, find\_end}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(forward\_range, forward\_range)}}{\texttt{(forward\_range, forward\_range)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Gq94oEo9K}{\ExternalLink}} \footnotesize Example of using \cpp{std::search} and \cpp{std::find_end}. \tcblower \cppfile{code_examples/algorithms/search_code.h} -\end{box-note} +\end{codebox} \subsubsection{Searchers} Since \CC17, we also can specify custom searchers for the search algorithm. Apart from the basic one, the standard implements Boyer-Moore and Boyer-Moore-Horspool string searchers that offer different best-case, worst-case and average complexity. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7MKfzoP77}{\ExternalLink}} \footnotesize Example of using \cpp{std::search} with custom searchers. \tcblower \cppfile{code_examples/algorithms/searchers_code.h} \index{\cpp{std::default_searcher}} \index{\cpp{std::boyer_moore_searcher}} \index{\cpp{std::boyer_moore_horspool_searcher}} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::count}, \cpp{std::count_if}}{\texttt{std::count}, \texttt{std::count\_if}}} \index{\cpp{std::count}} @@ -120,11 +118,11 @@ \subsection{\texorpdfstring{\cpp{std::count}, \cpp{std::count_if}}{\texttt{std:: The element searched for can be specified using a value (\cpp{std::count}) or a predicate (\cpp{std::count_if}). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/sP8eqd45j}{\ExternalLink}} \footnotesize Example of using \cpp{std::count} and \cpp{std::count_if}. \tcblower \cppfile{code_examples/algorithms/count_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::equal}, \cpp{std::mismatch}}{\texttt{std::equal}, \texttt{std::mismatch}}} \index{\cpp{std::equal}} @@ -135,29 +133,29 @@ \subsection{\texorpdfstring{\cpp{std::equal}, \cpp{std::mismatch}}{\texttt{std:: \cppversions{\texttt{equal}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/16jEbK3qo}{\ExternalLink}} \footnotesize Example of using \cpp{std::equal}. \tcblower \cppfile{code_examples/algorithms/equal_code.h} -\end{box-note} +\end{codebox} The \cpp{std::mismatch} algorithm behaves exactly like \cpp{std::equal}; however, instead of returning a simple boolean, it returns a pair of iterators denoting the mismatched elements. \cppversions{\texttt{mismatch}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\texttt{(input\_range, input\_iterator)}}{\texttt{(forward\_range, forward\_iterator)}}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/cY4Eo3MYv}{\ExternalLink}} \footnotesize Example of using \cpp{std::mismatch}. \tcblower \cppfile{code_examples/algorithms/mismatch_code.h} -\end{box-note} +\end{codebox} On top of the basic variants, both \cpp{std::equal} and \cpp{std::mismatch} offer a version where both ranges are fully specified (since C++14). These versions can detect mismatches beyond the first range's scope. \constraints{\texttt{(input\_range, input\_range)} since \CC14}{(forward\_range, forward\_range)}{\texttt{operator==}}{\texttt{binary\_predicate}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/f93PY3fPM}{\ExternalLink}} \footnotesize Example of detecting a mismatch beyond the scope of the first range. \tcblower \cppfile{code_examples/algorithms/equal_with_range_code.h} -\end{box-note} \ No newline at end of file +\end{codebox} \ No newline at end of file diff --git a/chapters/03_algorithms_17_minmax.tex b/chapters/03_algorithms_17_minmax.tex index 3f5952b..5dff7fb 100644 --- a/chapters/03_algorithms_17_minmax.tex +++ b/chapters/03_algorithms_17_minmax.tex @@ -4,21 +4,21 @@ \section{Min-Max algorithms} For functions that accept an \cpp{std::initializer_list}, it is worth keeping in mind that \cpp{std::initializer_list} is constructed by copy; its internal array of elements is copy constructed from the listed elements. Therefore we need to be careful when using \cpp{std::initializer_list} outside of compile-time contexts. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/14enPTMbj}{\ExternalLink}} \footnotesize Example demonstrating case when utilizing \cpp{std::initializer_list} leads to excessive copies. \tcblower \cppfile{code_examples/theory/initializer_list_code.h} -\end{box-note} +\end{codebox} In this example, using \cpp{std::initializer_list} leads to six copies (which we count using the static data member \cpp{X::copy_cnt}). Five copies result from passing in the variables \texttt{a} to \texttt{e} into the \cpp{std::initializer_list}, and one is the result of the return from \cpp{std::max}. In rare cases, we can force constness on a mutable entity. If the constness is undesirable, using \cpp{const_cast} to cast away the const is an option. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/64qvaf3cj}{\ExternalLink}} \footnotesize Example demonstrating the valid and invalid uses for \cpp{const_cast}. \tcblower \cppfile{code_examples/theory/const_cast_code.h} -\end{box-note} +\end{codebox} Please remember that when using casts like \cpp{const_cast}, you effectively override the compiler's judgment. Therefore it is entirely up to you to ensure that the given cast is valid. @@ -32,29 +32,29 @@ \subsection{\texorpdfstring{\cpp{std::min}, \cpp{std::max}, \cpp{std::minmax}}{\ \cppversions{\texttt{min, max}}{\CC98}{\CC14}{N/A}{\CC20} \cppversions{\texttt{minmax}}{\CC11}{\CC14}{N/A}{\CC20} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/5GKe13fd5}{\ExternalLink}} \footnotesize Example demonstrating use of \cpp{std::min} and \cpp{std::max}. \tcblower \cppfile{code_examples/algorithms/min_max_code.h} -\end{box-note} +\end{codebox} Capturing the result by value gets a bit more complicated with \cpp{std::minmax}, which returns a \cpp{std::pair} of const references. To avoid dangling references to expired prvalues we must explicitly name the result type. Unfortunately, there is no way to work around this problem when using \cpp{auto} or structured binding. -\begin{box-note} +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/WGahheGfT}{\ExternalLink}} \footnotesize Example demonstrating use of \cpp{std::minmax}. \tcblower \cppfile{code_examples/algorithms/minmax_code.h} -\end{box-note} +\end{codebox} The C++14 and C++20 standards introduced additional variants of the min-max algorithms that return by value. The change to return by value resolves the dangling reference issue while simultaneously introducing the potential for excessive copies. \constraints{\CC14: \texttt{initializer\_list}\newline \CC20 range version: \texttt{input\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/P4r5ze416}{\ExternalLink}} \footnotesize Example of \cpp{std::initializer_list} and range variants of \cpp{std::min}, \cpp{std::max} and \cpp{std::minmax}. \tcblower \cppfile{code_examples/algorithms/minmax_extra_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} \index{\cpp{std::clamp}} @@ -71,11 +71,11 @@ \subsection{\texorpdfstring{\cpp{std::clamp}}{\texttt{std::clamp}}} Because the algorithm accepts its arguments by const reference and returns a const reference, it shares the same issues as the min-max algorithms regarding const correctness and dangling references. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/3fcTvTEfM}{\ExternalLink}} \footnotesize Examples of using \cpp{std::clamp} with prvalues and with lvalues and \cpp{const_cast} to get mutable access to the original variable. \tcblower \cppfile{code_examples/algorithms/clamp_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \newline\cpp{std::minmax_element}}{\texttt{std::min\_element}, \texttt{std::max\_element}, \textCR\texttt{std::minmax\_element}}} @@ -91,18 +91,18 @@ \subsection{\texorpdfstring{\cpp{std::min_element}, \cpp{std::max_element}, \new \constraints{\texttt{forward\_range}}{\texttt{forward\_range}}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/M1v9Yb93K}{\ExternalLink}} \footnotesize Example of using element versions of min-max algorithms. \tcblower \cppfile{code_examples/algorithms/min_element_code.h} -\end{box-note} +\end{codebox} You might be wondering whether we can cause the same dangling reference (iterator) issue with the element versions of min-max algorithms. Fortunately, one very nice feature of C++20 ranges is the protection against precisely this problem. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vq7d3bq1K}{\ExternalLink}} \footnotesize Example demonstrating protection from dangling iterators. \tcblower \cppfile{code_examples/algorithms/min_element_dangling_code.h} -\end{box-note} +\end{codebox} All ranged versions of algorithms that return iterators will return the \texttt{std::ranges\-::dangling} type when invoked on a temporary range. This would preclude the use case of using \cpp{std::span} to sub-reference a range, which is why the range algorithms have an additional concept of a \cpp{borrowed_range}. Such ranges can be passed in as temporaries since they do not own their elements. \ No newline at end of file diff --git a/chapters/04_ranges_in_depth.tex b/chapters/04_ranges_in_depth.tex deleted file mode 100644 index 25ea711..0000000 --- a/chapters/04_ranges_in_depth.tex +++ /dev/null @@ -1,284 +0,0 @@ -\chapter{Introduction to Ranges} - -The \CC20 standard introduced the Ranges library (a.k.a. STLv2), which effectively replaces existing algorithms and facilities. -In this chapter, we will go over the main changes. - -Note that Ranges are one of the features that landed in \CC20 in a partial state. -Despite \CC23 introducing plenty of additional features, we already know that many features will still not make it into \CC23. - -\section{Reliance on concepts} - -The standard has always described what is required of each argument passed to an algorithm. However, this description was purely informative, and the language did not offer first-class tools to enforce these requirements. Because of this, the error messages were often confusing. - -Concepts are a new C++20 language feature that permits library implementors to constrain the arguments of generic code. Concepts are beyond the scope of this book; however, there are two consequences worth noting. - -First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. - -\begin{box-nobreak} -\footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. -\tcblower -\cppfile{code_examples/ranges/concepts_code.h} -\end{box-nobreak} - -Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. - -\begin{minted}[fontsize=\footnotesize]{cpp} -note: candidate: 'template - requires (random_access_iterator<_Iter>) -\end{minted} - -\section{Notion of a Range} - -Non-range algorithms have always operated strictly using iterators. Conceptually, the range of elements has been defined by two iterators \cpp{[first, last)} or when working with all elements from a standard data structure \cpp{[begin, end)}. - -Range algorithms formally define a range as an iterator and a sentinel pair. Moving the sentinel to a different type unlocks the potential to simplify code where an end iterator doesn't naturally map to an element. The standard offers two default sentinel types, \cpp{std::default_sentinel} and \cpp{std::unreachable_sentinel}. - -Apart from simplifying certain use cases, sentinels also allow for infinite ranges and potential performance improvements. - -\begin{box-note} -\footnotesize Example of an infinite range when the data guarantees termination. Using \cpp{std::unreachable_sentinel} causes the boundary check to be optimized-out, removing one comparison from the loop. -\tcblower -\cppfile{code_examples/ranges/infinite_code.h} -\end{box-note} - -We can only use this approach when we have a contextual guarantee that the algorithm will terminate without going out of bounds. However, this removes one of the few instances when algorithms couldn't perform as well as handwritten code. - -And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. - -\begin{box-note} -\footnotesize Example of using the range overload of \cpp{std::sort}. -\tcblower -\cppfile{code_examples/ranges/rangified_code.h} -\end{box-note} - -\section{Projections} - -Each range algorithm comes with an additional argument, the projection. -The projection is effectively a baked-in transform operation applied before an element is passed into the main functor. - -The projection can be any invocable, which includes member pointers. - -\begin{box-note} -\footnotesize Example of using a projection to sort elements of a range based on a computed value from an element method. -\tcblower -\cppfile{code_examples/ranges/projection_code.h} -\end{box-note} - -Because the projection result is only used for the main functor, it does not modify the output of the algorithm except for \cpp{std::ranges::transform}. - -\begin{box-note} -\footnotesize Example of the difference between \cpp{std::ranges::copy_if}, which uses the projection result for the predicate and \cpp{std::ranges::transform}, which uses the projection result as the input for the transformation function (therefore making it affect the output type). -\tcblower -\cppfile{code_examples/ranges/projected_type_code.h} -\end{box-note} - -\section{Dangling iterator protection} - -Because range versions of algorithms provide overloads that accept entire ranges, they open the potential for dangling iterators when users pass in a temporary range. - -However, for some ranges, passing in a temporary is not an issue. To distinguish these two cases and to prevent dangling iterators, the range library has the concepts of a borrowed range and a special type \cpp{std::ranges::dangling}. - -Borrowed ranges are ranges that "borrow" their elements from another range. Primary examples are \cpp{std::string_view} and \cpp{std::span}. For borrowed ranges, the lifetime of the elements is not tied to the lifetime of the range itself. - -\begin{box-note} -\footnotesize Example demonstrating the different handling for a borrowed range \cpp{std::string_view} and a \cpp{std::string}. -\tcblower -\cppfile{code_examples/ranges/dangling_code.h} -\end{box-note} - -User types can declare themselves as borrowed ranges by specializing the \texttt{enable\-\_borrowed\-\_range} constant. - -\begin{box-note} -\footnotesize Example demonstrating declaring MyView as a borrowed range. -\tcblower -\cppfile{code_examples/ranges/borrowed_optin_code.h} -\end{box-note} - -\section{Views} - -One of the core problems with algorithms is that they are not easily composable. As a result, the code using algorithms is often quite verbose and requires additional copies when working with immutable data. - -Views aim to address this problem by providing cheap to copy and move facilities composed at compile-time. - -\begin{box-note} -\footnotesize Example of composing several views. -\tcblower -\cppfile{code_examples/ranges/view_demo_code.h} -\end{box-note} - -In this chapter, we will go over all the views the standard library offers. - -\subsection{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\texttt{std::views::keys}, \texttt{std::views::values}}} -\index{\cpp{std::views::keys}} -\index{\cpp{std::views::values}} - -The \cpp{std::views::keys} and \cpp{std::views::values} operate on ranges of pair-like elements. -The \cpp{std::views::keys} will produce a range of the first elements from each pair. -The \cpp{std::views::values} will produce a range of the second elements from each pair. - -\begin{box-note} -\footnotesize Example of decomposing a \cpp{std::unordered_map} into a view over the keys and a view over the values. -\tcblower -\cppfile{code_examples/views/keys_values_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} -\index{\cpp{std::views::elements}} - -The \cpp{std::views::elements} will produce a range of the Nth elements from a range of tuple-like elements. - -\begin{box-note} -\footnotesize Example of creating element views for each tuple's second and third elements in the source range. -\tcblower -\cppfile{code_examples/views/elements_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::transform}}{\texttt{std::views::transform}}} -\index{\cpp{std::views::transform}} - -The transform view applies a transformation functor to every element of the range. - -\begin{box-note} -\footnotesize Example of using \cpp{std::views::transform} that also changes the base type of the range. -\tcblower -\cppfile{code_examples/views/transform_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} -\index{\cpp{std::views::take}} -\index{\cpp{std::views::take_while}} - -Both views are formed from the leading elements of the source range. In the case of \cpp{std::views::take}, the view consists of the first N elements. In the case of \cpp{std::views::take_while}, the view consists of the sequence of elements for which the predicate evaluates true. - -\begin{box-note} -\footnotesize Example of a view of the first three elements and a view of the leading sequence of odd elements. -\tcblower -\cppfile{code_examples/views/take_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while}}{\texttt{std::views::drop}, \texttt{std::views::drop\_while}}} -\index{\cpp{std::views::drop}} -\index{\cpp{std::views::drop_while}} - -The drop views are the inverse of take views. -The \cpp{std::views::drop} consists of all but the first N elements. -The \cpp{std::views::drop_while} consists of all but the leading sequence of elements for which the predicate evaluates true. - -\begin{box-note} -\footnotesize Example of a view of all but the first three elements and a view skipping over the leading sequence of odd elements. -\tcblower -\cppfile{code_examples/views/drop_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter}}} -\index{\cpp{std::views::filter}} - -The filter view consists of all elements that satisfy the provided predicate. - -\begin{box-note} -\footnotesize Example of a view of even elements. -\tcblower -\cppfile{code_examples/views/filter_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} -\index{\cpp{std::views::reverse}} - -The reverse view is the reverse iteration view for bidirectional ranges. - -\begin{box-note} -\footnotesize Example of a reverse view. -\tcblower -\cppfile{code_examples/views/reverse_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} -\index{\cpp{std::views::counted}} - -The counted view adapts an iterator and number of elements into a view. - -\begin{box-note} -\footnotesize Example of using a counted view to iterate over a subrange. -\tcblower -\cppfile{code_examples/views/counted_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} -\index{\cpp{std::views::common}} - -The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. - -\begin{box-note} -\footnotesize Example of using adapting a view for a non-range algorithm. -\tcblower -\cppfile{code_examples/views/common_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} -\index{\cpp{std::views::all}} - -The all view is a view of all the elements from a range. - -\begin{box-note} -\footnotesize Example of creating a view over all elements. Note that view over all elements is the default. -\tcblower -\cppfile{code_examples/views/all_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \newline\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \textCR\texttt{std::views::join\_view}}} -\index{\cpp{std::views::split}} -\index{\cpp{std::views::lazy_split}} -\index{\cpp{std::views::join_view}} - -The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. -The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \texttt{std::view::\-lazy\_split} does not, but it does support input ranges. - -\begin{box-note} -\footnotesize Example of using split view to parse a version number. -\tcblower -\cppfile{code_examples/views/split_code.h} -\end{box-note} - -The join view flattens a view of ranges. - -\begin{box-note} -\footnotesize Example of using \cpp{std::views::lazy_split} to split a string into tokens and then join them using the \cpp{std::views::join}. -\tcblower -\cppfile{code_examples/views/join_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::empty}, \cpp{std::views::single}}{\texttt{std::views::empty}, \texttt{std::views::single}}} -\index{\cpp{std::views::empty}} -\index{\cpp{std::views::single}} - -The empty view is an empty view (containing no elements), and the single view is a view that contains a single element. -Note that the view owns the single element, which will be copied/moved alongside the view. - -\begin{box-note} -\footnotesize Example of using \cpp{std::views::empty} and \cpp{std::views::single}. -\tcblower -\cppfile{code_examples/views/single_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} -\index{\cpp{std::views::iota}} - -The iota view represents a generated sequence formed by repeatedly incrementing an initial value. - -\begin{box-note} -\footnotesize Example of using the finite and infinite iota view. -\tcblower -\cppfile{code_examples/views/iota_code.h} -\end{box-note} - -\subsection{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} -\index{\cpp{std::views::istream}} - -The istream view provides similar functionality to istream iterator, except in the form of a view. -It represents a view obtained by successively applying the istream input operator. - -\begin{box-note} -\footnotesize Example of using istream view. -\tcblower -\cppfile{code_examples/views/istream_code.h} -\end{box-note} \ No newline at end of file diff --git a/chapters/04_views_00_main.tex b/chapters/04_views_00_main.tex new file mode 100644 index 0000000..f9281d4 --- /dev/null +++ b/chapters/04_views_00_main.tex @@ -0,0 +1,2 @@ +\input{chapters/04_views_01_intro_ranges} +\input{chapters/04_views_02_the_views} \ No newline at end of file diff --git a/chapters/04_views_01_intro_ranges.tex b/chapters/04_views_01_intro_ranges.tex new file mode 100644 index 0000000..d891e71 --- /dev/null +++ b/chapters/04_views_01_intro_ranges.tex @@ -0,0 +1,116 @@ +\chapter{Introduction to Ranges} + +The \CC20 standard introduced the Ranges library (a.k.a. STLv2), which effectively replaces existing algorithms and facilities. +In this chapter, we will go over the main changes. + +Note that Ranges are one of the features that landed in \CC20 in a partial state. +Despite \CC23 introducing plenty of additional features, we already know that many features will still not make it into \CC23. + +\section{Reliance on concepts} + +The standard has always described what is required of each argument passed to an algorithm. However, this description was purely informative, and the language did not offer first-class tools to enforce these requirements. Because of this, the error messages were often confusing. + +Concepts are a new C++20 language feature that permits library implementors to constrain the arguments of generic code. Concepts are beyond the scope of this book; however, there are two consequences worth noting. + +First, the definitions of all concepts are now part of the standard library, e.g. you can look up what exactly it means for a type to be a \cpp{random_access_range} and also use these concepts to constrain your code. +\raggedbottom + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/ajd55nzsr}{\ExternalLink}} +\footnotesize Example of using standard concepts in user code. The function accepts any random access range as the first argument and an output iterator with the same underlying type as the second argument. +\tcblower +\cppfile{code_examples/ranges/concepts_code.h} +\end{codebox} + +Second, error messages now reference unsatisfied constraints instead of reporting an error deep in the library implementation. + +\begin{minted}[fontsize=\footnotesize]{cpp} +note: candidate: 'template + requires (random_access_iterator<_Iter>) +\end{minted} + +\section{Notion of a Range} + +Non-range algorithms have always operated strictly using iterators. Conceptually, the range of elements has been defined by two iterators \cpp{[first, last)} or when working with all elements from a standard data structure \cpp{[begin, end)}. + +Range algorithms formally define a range as an iterator and a sentinel pair. Moving the sentinel to a different type unlocks the potential to simplify code where an end iterator doesn't naturally map to an element. The standard offers two default sentinel types, \cpp{std::default_sentinel} and \cpp{std::unreachable_sentinel}. + +Apart from simplifying certain use cases, sentinels also allow for infinite ranges and potential performance improvements. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6o5M9fqE7}{\ExternalLink}} +\footnotesize Example of an infinite range when the data guarantees termination. Using \cpp{std::unreachable_sentinel} causes the boundary check to be optimized-out, removing one comparison from the loop. +\tcblower +\cppfile{code_examples/ranges/infinite_code.h} +\end{codebox} + +We can only use this approach when we have a contextual guarantee that the algorithm will terminate without going out of bounds. However, this removes one of the few instances when algorithms couldn't perform as well as handwritten code. + +And finally, with the introduction of the range, algorithms now provide range overloads, leading to more concise and easier-to-read code. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/47h5MMKKv}{\ExternalLink}} +\footnotesize Example of using the range overload of \cpp{std::sort}. +\tcblower +\cppfile{code_examples/ranges/rangified_code.h} +\end{codebox} + +\section{Projections} + +Each range algorithm comes with an additional argument, the projection. +The projection is effectively a baked-in transform operation applied before an element is passed into the main functor. + +The projection can be any invocable, which includes member pointers. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/4xvjn8jbc}{\ExternalLink}} +\footnotesize Example of using a projection to sort elements of a range based on a computed value from an element method. +\tcblower +\cppfile{code_examples/ranges/projection_code.h} +\end{codebox} + +Because the projection result is only used for the main functor, it does not modify the output of the algorithm except for \cpp{std::ranges::transform}. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/Tr3zY5YPd}{\ExternalLink}} +\footnotesize Example of the difference between \cpp{std::ranges::copy_if}, which uses the projection result for the predicate and \cpp{std::ranges::transform}, which uses the projection result as the input for the transformation function (therefore making it affect the output type). +\tcblower +\cppfile{code_examples/ranges/projected_type_code.h} +\end{codebox} + +\section{Dangling iterator protection} + +Because range versions of algorithms provide overloads that accept entire ranges, they open the potential for dangling iterators when users pass in a temporary range. + +However, for some ranges, passing in a temporary is not an issue. To distinguish these two cases and to prevent dangling iterators, the range library has the concepts of a borrowed range and a special type \cpp{std::ranges::dangling}. + +Borrowed ranges are ranges that "borrow" their elements from another range. Primary examples are \cpp{std::string_view} and \cpp{std::span}. For borrowed ranges, the lifetime of the elements is not tied to the lifetime of the range itself. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/oY8zYGs7r}{\ExternalLink}} +\footnotesize Example demonstrating the different handling for a borrowed range \cpp{std::string_view} and a \cpp{std::string}. +\tcblower +\cppfile{code_examples/ranges/dangling_code.h} +\end{codebox} + +User types can declare themselves as borrowed ranges by specializing the \texttt{enable\-\_borrowed\-\_range} constant. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Tnh687nxj}{\ExternalLink}} +\footnotesize Example demonstrating declaring MyView as a borrowed range. +\tcblower +\cppfile{code_examples/ranges/borrowed_optin_code.h} +\end{codebox} + +\section{Views} + +One of the core problems with algorithms is that they are not easily composable. As a result, the code using algorithms is often quite verbose and requires additional copies when working with immutable data. + +Views aim to address this problem by providing cheap to copy and move facilities composed at compile-time. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/aM3vxs1f8}{\ExternalLink}} +\footnotesize Example of composing several views. +\tcblower +\cppfile{code_examples/ranges/view_demo_code.h} +\end{codebox} + +It is worth noting that views are stateful and should be treated as stateful lambdas (e.g. for iterator validity and thread safety). + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/fK91qd3M4}{\ExternalLink}} +\footnotesize Example of \cpp{std::views::drop} caching behaviour. +\tcblower +\cppfile{code_examples/ranges/stateful_code.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/04_views_02_the_views.tex b/chapters/04_views_02_the_views.tex new file mode 100644 index 0000000..e4f98e4 --- /dev/null +++ b/chapters/04_views_02_the_views.tex @@ -0,0 +1,178 @@ +\chapter{The views} + +In this chapter, we will go over all the views the standard library offers. + +\section{\texorpdfstring{\cpp{std::views::keys}, \cpp{std::views::values}}{\texttt{std::views::keys}, \texttt{std::views::values}}} +\index{\cpp{std::views::keys}} +\index{\cpp{std::views::values}} + +The \cpp{std::views::keys} and \cpp{std::views::values} operate on ranges of pair-like elements. +The \cpp{std::views::keys} will produce a range of the first elements from each pair. +The \cpp{std::views::values} will produce a range of the second elements from each pair. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/vs1WjT1cM}{\ExternalLink}} +\footnotesize Example of decomposing a \cpp{std::unordered_map} into a view over the keys and a view over the values. +\tcblower +\cppfile{code_examples/views/keys_values_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::elements}}{\texttt{std::views::elements}}} +\index{\cpp{std::views::elements}} + +The \cpp{std::views::elements} will produce a range of the Nth elements from a range of tuple-like elements. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/efjT5o8G8}{\ExternalLink}} +\footnotesize Example of creating element views for each tuple's second and third elements in the source range. +\tcblower +\cppfile{code_examples/views/elements_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::transform}}{\texttt{std::views::transform}}} +\index{\cpp{std::views::transform}} + +The transform view applies a transformation functor to every element of the range. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/qanMb3GqG}{\ExternalLink}} +\footnotesize Example of using \cpp{std::views::transform} that also changes the base type of the range. +\tcblower +\cppfile{code_examples/views/transform_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::take}, \cpp{std::views::take_while}}{\texttt{std::views::take}, \texttt{std::views::take\_while}}} +\index{\cpp{std::views::take}} +\index{\cpp{std::views::take_while}} + +Both views are formed from the leading elements of the source range. In the case of \cpp{std::views::take}, the view consists of the first N elements. In the case of \cpp{std::views::take_while}, the view consists of the sequence of elements for which the predicate evaluates true. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/6vavoW6T3}{\ExternalLink}} +\footnotesize Example of a view of the first three elements and a view of the leading sequence of odd elements. +\tcblower +\cppfile{code_examples/views/take_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::drop}, \cpp{std::views::drop_while}}{\texttt{std::views::drop}, \texttt{std::views::drop\_while}}} +\index{\cpp{std::views::drop}} +\index{\cpp{std::views::drop_while}} + +The drop views are the inverse of take views. +The \cpp{std::views::drop} consists of all but the first N elements. +The \cpp{std::views::drop_while} consists of all but the leading sequence of elements for which the predicate evaluates true. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Yn9PKe1aY}{\ExternalLink}} +\footnotesize Example of a view of all but the first three elements and a view skipping over the leading sequence of odd elements. +\tcblower +\cppfile{code_examples/views/drop_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::filter}}{\texttt{std::views::filter}}} +\index{\cpp{std::views::filter}} + +The filter view consists of all elements that satisfy the provided predicate. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/4hYba6qf3}{\ExternalLink}} +\footnotesize Example of a view of even elements. +\tcblower +\cppfile{code_examples/views/filter_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::reverse}}{\texttt{std::views::reverse}}} +\index{\cpp{std::views::reverse}} + +The reverse view is the reverse iteration view for bidirectional ranges. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/eY7ohjGTM}{\ExternalLink}} +\footnotesize Example of a reverse view. +\tcblower +\cppfile{code_examples/views/reverse_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::counted}}{\texttt{std::views::counted}}} +\index{\cpp{std::views::counted}} + +The counted view adapts an iterator and number of elements into a view. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/rcfe69aEo}{\ExternalLink}} +\footnotesize Example of using a counted view to iterate over a subrange. +\tcblower +\cppfile{code_examples/views/counted_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::common}}{\texttt{std::views::common}}} +\index{\cpp{std::views::common}} + +The common view adapts a view into a common range, a range with a begin and end iterator of matching types. Non-range versions of algorithms require a common range. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/WbGrGK56z}{\ExternalLink}} +\footnotesize Example of using adapting a view for a non-range algorithm. +\tcblower +\cppfile{code_examples/views/common_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::all}}{\texttt{std::views::all}}} +\index{\cpp{std::views::all}} + +The all view is a view of all the elements from a range. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/1hf9ajK81}{\ExternalLink}} +\footnotesize Example of creating a view over all elements. Note that view over all elements is the default. +\tcblower +\cppfile{code_examples/views/all_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::split}, \cpp{std::views::lazy_split}, \newline\cpp{std::views::join_view}}{\texttt{std::views::split}, \texttt{std::views::lazy\_split}, \textCR\texttt{std::views::join\_view}}} +\index{\cpp{std::views::split}} +\index{\cpp{std::views::lazy_split}} +\index{\cpp{std::views::join_view}} + +The two split views split a single range into a view over sub-ranges. However, they differ in their implementation. +The \cpp{std::view::split} maintains the bidirectional, random access or contiguous properties of the underlying range, and the \texttt{std::view::\-lazy\_split} does not, but it does support input ranges. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/cjxdMjYhr}{\ExternalLink}} +\footnotesize Example of using split view to parse a version number. +\tcblower +\cppfile{code_examples/views/split_code.h} +\end{codebox} + +The join view flattens a view of ranges. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/MT7qbqjT6}{\ExternalLink}} +\footnotesize Example of using \cpp{std::views::lazy_split} to split a string into tokens and then join them using the \cpp{std::views::join}. +\tcblower +\cppfile{code_examples/views/join_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::empty}, \cpp{std::views::single}}{\texttt{std::views::empty}, \texttt{std::views::single}}} +\index{\cpp{std::views::empty}} +\index{\cpp{std::views::single}} + +The empty view is an empty view (containing no elements), and the single view is a view that contains a single element. +Note that the view owns the single element, which will be copied/moved alongside the view. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/d91M3o36e}{\ExternalLink}} +\footnotesize Example of using \cpp{std::views::empty} and \cpp{std::views::single}. +\tcblower +\cppfile{code_examples/views/single_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::iota}}{\texttt{std::views::iota}}} +\index{\cpp{std::views::iota}} + +The iota view represents a generated sequence formed by repeatedly incrementing an initial value. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/cEMdszTjE}{\ExternalLink}} +\footnotesize Example of using the finite and infinite iota view. +\tcblower +\cppfile{code_examples/views/iota_code.h} +\end{codebox} + +\section{\texorpdfstring{\cpp{std::views::istream}}{\texttt{std::views::istream}}} +\index{\cpp{std::views::istream}} + +The istream view provides similar functionality to istream iterator, except in the form of a view. +It represents a view obtained by successively applying the istream input operator. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/7r6x74PsY}{\ExternalLink}} +\footnotesize Example of using istream view. +\tcblower +\cppfile{code_examples/views/istream_code.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/90_theory.tex b/chapters/90_theory.tex index 76d2238..f1cffd8 100644 --- a/chapters/90_theory.tex +++ b/chapters/90_theory.tex @@ -7,27 +7,27 @@ \section{Argument-dependent lookup (ADL)} When calling a method without qualification (i.e. not specifying the namespace), the compiler needs to determine the set of candidate functions. As a first step, the compiler will do an unqualified name lookup, which starts at the local scope and examines the parent scopes until it finds the first instance of the name (at which point it stops). -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/GbbM5x98E}{\ExternalLink}} \footnotesize Example of unqualified lookup. Both calls to \cpp{some_call} will resolve to \cpp{::A::B::some_call} since this is the first instance discovered by the compiler. \tcblower \cppfile{code_examples/theory/adl_unqalified_code.h} -\end{box-note} +\end{codebox} Due to the simplicity of unqualified lookup, we need an additional mechanism to discover overloads. Notably, it is a requirement for operator overloading since operator calls are unqualified. This is where argument-dependent lookup comes in. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/odqGe47jW}{\ExternalLink}} \footnotesize Without ADL, any call to a custom operator overload would have to be fully qualified, requiring the function call syntax. \tcblower \cppfile{code_examples/theory/adl_code.h} -\end{box-note} +\end{codebox} While the full rules for ADL are quite complex, the heavily simplified version is that the compiler will also consider the innermost namespace of all the arguments when determining the viable function overloads. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/ebvhrsqKW}{\ExternalLink}} \footnotesize \tcblower \cppfile{code_examples/theory/adl_simple_code.h} -\end{box-note} +\end{codebox} Arguably the true power of ADL lies in the interactions with other language features, so let's look at how ADL interacts with friend functions and function objects. @@ -35,11 +35,11 @@ \subsection{Friend functions vs ADL} Friend functions (when defined inline) do not participate in the normal lookup (they are part of the surrounding namespace but are not visible). However, they are still visible to ADL, which permits a common implementation pattern for a default implementation with a customization point through ADL. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/nYME37jqE}{\ExternalLink}} \footnotesize Example demonstrating a default implementation with a customization point through ADL. The default implementation needs to be discovered during the unqualified lookup; therefore, if any custom implementation is visible in the surrounding namespaces, it will block this discovery. \tcblower \cppfile{code_examples/theory/adl_default_code.h} -\end{box-note} +\end{codebox} \subsection{Function objects vs ADL} @@ -47,28 +47,184 @@ \subsection{Function objects vs ADL} Argument-dependent lookup will not consider non-function symbols. This means that a function object or a lambda must be either visible to an unqualified lookup or need to be fully qualified. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/v8KobovKf}{\ExternalLink}} \footnotesize Example demonstrating a lambda stored in an inline variable that can only be invoked through a qualified call. \tcblower \cppfile{code_examples/theory/adl_nonfunc_code.h} -\end{box-note} +\end{codebox} Finally, discovering a non-function symbol during the unqualified lookup phase will prevent ADL completely. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/f75bTc7aE}{\ExternalLink}} \footnotesize Example demonstrating a non-function object preventing ADL, making a friend function impossible to invoke. \tcblower \cppfile{code_examples/theory/adl_shutdown_code.h} -\end{box-note} +\end{codebox} \subsection{\texorpdfstring{\CC20 ADL customization point}{C++20 ADL customization point}} With the introduction of concepts in \CC20, we now have a cleaner way to introduce a customization point using ADL. -\begin{box-note} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/Kr13she7v}{\ExternalLink}} \footnotesize The concept on line 4 will be satisfied if an ADL call is valid, i.e. there is a custom implementation. This is then used on lines 8 and 13 to differentiate between the two overloads. One calls the custom implementation, and the other contains the default implementation. The inline variable on line 18 is in an inline namespace to prevent collision with friend functions in the same namespace. However, because friend functions are only visible to ADL, a fully qualified call will always invoke this function object. \tcblower \cppfile{code_examples/theory/adl_niebloid_code.h} -\end{box-note} +\end{codebox} -This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. \ No newline at end of file +This approach has several benefits, in particular on the call site. We no longer need to remember to pull in the namespace of the default implementation. Furthermore, because the call is now fully qualified, we avoid the problem of symbol collisions that can potentially completely prevent ADL. + +\section{Integral and floating-point types} +\label{theory:numerics} + +Arguably one of the most error-prone parts of C++ is integral and floating-point expressions. As this part of the language is inherited from C, it relies heavily on fairly complex implicit conversion rules and sometimes interacts unintuitively with more static parts of C++ language. + +\subsection{Integral types} + +There are two phases of potential type changes when working with integral types. First, promotions are applied to types of lower rank than int, and if the resulting expression still contains different integral types, a conversion is applied to arrive at a common type. + +The ranks of integral types are defined in the standard: + +\begin{enumerate} + \item \cpp{bool} + \item \cpp{char}, \cpp{signed char}, \cpp{unsigned char} + \item \cpp{short int}, \cpp{unsigned short int} + \item \cpp{int}, \cpp{unsigned int} + \item \cpp{long int}, \cpp{unsigned long int} + \item \cpp{long long int}, \cpp{unsigned long long int} +\end{enumerate} + +\subsubsection{Promotions} + +As mentioned, integral promotions are applied to types of lower rank than \cpp{int} (e.g. \cpp{bool}, \cpp{char}, \cpp{short}). Such operands will be promoted to int (if \cpp{int} can represent all the values of the type, \cpp{unsigned int} if not). + +Promotions are generally harmless and invisible but can pop up when we mix them with static C++ features (more on that later). + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/P4EGeYxWc}{\ExternalLink}} +\footnotesize Example of \cpp{uint16_t} (both \cpp{a} and \cpp{b}) being promoted to \cpp{int}. +\tcblower +\cppfile{code_examples/theory/integral_promotions.h} +\end{codebox} + +\subsubsection{Conversions} + +Conversions apply after promotions when the two operands are still of different integral types. + +If the types are of the same signedness, the operand of the lower rank is converted to the type of the operand with the higher rank. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/91jhK9xdK}{\ExternalLink}} +\footnotesize Example of integral conversion. Both operands have the same signedness. +\tcblower +\cppfile{code_examples/theory/integral_conversions_same.h} +\end{codebox} + +When we mix integral types of different signedness, there are three possible outcomes. + +When the unsigned operand is of the same or higher rank than the signed operand, the signed operand is converted to the type of the unsigned operand. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/q988MTKor}{\ExternalLink}} +\footnotesize Example of integral conversion. The operands have different signedness but the same rank. +\tcblower +\cppfile{code_examples/theory/integral_conversions_different_a.h} +\end{codebox} + +When the type of the signed operand can represent all values of the unsigned operand, the unsigned operand is converted to the type of the signed operand. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/q988MTKor}{\ExternalLink}} +\footnotesize Example of integral conversion. The operands have different signedness, and the type of the signed operand can represent all values of the unsigned operand. +\tcblower +\cppfile{code_examples/theory/integral_conversions_different_b.h} +\end{codebox} + +Otherwise, both operands are converted to the unsigned version of the signed operand type. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/4hxMGd7Y8}{\ExternalLink}} +\footnotesize Example of integral conversion. The operands have different signedness, and the type of the signed operand cannot represent all values of the unsigned operand. +\tcblower +\cppfile{code_examples/theory/integral_conversions_different_c.h} +\end{codebox} + +Due to these rules, mixing integral types can sometimes lead to non-intuitive behaviour. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/c77aKGGaW}{\ExternalLink}} +\footnotesize Demonstration of a condition that changes value based on the type of one of the operands. +\tcblower +\cppfile{code_examples/theory/integral_conversions_problems.h} +\end{codebox} + +\subsubsection{C++20 safe integral operations} + +The C++20 standard introduced several tools that can be used to mitigate the issues when working with different integral types. + +Firstly, the standard introduced \cpp{std::ssize()}, which allows code that relies on signed integers to avoid mixing signed and unsigned integers when working with containers. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/jej1djd1W}{\ExternalLink}} +\footnotesize Demonstration of using \cpp{std::ssize} to avoid a signed-unsigned mismatch between the index variable and the container size. +\tcblower +\cppfile{code_examples/theory/safe_ssize.h} +\end{codebox} + +Second, a set of safe integral comparisons was introduced to correctly compare values of different integral types (without any value changes caused by conversions). + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/s5ecd1rfh}{\ExternalLink}} +\footnotesize Demonstration of safe comparison functions. +\tcblower +\cppfile{code_examples/theory/safe_compare.h} +\end{codebox} + +Finally, a small utility \cpp{std::in_range} will return whether the tested type can represent the supplied value. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/M3M3bMEYK}{\ExternalLink}} +\footnotesize Demonstration of \cpp{std::in_range}. +\tcblower +\cppfile{code_examples/theory/safe_in_range.h} +\end{codebox} + +\subsection{Floating-point types} + +The rules for floating-point types are a lot simpler. The resulting type of an expression is the highest floating-point type of the two arguments, including situations when one of the arguments is an integral type (highest in order: \cpp{float}, \cpp{double}, \cpp{long double}). + +Importantly, this logic is applied per operator, so ordering matters. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/dvncjjGj6}{\ExternalLink}} +\footnotesize In this example, both expressions end up with the resulting type \cpp{long double}; however, in the first expression, we lose precision by first converting to \cpp{float}. +\tcblower +\cppfile{code_examples/theory/floating_point.h} +\end{codebox} + +Ordering is one of the main things to remember when working with floating-point numbers (this is a general rule, not specific to C++). Operations with floating-point numbers are not associative. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/q3c43h7hj}{\ExternalLink}} +\footnotesize Demonstration of non-associativity of floating point expressions. +\tcblower +\cppfile{code_examples/theory/floating_point_ordering.h} +\end{codebox} + +Any operation with floating-point numbers of different magnitudes should be done with care. + +\subsection{Interactions with other C++ features} + +While integral types are implicitly inter-convertible, references to different integral types are not related types and will, therefore, not bind to each other. This has two consequences. + +First, trying to bind an lvalue reference to a non-matching integral type will not succeed. Second, if the destination reference can bind to temporaries (rvalue, const lvalue), the value will go through an implicit conversion, and the reference will bind to the resulting temporary. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/71b73eo8P}{\ExternalLink}} +\footnotesize Demonstration of integral type behaviour with reference types. +\tcblower +\cppfile{code_examples/theory/references.h} +\end{codebox} + +Finally, we need to talk about type deduction. Because type deduction is a static process, it does remove the opportunity for implicit conversions. However, this also brings potential issues. + +\begin{codebox}[]{\href{https://compiler-explorer.com/z/f131811x4}{\ExternalLink}} +\footnotesize Demonstration of potential problem of type deduction when using standard algorithms. +\tcblower +\cppfile{code_examples/theory/deduction_algorithm.h} +\end{codebox} + +But at the same time, when mixed with concepts, we can mitigate implicit conversions while only accepting a specific integral type. + +\begin{codebox}[breakable]{\href{https://compiler-explorer.com/z/qcWPPhTT6}{\ExternalLink}} +\footnotesize Demonstration of using concepts to prevent implicit conversions. +\tcblower +\cppfile{code_examples/theory/deduction_concepts.h} +\end{codebox} \ No newline at end of file diff --git a/chapters/XX_index.tex b/chapters/XX_index.tex deleted file mode 100644 index c620e65..0000000 --- a/chapters/XX_index.tex +++ /dev/null @@ -1 +0,0 @@ -\printindex \ No newline at end of file diff --git a/chapters/Y4_TODO.tex b/chapters/Y4_TODO.tex deleted file mode 100644 index fee66c8..0000000 --- a/chapters/Y4_TODO.tex +++ /dev/null @@ -1,16 +0,0 @@ -\chapter{What's next} - -The following chapters are still in the works: - -\begin{itemize} - \item \CC20 ranges in depth - \item \CC20 views - \item writing your own algorithm, writing your own view - \item parallel algorithms and beyond the \CC standard library - \item iterator adapters - \item numerical conversions and promotions (relates to numerical algorithms) - \item \CC23 features -\end{itemize} - -% TODO: Note algorithms that in their range versions only use projection for the functor, not for the output range, such as copy_if. -% TODO: Some views were covered in the transformation chapter re-review. \ No newline at end of file diff --git a/chapters/Y4_making_your_own.tex b/chapters/Y4_making_your_own.tex deleted file mode 100644 index f1073ab..0000000 --- a/chapters/Y4_making_your_own.tex +++ /dev/null @@ -1 +0,0 @@ -\chapter{Implementing custom algorithms} \ No newline at end of file diff --git a/chapters/Y5_parallel_algorithms.tex b/chapters/Y5_parallel_algorithms.tex deleted file mode 100644 index 9f40e28..0000000 --- a/chapters/Y5_parallel_algorithms.tex +++ /dev/null @@ -1 +0,0 @@ -\chapter{Parallel algorithms} \ No newline at end of file diff --git a/chapters/Y8_extra_topics.tex b/chapters/Y8_extra_topics.tex index 80e376b..ed01e48 100644 --- a/chapters/Y8_extra_topics.tex +++ b/chapters/Y8_extra_topics.tex @@ -1,192 +1,9 @@ \chapter{Extras} -\section{A short arithmetic detour} - -The algorithms in the header can have sharp edges. This mainly has to do with the way C++ handles mixed numerical types and how template deduction works. For example, the following is an easy mistake to make: - -\begin{box-note} -\begin{cppcode} -std::vector data{1.1, 2.2, 3.3, 4.4, 5.5}; -auto result = std::accumulate(data.begin(), data.end(), 0); -// result == 15 (actual sum 16.5) -\end{cppcode} -\end{box-note} - -Because we are passing a 0 as the initial value of the accumulator, which is a constant of type int, the accumulator ends up as int. Each fold operation then adds an integer and a double, resulting in a floating-point value. However, it immediately stores it in an integer variable, truncating the value. - -If you want to avoid this problem, you need to be familiar with literal suffixes: - -% TODO: include this information directly in the book, instead of the original links. -\begin{itemize} - \item integer literal suffixes - \item floating-point literal suffixes - \item fixed-size integer literal macros -\end{itemize} - -The second problem arises when mixing different integer types, particularly signed and unsigned integers. Again, as long as you do not mix types, you will not run into issues, but remember that the types that matter are the element types, the initial accumulator value and the functor arguments and return type. - -\begin{box-note} -\begin{cppcode} -std::vector data{1, std::numeric_limits::max()/2}; -auto ok = std::accumulate(data.begin(), data.end(), 0u, - [](auto acc, auto el) { return acc + el; }); -// Always OK, matching types. - -auto impl = std::accumulate(data.begin(), data.end(), 0, - [](auto acc, auto el) { return acc + el; }); -// Implementation defined: -// acc is int -// in acc + el: acc promoted to unsigned, result is unsigned - -// if an unsigned value cannot be represented by the target singed variable -// the behavior is implementation defined - -auto maybe = std::accumulate(data.begin(), data.end(), 0L, - [](auto acc, auto el) { return acc + el; }); -// OK as long as sizeof(long) > sizeof(unsigned) -// acc is long -// in acc + el: el is promoted to long - -// if sizeof(long) > sizeof(unsigned), the long can represent all -// values of unsigned, therefore OK -\end{cppcode} -\end{box-note} - -These are arguably very synthetic examples and shouldn’t appear in production codebases. - -If you are interested in more details about integer conversions and promotions, there is an excellent cheatsheet from hackingcpp on this topic. - -\section{Iterator adapaters} - -We have already used the std::back\_inserter many times across the examples in this series, so let's start with inserter adapters. - -\subsubsection{back\_inserter, front\_inserter, inserter} - -The standard offers three adapters that create instances of back\_insert\_iterator, front\_insert\_iterator and insert\_iterator. When assigned to, these iterators call the corresponding push\_back, push\_front and insert methods on the adapted container. - -\begin{box-note} -\begin{cppcode} -std::list data; -auto back = std::back_inserter(data); -auto front = std::front_inserter(data); - -*back = 10; -// data == { 10 } -*front = 20; -// data == { 20, 10 } - -auto mid = std::inserter(data, std::next(data.begin())); -*mid = 5; -// data == { 20, 5, 10 } -\end{cppcode} -\end{box-note} - -All inserter iterators model only output iterator concept, therefore cannot be used with algorithms that require forward or bidirectional iterators. - -\subsubsection{istream\_iterator, ostream\_iterator} - -These two adapters provide iteration over istream and ostream, respectively. - -The istream\_iterator will read a value by calling the corresponding operator>> when incremented. - -\begin{box-note} -\begin{cppcode} -std::stringstream input("10 20 30 40 50"); -auto in = std::istream_iterator(input); - -int i = *in; // i == 10 -++in; -int j = *in; // j == 20 -\end{cppcode} -\end{box-note} - -The ostream\_iterator will write a value by calling the corresponding operator<< when assigned to. - -\begin{box-note} -\begin{cppcode} -std::stringstream output; -auto out = std::ostream_iterator(output, ", "); - -*out = 10; -*out = 20; -// output == "10, 20," -\end{cppcode} -\end{box-note} - -The additional parameter for ostream\_iterator specifies the divider added after each element. - -The istream\_iterator models an input iterator, and the ostream\_iterator models an output iterator. - -\subsubsection{move\_iterator, make\_move\_iterator} - -The move\_iterator adapter inherits the iterator category from the adapted iterator but will return an rvalue when dereferenced, thus enabling move semantics. - -\begin{box-note} -\begin{cppcode} -std::vector data = { "a", "b", "c" }; -auto it = std::make_move_iterator(data.begin()); - -std::string str(*it); // move construction -++it; -str = *it; // move assignment -// data == { ?, ?, "c" } -// str == "b" -\end{cppcode} -\end{box-note} - -\subsubsection{reverse\_iterator} - -We can use the reverse\_iterator adapter for bidirectional iterators to obtain the iterator for the opposite direction. - -\begin{box-note} -\begin{cppcode} -std::vector data = { 1, 2, 3 }; -auto mid = std::reverse_iterator(data.end()); - -int i = *mid; // i == 3 -++mid; -int j = *mid; // j == 2 -\end{cppcode} -\end{box-note} - -Note that the reversed iterator maps to the previous element in the source direction. Therefore, when reversing begin or rbegin, the result is rend and end, respectively. - -\subsubsection{counted\_iterator} - -When working with non-random-access ranges, it is convenient to specify the range using a begin iterator coupled with the number of elements. The counted\_iterator brings this feature to C++20 ranges without the need for specialised algorithm variants like std::fill\_n. - -\begin{box-note} -\begin{cppcode} -std::list data = { 1, 2, 3, 4, 5 }; - -std::ranges::fill(std::counted_iterator(data.begin(), 3), std::default_sentinel, 9); -// data == { 9, 9, 9, 4, 5 } -\end{cppcode} -\end{box-note} - -The counted\_iterator keeps track of the number of elements, and when the counter reaches zero, the counted\_iterator will compare equal to std::default\_sentinel, signifying the end of the range. - -% TODO: integrate -% Taken from copy section -We can also emulate std::move by using the previously mentioned move\_iterator adapter. - -\begin{box-note} -\begin{cppcode} -std::vector data{ "a", "b", "c", "d", "e", "f"}; - -std::copy(std::make_move_iterator(data.begin()), std::make_move_iterator(data.begin()+3), - data.begin()+3); -// data = { ?, ?, ?, "a", "b", "c" } -\end{cppcode} -\end{box-note} - - \section{Unitialized memory} The heap and uninitialized memory algorithms represent a niche category in the overall algorithm arsenal, primarily because the standard library offers more convenient alternatives that, on the other hand, offer less control. Therefore, if you desire or need the control, you can use these algorithms as a foundation for your custom implementation. - - \subsection{Working with uninitialized memory} The second category of algorithms we will talk about today are algorithms that operate on uninitialized memory. Like the heap algorithms, you should prefer higher-level abstractions (e.g. polymorphic memory resource). However, when working with uninitialized memory, using these algorithms is preferable to implementing all the functionality from scratch. @@ -197,7 +14,7 @@ \subsection{Working with uninitialized memory} Here is an example of allocating (and deallocating) space for ten std::string objects using the global operator new: -\begin{box-note} +\begin{codebox} \begin{cppcode} auto* begin = static_cast( ::operator new[](sizeof(std::string)*10, @@ -205,7 +22,7 @@ \subsection{Working with uninitialized memory} ::operator delete[](begin, static_cast(alignof(std::string))); \end{cppcode} -\end{box-note} +\end{codebox} This example contains a lot of spelling, so let’s step through: @@ -219,12 +36,12 @@ \subsection{Working with uninitialized memory} Here is an example of using a char buffer on the stack: -\begin{box-note} +\begin{codebox} \begin{cppcode} alignas(alignof(std::string)) char buff[sizeof(std::string)*10]; auto *begin = reinterpret_cast(buff); \end{cppcode} -\end{box-note} +\end{codebox} Because the memory lives on the stack, we do not need a deallocation step. diff --git a/code_examples/algorithms/accumulate_code.h b/code_examples/algorithms/accumulate_code.h index aafb1f0..f0e7e85 100644 --- a/code_examples/algorithms/accumulate_code.h +++ b/code_examples/algorithms/accumulate_code.h @@ -1,7 +1,7 @@ std::vector data{1, 2, 3, 4, 5}; - auto sum = std::accumulate(data.begin(), data.end(), 0); // sum == 15 -auto product = std::accumulate(data.begin(), data.end(), 1, std::multiplies<>{}); -// product == 120 +auto product = std::accumulate(data.begin(), data.end(), 1, + std::multiplies<>{}); +// product == 120 \ No newline at end of file diff --git a/code_examples/algorithms/accumulate_right_code.h b/code_examples/algorithms/accumulate_right_code.h index c0f6b22..c62dba0 100644 --- a/code_examples/algorithms/accumulate_right_code.h +++ b/code_examples/algorithms/accumulate_right_code.h @@ -10,5 +10,4 @@ auto right_fold = std::accumulate(data.rbegin(), data.rend(), 0, [](int acc, int el) { return acc / 2 + el; }); -// right_fold == 3 - +// right_fold == 3 \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_difference_code.h b/code_examples/algorithms/adjacent_difference_code.h index c4c1196..34fb6cc 100644 --- a/code_examples/algorithms/adjacent_difference_code.h +++ b/code_examples/algorithms/adjacent_difference_code.h @@ -1,3 +1,3 @@ std::vector data{2, 3, 5, 7, 11, 13}; std::adjacent_difference(data.begin(), data.end(), data.begin()); -// data == {2, 1, 2, 2, 4, 2} +// data == {2, 1, 2, 2, 4, 2} \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_difference_extra_code.h b/code_examples/algorithms/adjacent_difference_extra_code.h index cff7a39..8ccfcbc 100644 --- a/code_examples/algorithms/adjacent_difference_extra_code.h +++ b/code_examples/algorithms/adjacent_difference_extra_code.h @@ -4,4 +4,4 @@ std::vector data(10, 1); std::adjacent_difference(data.begin(), std::prev(data.end()), std::next(data.begin()), std::plus()); // Fibonacci sequence: -// data == {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} +// data == {1, 1, 2, 3, 5, 8, 13, 21, 34, 55} \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_difference_par_code.h b/code_examples/algorithms/adjacent_difference_par_code.h new file mode 100644 index 0000000..9c27055 --- /dev/null +++ b/code_examples/algorithms/adjacent_difference_par_code.h @@ -0,0 +1,6 @@ +std::vector data{2, 3, 5, 7, 11, 13}; +std::vector out(data.size()); + +std::adjacent_difference(std::execution::par_unseq, + data.begin(), data.end(), out.begin()); +// out == {2, 1, 2, 2, 4, 2} \ No newline at end of file diff --git a/code_examples/algorithms/adjacent_find_code.h b/code_examples/algorithms/adjacent_find_code.h index c338b6f..d781286 100644 --- a/code_examples/algorithms/adjacent_find_code.h +++ b/code_examples/algorithms/adjacent_find_code.h @@ -4,4 +4,4 @@ auto it1 = std::adjacent_find(data.begin(), data.end()); auto it2 = std::adjacent_find(data.begin(), data.end(), [](int l, int r) { return l + r > 10; }); -// *it2 == 5, i.e. {5, 6} +// *it2 == 5, i.e. {5, 6} \ No newline at end of file diff --git a/code_examples/algorithms/clamp_code.h b/code_examples/algorithms/clamp_code.h index 43a556c..ca8eb16 100644 --- a/code_examples/algorithms/clamp_code.h +++ b/code_examples/algorithms/clamp_code.h @@ -10,4 +10,4 @@ int c = std::clamp(30, 0, 20); int x = 10, y = 20, z = 30; int &w = const_cast(std::clamp(z, x, y)); w = 99; -// x == 10, y == 99, z == 30 +// x == 10, y == 99, z == 30 \ No newline at end of file diff --git a/code_examples/algorithms/copy_backward_code.h b/code_examples/algorithms/copy_backward_code.h index eda5ca3..965f0fd 100644 --- a/code_examples/algorithms/copy_backward_code.h +++ b/code_examples/algorithms/copy_backward_code.h @@ -6,4 +6,4 @@ std::copy_backward(data.begin(), data.end(), out.end()); // out == {"", "", "", "a", "b", "c", "d", "e", "f"} std::copy_backward(data.begin(), std::prev(data.end()), data.end()); -// data == { "a", "a", "b", "c", "d", "e" } +// data == { "a", "a", "b", "c", "d", "e" } \ No newline at end of file diff --git a/code_examples/algorithms/copy_code.h b/code_examples/algorithms/copy_code.h index c4578b3..daa10e2 100644 --- a/code_examples/algorithms/copy_code.h +++ b/code_examples/algorithms/copy_code.h @@ -5,4 +5,4 @@ std::copy(data.begin(), data.begin()+3, data.begin()+3); // Overlapping case: std::copy(std::next(data.begin()), data.end(), data.begin()); -// data = { "b", "c", "a", "b", "c", "c" } +// data = { "b", "c", "a", "b", "c", "c" } \ No newline at end of file diff --git a/code_examples/algorithms/copy_if_code.h b/code_examples/algorithms/copy_if_code.h index 7bc5b72..23c6fee 100644 --- a/code_examples/algorithms/copy_if_code.h +++ b/code_examples/algorithms/copy_if_code.h @@ -10,4 +10,4 @@ std::ranges::remove_copy_if(data, std::back_inserter(odd), is_even); // odd == { 1, 3, 5, 7, 9 } std::ranges::remove_copy(data, std::back_inserter(no_five), 5); -// no_five == { 1, 2, 3, 4, 6, 7, 8, 9 } +// no_five == { 1, 2, 3, 4, 6, 7, 8, 9 } \ No newline at end of file diff --git a/code_examples/algorithms/copy_n_code.h b/code_examples/algorithms/copy_n_code.h index b316322..3217c88 100644 --- a/code_examples/algorithms/copy_n_code.h +++ b/code_examples/algorithms/copy_n_code.h @@ -2,4 +2,4 @@ std::list data{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector out; std::copy_n(data.begin(), 5, std::back_inserter(out)); -// out == { 1, 2, 3, 4, 5 } +// out == { 1, 2, 3, 4, 5 } \ No newline at end of file diff --git a/code_examples/algorithms/equal_with_range_code.h b/code_examples/algorithms/equal_with_range_code.h index 7dffa7f..28a9cc2 100644 --- a/code_examples/algorithms/equal_with_range_code.h +++ b/code_examples/algorithms/equal_with_range_code.h @@ -11,6 +11,4 @@ bool test2 = std::equal(first.begin(), first.end(), auto pair_it = std::mismatch(first.begin(), first.end(), second.begin(), second.end()); // pair_it.first == first.end() -// *pair_it.second == 6 - - +// *pair_it.second == 6 \ No newline at end of file diff --git a/code_examples/algorithms/exclusive_scan_code.h b/code_examples/algorithms/exclusive_scan_code.h index 90abcfd..caa8145 100644 --- a/code_examples/algorithms/exclusive_scan_code.h +++ b/code_examples/algorithms/exclusive_scan_code.h @@ -7,4 +7,4 @@ std::exclusive_scan(src.begin(), src.end(), std::exclusive_scan(src.begin(), src.end(), out.begin(), 1, std::multiplies<>{}); -// out == {1, 1, 2, 6, 24, 120} +// out == {1, 1, 2, 6, 24, 120} \ No newline at end of file diff --git a/code_examples/algorithms/fill_n_code.h b/code_examples/algorithms/fill_n_code.h index b8bc0e0..ba4be01 100644 --- a/code_examples/algorithms/fill_n_code.h +++ b/code_examples/algorithms/fill_n_code.h @@ -6,4 +6,4 @@ std::fill_n(std::back_inserter(data), 5, 11); std::ranges::generate_n(std::back_inserter(data), 5, []() { return 7; }); -// data == {11, 11, 11, 11, 11, 7, 7, 7, 7, 7} +// data == {11, 11, 11, 11, 11, 7, 7, 7, 7, 7} \ No newline at end of file diff --git a/code_examples/algorithms/find_code.h b/code_examples/algorithms/find_code.h index fc0eb5d..504104d 100644 --- a/code_examples/algorithms/find_code.h +++ b/code_examples/algorithms/find_code.h @@ -7,5 +7,4 @@ while ((token = find(it, data.end(), ';')) != data.end()) { std::copy(it, token, std::back_inserter(out.back())); it = std::next(token); } -// out == { "John", "Doe", "April", "1", "1900" } - +// out == { "John", "Doe", "April", "1", "1900" } \ No newline at end of file diff --git a/code_examples/algorithms/find_first_of_code.h b/code_examples/algorithms/find_first_of_code.h index d442027..25c9f7b 100644 --- a/code_examples/algorithms/find_first_of_code.h +++ b/code_examples/algorithms/find_first_of_code.h @@ -3,4 +3,4 @@ std::vector needles = { 7, 5, 3 }; auto it = std::find_first_of(haystack.begin(), haystack.end(), needles.begin(), needles.end()); -// *it == 3, i.e. haystack[2] +// *it == 3, i.e. haystack[2] \ No newline at end of file diff --git a/code_examples/algorithms/find_if_code.h b/code_examples/algorithms/find_if_code.h index ca64255..d1ab486 100644 --- a/code_examples/algorithms/find_if_code.h +++ b/code_examples/algorithms/find_if_code.h @@ -11,4 +11,4 @@ std::copy(begin, [](char c) { return isspace(c); } ).base(), std::back_inserter(out)); -// out == "hello world!" +// out == "hello world!" \ No newline at end of file diff --git a/code_examples/algorithms/for_each_code_parallel.h b/code_examples/algorithms/for_each_code_parallel.h index d0e892f..d7df0ba 100644 --- a/code_examples/algorithms/for_each_code_parallel.h +++ b/code_examples/algorithms/for_each_code_parallel.h @@ -10,5 +10,4 @@ std::for_each(std::execution::par_unseq, data.begin(), data.end(), [](Custom& el) { el.expensive_operation(); - }); - + }); \ No newline at end of file diff --git a/code_examples/algorithms/for_each_code_range.h b/code_examples/algorithms/for_each_code_range.h index 8954667..af7a4be 100644 --- a/code_examples/algorithms/for_each_code_range.h +++ b/code_examples/algorithms/for_each_code_range.h @@ -3,5 +3,4 @@ int sum = 0; for(auto el : data) { sum += el; } -// sum == 45 - +// sum == 45 \ No newline at end of file diff --git a/code_examples/algorithms/for_each_code_simple.h b/code_examples/algorithms/for_each_code_simple.h index ad8c3b9..e5bb075 100644 --- a/code_examples/algorithms/for_each_code_simple.h +++ b/code_examples/algorithms/for_each_code_simple.h @@ -3,5 +3,4 @@ int sum = 0; std::for_each(data.begin(), data.end(), [&sum](int el) { sum += el; }); -// sum == 45 - +// sum == 45 \ No newline at end of file diff --git a/code_examples/algorithms/for_each_n_code.h b/code_examples/algorithms/for_each_n_code.h index 46b70e0..b504025 100644 --- a/code_examples/algorithms/for_each_n_code.h +++ b/code_examples/algorithms/for_each_n_code.h @@ -15,4 +15,4 @@ while (it != final_ranking.end()) { }); page++; it += cnt; -} +} \ No newline at end of file diff --git a/code_examples/algorithms/generate_code.h b/code_examples/algorithms/generate_code.h index 20ecb6f..e0dba25 100644 --- a/code_examples/algorithms/generate_code.h +++ b/code_examples/algorithms/generate_code.h @@ -9,4 +9,4 @@ std::ranges::generate(data, []() { return 5; }); // iota-like std::ranges::generate(data, [i = 0]() mutable { return i++; }); -// data == {0, 1, 2, 3, 4} +// data == {0, 1, 2, 3, 4} \ No newline at end of file diff --git a/code_examples/algorithms/includes_code.h b/code_examples/algorithms/includes_code.h index 4c9c5fa..aa12904 100644 --- a/code_examples/algorithms/includes_code.h +++ b/code_examples/algorithms/includes_code.h @@ -1,8 +1,7 @@ std::vector letters('z'-'a'+1,'\0'); std::iota(letters.begin(), letters.end(), 'a'); - std::string input = "the quick brown fox jumps over the lazy dog"; std::ranges::sort(input); bool test = std::ranges::includes(input, letters); -// test == true +// test == true \ No newline at end of file diff --git a/code_examples/algorithms/inclusive_scan_code.h b/code_examples/algorithms/inclusive_scan_code.h index f444b06..638570e 100644 --- a/code_examples/algorithms/inclusive_scan_code.h +++ b/code_examples/algorithms/inclusive_scan_code.h @@ -7,4 +7,4 @@ std::inclusive_scan(src.begin(), src.end(), std::inclusive_scan(src.begin(), src.end(), out.begin(), std::multiplies<>{}, 1); -// out == {1, 2, 6, 24, 120, 720} +// out == {1, 2, 6, 24, 120, 720} \ No newline at end of file diff --git a/code_examples/algorithms/inner_product_code.h b/code_examples/algorithms/inner_product_code.h index a3f3713..565330c 100644 --- a/code_examples/algorithms/inner_product_code.h +++ b/code_examples/algorithms/inner_product_code.h @@ -3,4 +3,4 @@ std::vector widths{2, 3, 4, 5, 6}; auto total_area = std::inner_product(heights.begin(), heights.end(), widths.begin(), 0); -// total_area == 70 +// total_area == 70 \ No newline at end of file diff --git a/code_examples/algorithms/inner_product_one_code.h b/code_examples/algorithms/inner_product_one_code.h index 5297ecc..ae59f9a 100644 --- a/code_examples/algorithms/inner_product_one_code.h +++ b/code_examples/algorithms/inner_product_one_code.h @@ -5,4 +5,4 @@ auto sum_of_diffs = std::inner_product( std::plus<>{}, [](int left, int right) { return std::abs(left-right); } ); -// sum_of_diffs == 13 +// sum_of_diffs == 13 \ No newline at end of file diff --git a/code_examples/algorithms/iota_code.h b/code_examples/algorithms/iota_code.h index 37fa952..fe32cb2 100644 --- a/code_examples/algorithms/iota_code.h +++ b/code_examples/algorithms/iota_code.h @@ -2,4 +2,4 @@ std::vector data(9, 0); // data == {0, 0, 0, 0, 0, 0, 0, 0, 0} std::iota(data.begin(), data.end(), -4); -// data == {-4, -3, -2, -1, 0, 1, 2, 3, 4} +// data == {-4, -3, -2, -1, 0, 1, 2, 3, 4} \ No newline at end of file diff --git a/code_examples/algorithms/is_partitioned_code.h b/code_examples/algorithms/is_partitioned_code.h index 035a7aa..cc6890c 100644 --- a/code_examples/algorithms/is_partitioned_code.h +++ b/code_examples/algorithms/is_partitioned_code.h @@ -9,4 +9,4 @@ for (int i = 0; i < 16; ++i) { test2 = test2 && std::is_partitioned(data.begin(), data.end(), [&i](int v) { return v < i; }); } -// test2 == true +// test2 == true \ No newline at end of file diff --git a/code_examples/algorithms/is_sorted_until_code.h b/code_examples/algorithms/is_sorted_until_code.h index e550c79..eb55b86 100644 --- a/code_examples/algorithms/is_sorted_until_code.h +++ b/code_examples/algorithms/is_sorted_until_code.h @@ -1,3 +1,3 @@ std::vector data {1, 5, 9, 2, 4, 6}; auto it = std::is_sorted_until(data.begin(), data.end()); -// *it == 2 +// *it == 2 \ No newline at end of file diff --git a/code_examples/algorithms/iter_swap_partition_code.h b/code_examples/algorithms/iter_swap_partition_code.h index 415dee3..b7c4d38 100644 --- a/code_examples/algorithms/iter_swap_partition_code.h +++ b/code_examples/algorithms/iter_swap_partition_code.h @@ -1,7 +1,7 @@ template requires std::forward_iterator && std::indirectly_swappable - && std::predicate + && std::predicate auto partition(It first, It last, Cond cond) { while (first != last && cond(first)) ++first; if (first == last) return last; diff --git a/code_examples/algorithms/iter_swap_unique_ptr_code.h b/code_examples/algorithms/iter_swap_unique_ptr_code.h index 84ac1de..3a6f278 100644 --- a/code_examples/algorithms/iter_swap_unique_ptr_code.h +++ b/code_examples/algorithms/iter_swap_unique_ptr_code.h @@ -5,4 +5,4 @@ int* p2_pre = p2.get(); std::ranges::iter_swap(p1, p2); // p1.get() == p1_pre, *p1 == 2 -// p2.get() == p2_pre, *p2 == 1 +// p2.get() == p2_pre, *p2 == 1 \ No newline at end of file diff --git a/code_examples/algorithms/lexicographical_compare_code.h b/code_examples/algorithms/lexicographical_compare_code.h index bb8c55f..4c090f8 100644 --- a/code_examples/algorithms/lexicographical_compare_code.h +++ b/code_examples/algorithms/lexicographical_compare_code.h @@ -8,7 +8,7 @@ bool cmp1 = std::lexicographical_compare(range1.begin(), range1.end(), bool cmp2 = range1 < range2; // cmp1 == cmp2 == true -bool cmp3 = std::lexicographical_compare(range2.begin(), range2.end(), +bool cmp3 = std::lexicographical_compare(range2.begin(), range2.end(), range3.begin(), range3.end()); // same as bool cmp4 = range2 < range3; diff --git a/code_examples/algorithms/lexicographical_compare_three_way_code.h b/code_examples/algorithms/lexicographical_compare_three_way_code.h index c006bd8..e7ae590 100644 --- a/code_examples/algorithms/lexicographical_compare_three_way_code.h +++ b/code_examples/algorithms/lexicographical_compare_three_way_code.h @@ -1,7 +1,7 @@ -std::vector data1 = { 1, 1, 1}; -std::vector data2 = { 1, 2, 3}; +std::vector data1 = { 1, 1, 1 }; +std::vector data2 = { 1, 2, 3 }; auto cmp = std::lexicographical_compare_three_way( data1.begin(), data1.end(), data2.begin(), data2.end()); -// cmp == std::strong_ordering::less +// cmp == std::strong_ordering::less \ No newline at end of file diff --git a/code_examples/algorithms/lower_bound_code.h b/code_examples/algorithms/lower_bound_code.h index 4579a02..c7d44a9 100644 --- a/code_examples/algorithms/lower_bound_code.h +++ b/code_examples/algorithms/lower_bound_code.h @@ -1,8 +1,10 @@ const std::vector& results = get_results(); -auto lb = std::ranges::lower_bound(results, 49, {}, &ExamResult::score); +auto lb = std::ranges::lower_bound(results, 49, {}, + &ExamResult::score); // First element for which: it->score >= 49 -auto ub = std::ranges::upper_bound(results, 99, {}, &ExamResult::score); +auto ub = std::ranges::upper_bound(results, 99, {}, + &ExamResult::score); // First element for which: 99 < it->score for (auto it = results.begin(); it != lb; it++) { diff --git a/code_examples/algorithms/make_heap_code.h b/code_examples/algorithms/make_heap_code.h index 873cc1a..8685d2b 100644 --- a/code_examples/algorithms/make_heap_code.h +++ b/code_examples/algorithms/make_heap_code.h @@ -12,4 +12,4 @@ std::make_heap(data.begin(), data.end(), std::greater<>{}); // 1 <= 2, 1 <= 3 // 2 <= 4, 2 <= 5 // 3 <= 6, 3 <= 7 -// ... +// ... \ No newline at end of file diff --git a/code_examples/algorithms/merge_code.h b/code_examples/algorithms/merge_code.h index 09ebc0a..9f22b7d 100644 --- a/code_examples/algorithms/merge_code.h +++ b/code_examples/algorithms/merge_code.h @@ -14,5 +14,4 @@ auto cmp = [](const auto& left, const auto& right) std::ranges::merge(data1, data2, std::back_inserter(result), cmp); // result == {0, second}, {1, first}, {2, first}, -// {2, second}, {3, first}, {4, second} - +// {2, second}, {3, first}, {4, second} \ No newline at end of file diff --git a/code_examples/algorithms/min_max_code.h b/code_examples/algorithms/min_max_code.h index 237149e..98a9187 100644 --- a/code_examples/algorithms/min_max_code.h +++ b/code_examples/algorithms/min_max_code.h @@ -3,7 +3,8 @@ int min = std::min(x, y); // min == 10 std::string hello = "hello", world = "world"; -std::string& universe = const_cast(std::max(hello, world)); +std::string& universe = + const_cast(std::max(hello, world)); universe = "universe"; std::string greeting = hello + " " + world; diff --git a/code_examples/algorithms/minmax_code.h b/code_examples/algorithms/minmax_code.h index 5028c25..a42fa27 100644 --- a/code_examples/algorithms/minmax_code.h +++ b/code_examples/algorithms/minmax_code.h @@ -9,4 +9,4 @@ auto [first, second] = std::minmax(b, a); // Operating on prvalues requires capture by value std::pair result = std::minmax(5, 10); -// result.first = 5, result.second = 10 +// result.first = 5, result.second = 10 \ No newline at end of file diff --git a/code_examples/algorithms/minmax_extra_code.h b/code_examples/algorithms/minmax_extra_code.h index 2b6b23e..b8d6fd5 100644 --- a/code_examples/algorithms/minmax_extra_code.h +++ b/code_examples/algorithms/minmax_extra_code.h @@ -6,4 +6,4 @@ auto minmax = std::minmax({5, 3, -2, 0}); std::list data{5, 3, -2, 0}; auto max = std::ranges::max(data); -// max == 5 +// max == 5 \ No newline at end of file diff --git a/code_examples/algorithms/mismatch_code.h b/code_examples/algorithms/mismatch_code.h index 3e0666b..ac7721c 100644 --- a/code_examples/algorithms/mismatch_code.h +++ b/code_examples/algorithms/mismatch_code.h @@ -1,5 +1,6 @@ std::vector first = { 1, 2, 3, 4, 5 }; std::vector second = { 1, 2, 2, 4, 5 }; -auto it_pair = std::mismatch(first.begin(), first.end(), second.begin()); -// *it_pair.first == 3, *it_pair.second == 2 +auto it_pair = std::mismatch(first.begin(), first.end(), + second.begin()); +// *it_pair.first == 3, *it_pair.second == 2 \ No newline at end of file diff --git a/code_examples/algorithms/move_code.h b/code_examples/algorithms/move_code.h index 9eb5616..5f0ecd9 100644 --- a/code_examples/algorithms/move_code.h +++ b/code_examples/algorithms/move_code.h @@ -2,4 +2,5 @@ std::vector data{ "a", "b", "c", "d", "e", "f"}; std::move(data.begin(), data.begin()+3, data.begin()+3); // data = { ?, ?, ?, "a", "b", "c" } -// Note: most implementations will set moved-out-of std::string to empty. +// Note: most implementations will set +// a moved-out-of std::string to empty. \ No newline at end of file diff --git a/code_examples/algorithms/nth_element_code.h b/code_examples/algorithms/nth_element_code.h index 53de271..a742938 100644 --- a/code_examples/algorithms/nth_element_code.h +++ b/code_examples/algorithms/nth_element_code.h @@ -1,8 +1,7 @@ std::vector data{9, 1, 8, 2, 7, 3, 6, 4, 5}; -std::nth_element(data.begin(), data.begin()+4, data.end()); -// data[4] == 5 -// data[0..3] == {1, 2, 3, 4} - in undeterminate order +std::nth_element(data.begin(), data.begin() + 4, data.end()); +// data[4] == 5, data[0..3] < data[4] -std::nth_element(data.begin(), data.begin()+7, data.end(), std::greater<>()); -// data[7] == 2 -// data[0..6] == {3, 4, 5, 6, 7, 8, 9} - in undeterminate order +std::nth_element(data.begin(), data.begin() + 7, data.end(), + std::greater<>()); +// data[7] == 2, data[0..6] > data[7] \ No newline at end of file diff --git a/code_examples/algorithms/partial_sort_code.h b/code_examples/algorithms/partial_sort_code.h index ae55eef..d150b76 100644 --- a/code_examples/algorithms/partial_sort_code.h +++ b/code_examples/algorithms/partial_sort_code.h @@ -3,4 +3,4 @@ std::partial_sort(data.begin(), data.begin()+3, data.end()); // data == {1, 2, 3, -unspecified order-} std::ranges::partial_sort(data, data.begin()+3, std::greater<>()); -// data == {9, 8, 7, -unspecified order-} +// data == {9, 8, 7, -unspecified order-} \ No newline at end of file diff --git a/code_examples/algorithms/partial_sort_copy_code.h b/code_examples/algorithms/partial_sort_copy_code.h index 034ec1b..38b8724 100644 --- a/code_examples/algorithms/partial_sort_copy_code.h +++ b/code_examples/algorithms/partial_sort_copy_code.h @@ -7,5 +7,4 @@ auto cnt = std::counted_iterator(input, 10); std::ranges::partial_sort_copy(cnt, std::default_sentinel, top.begin(), top.end(), std::greater<>{}); -// top == { 9, 8, 7 } - +// top == { 9, 8, 7 } \ No newline at end of file diff --git a/code_examples/algorithms/partial_sum_code.h b/code_examples/algorithms/partial_sum_code.h index d5d921f..232f57b 100644 --- a/code_examples/algorithms/partial_sum_code.h +++ b/code_examples/algorithms/partial_sum_code.h @@ -7,4 +7,4 @@ std::partial_sum(data.begin(), data.end(), data.begin()); std::vector out; std::partial_sum(data.begin(), data.end(), std::back_inserter(out), std::multiplies<>{}); -// out == {1, 2, 6, 24, 120, 720} +// out == {1, 2, 6, 24, 120, 720} \ No newline at end of file diff --git a/code_examples/algorithms/partition_code.h b/code_examples/algorithms/partition_code.h index ef80750..3123eff 100644 --- a/code_examples/algorithms/partition_code.h +++ b/code_examples/algorithms/partition_code.h @@ -12,4 +12,4 @@ for (auto it = results.begin(); it != pp; ++it) { // process failed students for (auto it = pp; it != results.end(); ++it) { std::cout << "[FAIL] " << it->student_name << "\n"; -} +} \ No newline at end of file diff --git a/code_examples/algorithms/partition_copy_code.h b/code_examples/algorithms/partition_copy_code.h index a37cbd9..c87dcb3 100644 --- a/code_examples/algorithms/partition_copy_code.h +++ b/code_examples/algorithms/partition_copy_code.h @@ -8,4 +8,4 @@ std::partition_copy(data.begin(), data.end(), is_even); // even == {2, 4, 6} -// odd == {1, 3, 5} +// odd == {1, 3, 5} \ No newline at end of file diff --git a/code_examples/algorithms/push_heap_code.h b/code_examples/algorithms/push_heap_code.h index bdea941..ff5b164 100644 --- a/code_examples/algorithms/push_heap_code.h +++ b/code_examples/algorithms/push_heap_code.h @@ -17,4 +17,4 @@ std::push_heap(data.begin(), data.end()); std::pop_heap(data.begin(), data.end()); // data == { [heap_part], 9 } std::pop_heap(data.begin(), std::prev(data.end())); -// data == { [heap_part], 7, 9 } +// data == { [heap_part], 7, 9 } \ No newline at end of file diff --git a/code_examples/algorithms/qsort_code.h b/code_examples/algorithms/qsort_code.h index d4a9979..f718ac7 100644 --- a/code_examples/algorithms/qsort_code.h +++ b/code_examples/algorithms/qsort_code.h @@ -10,4 +10,4 @@ qsort(data, size, sizeof(int), if (vl > vr) return 1; return 0; }); -// data == {-8, -1, 1, 2, 7, 9} +// data == {-8, -1, 1, 2, 7, 9} \ No newline at end of file diff --git a/code_examples/algorithms/qsort_not_code.h b/code_examples/algorithms/qsort_not_code.h index 663cc85..e196485 100644 --- a/code_examples/algorithms/qsort_not_code.h +++ b/code_examples/algorithms/qsort_not_code.h @@ -2,4 +2,4 @@ int data[] = {2, 1, 9, -1, 7, -8}; int size = sizeof data / sizeof(int); std::sort(&data[0], &data[size], std::less<>()); -// data == {-8, -1, 1, 2, 7, 9} +// data == {-8, -1, 1, 2, 7, 9} \ No newline at end of file diff --git a/code_examples/algorithms/reduce_code.h b/code_examples/algorithms/reduce_code.h index 6001d4c..9b20907 100644 --- a/code_examples/algorithms/reduce_code.h +++ b/code_examples/algorithms/reduce_code.h @@ -7,9 +7,10 @@ sum = std::reduce(std::execution::par_unseq, data.begin(), data.end(), 0); // sum == 15 -auto product = std::reduce(data.begin(), data.end(), 1, std::multiplies<>{}); +auto product = std::reduce(data.begin(), data.end(), 1, + std::multiplies<>{}); // product == 120 product = std::reduce(std::execution::par_unseq, data.begin(), data.end(), 1, std::multiplies<>{}); -// product == 120 +// product == 120 \ No newline at end of file diff --git a/code_examples/algorithms/reduce_noinit_code.h b/code_examples/algorithms/reduce_noinit_code.h index adb74d8..0b9262e 100644 --- a/code_examples/algorithms/reduce_noinit_code.h +++ b/code_examples/algorithms/reduce_noinit_code.h @@ -7,4 +7,4 @@ struct Duck { std::vector data(2, Duck{}); Duck final_duck = std::reduce(data.begin(), data.end()); -// final_duck.sound == "QuackQuackQuack" +// final_duck.sound == "QuackQuackQuack" \ No newline at end of file diff --git a/code_examples/algorithms/replace_copy_code.h b/code_examples/algorithms/replace_copy_code.h index d876270..04b8177 100644 --- a/code_examples/algorithms/replace_copy_code.h +++ b/code_examples/algorithms/replace_copy_code.h @@ -5,5 +5,6 @@ std::ranges::replace_copy(data, std::back_inserter(out), 5, 10); // out == { 1, 2, 3, 4, 10, 6, 7, 8, 9 } auto is_even = [](int v) { return v % 2 == 0; }; -std::ranges::replace_copy_if(data, std::back_inserter(odd), is_even, -1); -// odd == { 1, -1, 3, -1, 5, -1, 7, -1, 9 } +std::ranges::replace_copy_if(data, std::back_inserter(odd), + is_even, -1); +// odd == { 1, -1, 3, -1, 5, -1, 7, -1, 9 } \ No newline at end of file diff --git a/code_examples/algorithms/reverse_copy_code.h b/code_examples/algorithms/reverse_copy_code.h index c456309..7c8eed1 100644 --- a/code_examples/algorithms/reverse_copy_code.h +++ b/code_examples/algorithms/reverse_copy_code.h @@ -2,4 +2,4 @@ std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector out; std::ranges::reverse_copy(data, std::back_inserter(out)); -// out == { 9, 8, 7, 6, 5, 4, 3, 2, 1 } +// out == { 9, 8, 7, 6, 5, 4, 3, 2, 1 } \ No newline at end of file diff --git a/code_examples/algorithms/rotate_copy_code.h b/code_examples/algorithms/rotate_copy_code.h index 9316025..b692e4d 100644 --- a/code_examples/algorithms/rotate_copy_code.h +++ b/code_examples/algorithms/rotate_copy_code.h @@ -1,5 +1,6 @@ std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector out; -std::ranges::rotate_copy(data, data.begin()+4, std::back_inserter(out)); -// out == { 5, 6, 7, 8, 9, 1, 2, 3, 4 } +std::ranges::rotate_copy(data, data.begin() + 4, + std::back_inserter(out)); +// out == { 5, 6, 7, 8, 9, 1, 2, 3, 4 } \ No newline at end of file diff --git a/code_examples/algorithms/search_code.h b/code_examples/algorithms/search_code.h index bb3d4d4..6dbe535 100644 --- a/code_examples/algorithms/search_code.h +++ b/code_examples/algorithms/search_code.h @@ -7,4 +7,4 @@ auto it1 = std::search(haystack.begin(), haystack.end(), auto it2 = std::find_end(haystack.begin(), haystack.end(), needle.begin(), needle.end()); -// it2..end == "bba" +// it2..end == "bba" \ No newline at end of file diff --git a/code_examples/algorithms/search_n_code.h b/code_examples/algorithms/search_n_code.h index b057854..fa45a49 100644 --- a/code_examples/algorithms/search_n_code.h +++ b/code_examples/algorithms/search_n_code.h @@ -8,4 +8,4 @@ auto it2 = std::search_n(data.begin(), data.end(), 3, 3, // *it2 == 8, i.e. {8, 3, 3} auto it3 = std::search_n(data.begin(), data.end(), 2, 0); -// it3 == data.end(), i.e. not found +// it3 == data.end(), i.e. not found \ No newline at end of file diff --git a/code_examples/algorithms/searchers_code.h b/code_examples/algorithms/searchers_code.h index 5b0b2f5..816ce43 100644 --- a/code_examples/algorithms/searchers_code.h +++ b/code_examples/algorithms/searchers_code.h @@ -9,6 +9,4 @@ auto it2 = std::search(haystack.begin(), haystack.end(), auto it3 = std::search(haystack.begin(), haystack.end(), std::boyer_moore_horspool_searcher(needle.begin(), needle.end())); -// it1 == it2 == it3 - - +// it1 == it2 == it3 \ No newline at end of file diff --git a/code_examples/algorithms/set_difference_equal_code.h b/code_examples/algorithms/set_difference_equal_code.h index 11dadd5..1af7d82 100644 --- a/code_examples/algorithms/set_difference_equal_code.h +++ b/code_examples/algorithms/set_difference_equal_code.h @@ -7,7 +7,8 @@ auto cmp = [](const auto& l, const auto& r) { return l.value < r.value; }; -std::vector equal1{{"first_a", 1}, {"first_b", 1}, {"first_c", 1}, {"first_d", 1}}; +std::vector equal1{{"first_a", 1}, {"first_b", 1}, + {"first_c", 1}, {"first_d", 1}}; std::vector equal2{{"second_a", 1}, {"second_b", 1}}; std::vector equal_difference; diff --git a/code_examples/algorithms/set_intersection_equal_code.h b/code_examples/algorithms/set_intersection_equal_code.h index 0789c4d..4595f55 100644 --- a/code_examples/algorithms/set_intersection_equal_code.h +++ b/code_examples/algorithms/set_intersection_equal_code.h @@ -8,7 +8,8 @@ auto cmp = [](const auto& l, const auto& r) { }; std::vector equal1{{"first_a", 1}, {"first_b", 2}}; -std::vector equal2{{"second_a", 1}, {"second_b", 2}, {"second_c", 2}}; +std::vector equal2{{"second_a", 1}, {"second_b", 2}, + {"second_c", 2}}; std::vector intersection; std::ranges::set_intersection(equal1, equal2, diff --git a/code_examples/algorithms/set_symmetric_difference_equal_code.h b/code_examples/algorithms/set_symmetric_difference_equal_code.h index 239703e..877bcd3 100644 --- a/code_examples/algorithms/set_symmetric_difference_equal_code.h +++ b/code_examples/algorithms/set_symmetric_difference_equal_code.h @@ -7,12 +7,12 @@ auto cmp = [](const auto& l, const auto& r) { return l.value < r.value; }; -std::vector equal1{{"first_a", 1}, {"first_b", 2}, {"first_c", 2}}; -std::vector equal2{{"second_a", 1}, {"second_b", 1}, {"second_c", 2}}; +std::vector equal1{{"first_a", 1}, {"first_b", 2}, + {"first_c", 2}}; +std::vector equal2{{"second_a", 1}, {"second_b", 1}, + {"second_c", 2}}; std::vector equal_symmetric_difference; std::ranges::set_symmetric_difference(equal1, equal2, std::back_inserter(equal_symmetric_difference), cmp); -// equal_symmetric_difference == { {"second_b", 1}, {"first_c", 2} } - - +// equal_symmetric_difference == { {"second_b", 1}, {"first_c", 2} } \ No newline at end of file diff --git a/code_examples/algorithms/set_union_code.h b/code_examples/algorithms/set_union_code.h index c976849..4736d8c 100644 --- a/code_examples/algorithms/set_union_code.h +++ b/code_examples/algorithms/set_union_code.h @@ -4,4 +4,4 @@ std::vector data2{2, 4, 6}; std::vector set_union; std::ranges::set_union(data1, data2, std::back_inserter(set_union)); -// set_union == { 1, 2, 3, 4, 5, 6 } +// set_union == { 1, 2, 3, 4, 5, 6 } \ No newline at end of file diff --git a/code_examples/algorithms/set_union_equal_code.h b/code_examples/algorithms/set_union_equal_code.h index b10c984..5ff2300 100644 --- a/code_examples/algorithms/set_union_equal_code.h +++ b/code_examples/algorithms/set_union_equal_code.h @@ -7,10 +7,13 @@ auto cmp = [](const auto& l, const auto& r) { return l.value < r.value; }; -std::vector equal1{{"first_a", 1}, {"first_b", 1}, {"first_c", 2}}; -std::vector equal2{{"second_a", 1}, {"second_b", 2}, {"second_c", 2}}; +std::vector equal1{{"first_a", 1}, {"first_b", 1}, + {"first_c", 2}}; +std::vector equal2{{"second_a", 1}, {"second_b", 2}, + {"second_c", 2}}; std::vector equal_union; std::ranges::set_union(equal1, equal2, std::back_inserter(equal_union), cmp); -// equal_union == { {"first_a", 1}, {"first_b", 1}, {"first_c", 2}, {"second_c", 2} } +// equal_union == { {"first_a", 1}, {"first_b", 1}, +// {"first_c", 2}, {"second_c", 2} } diff --git a/code_examples/algorithms/shift_code.h b/code_examples/algorithms/shift_code.h new file mode 100644 index 0000000..f61c4db --- /dev/null +++ b/code_examples/algorithms/shift_code.h @@ -0,0 +1,7 @@ +std::vector data{1,2,3,4,5,6,7,8,9}; +std::shift_left(data.begin(), data.end(), 3); +// data == {4, 5, 6, 7, 8, 9, 7, 8, 9} + +data = {1,2,3,4,5,6,7,8,9}; +std::shift_right(data.begin(), data.end(), 3); +// data == {1, 2, 3, 1, 2, 3, 4, 5, 6} \ No newline at end of file diff --git a/code_examples/algorithms/shift_move_code.h b/code_examples/algorithms/shift_move_code.h new file mode 100644 index 0000000..a5889ad --- /dev/null +++ b/code_examples/algorithms/shift_move_code.h @@ -0,0 +1,20 @@ +struct EmptyOnMove { + char value; + EmptyOnMove(char value) : value(value) {} + EmptyOnMove(EmptyOnMove&& src) : + value(std::exchange(src.value,'-')) {} + EmptyOnMove& operator=(EmptyOnMove&& src) { + value = std::exchange(src.value, '-'); + return *this; + } + EmptyOnMove(const EmptyOnMove&) = default; + EmptyOnMove& operator=(const EmptyOnMove&) = default; +}; + +int main() { + std::vector nontrivial{ + {'a'},{'b'},{'c'},{'d'},{'e'},{'f'},{'g'}}; + + std::shift_right(nontrivial.begin(), nontrivial.end(), 4); + // nontrivial == { '-', '-', '-', 'd', 'a', 'b', 'c' } +} \ No newline at end of file diff --git a/code_examples/algorithms/shuffle_code.h b/code_examples/algorithms/shuffle_code.h index 9c40a62..cf075a3 100644 --- a/code_examples/algorithms/shuffle_code.h +++ b/code_examples/algorithms/shuffle_code.h @@ -9,7 +9,8 @@ friend std::ostream& operator << (std::ostream& s, const Card& card) { {"Hearts", "Diamonds", "Clubs", "Spades"}; if (card.index >= 52) - throw std::domain_error("Card index has to be in the range 0..51"); + throw std::domain_error( + "Card index has to be in the range 0..51"); s << ranks[card.index%13] << " of " << suits[card.index/13]; return s; diff --git a/code_examples/algorithms/sort_code.h b/code_examples/algorithms/sort_code.h index ec74321..d8e9df1 100644 --- a/code_examples/algorithms/sort_code.h +++ b/code_examples/algorithms/sort_code.h @@ -5,4 +5,4 @@ std::sort(data1.begin(), data1.end()); std::list data2 = {9, 1, 8, 2, 7, 3, 6, 4, 5}; // std::sort(data.begin(), data.end()); // doesn't compile data2.sort(); -// data2 == {1, 2, 3, 4, 5, 6, 7, 8, 9} +// data2 == {1, 2, 3, 4, 5, 6, 7, 8, 9} \ No newline at end of file diff --git a/code_examples/algorithms/sort_heap_code.h b/code_examples/algorithms/sort_heap_code.h index 624c467..dadde1b 100644 --- a/code_examples/algorithms/sort_heap_code.h +++ b/code_examples/algorithms/sort_heap_code.h @@ -4,4 +4,4 @@ std::make_heap(data.begin(), data.end()); // data == {9, 8, 7, 4, 5, 6, 3, 2, 1} - different ordering possible std::sort_heap(data.begin(), data.end()); -// data == {1, 2, 3, 4, 5, 6, 7, 8, 9} +// data == {1, 2, 3, 4, 5, 6, 7, 8, 9} \ No newline at end of file diff --git a/code_examples/algorithms/sort_projection_code.h b/code_examples/algorithms/sort_projection_code.h index 9efe63f..076848e 100644 --- a/code_examples/algorithms/sort_projection_code.h +++ b/code_examples/algorithms/sort_projection_code.h @@ -5,4 +5,4 @@ struct Account { std::vector accounts{{0.1}, {0.3}, {0.01}, {0.05}}; std::ranges::sort(accounts, std::greater<>{}, &Account::value); -// accounts = { {0.3}, {0.1}, {0.05}, {0.01} } +// accounts = { {0.3}, {0.1}, {0.05}, {0.01} } \ No newline at end of file diff --git a/code_examples/algorithms/stable_partition_code.h b/code_examples/algorithms/stable_partition_code.h index 99d5b22..f51e0be 100644 --- a/code_examples/algorithms/stable_partition_code.h +++ b/code_examples/algorithms/stable_partition_code.h @@ -1,2 +1,2 @@ auto& widget = get_widget(); -std::ranges::stable_partition(widget.items, &Item::is_selected); +std::ranges::stable_partition(widget.items, &Item::is_selected); \ No newline at end of file diff --git a/code_examples/algorithms/stable_sort_code.h b/code_examples/algorithms/stable_sort_code.h index c0141ad..7c59dd2 100644 --- a/code_examples/algorithms/stable_sort_code.h +++ b/code_examples/algorithms/stable_sort_code.h @@ -3,8 +3,9 @@ struct Record { int rank; }; -std::vector data {{"q", 1}, {"f", 1}, {"c", 2}, {"a", 1}, {"d", 3}}; +std::vector data {{"q", 1}, {"f", 1}, {"c", 2}, + {"a", 1}, {"d", 3}}; -std::ranges::stable_sort(data, {}, &Record::label); +std::ranges::stable_sort(data, {}, &Record::label); std::ranges::stable_sort(data, {}, &Record::rank); -// Guarantted order: a-1, f-1, q-1, c-2, d-3 +// Guarantted order: a-1, f-1, q-1, c-2, d-3 \ No newline at end of file diff --git a/code_examples/algorithms/swap_calling_code.h b/code_examples/algorithms/swap_calling_code.h index b240385..0530bd0 100644 --- a/code_examples/algorithms/swap_calling_code.h +++ b/code_examples/algorithms/swap_calling_code.h @@ -1,4 +1,4 @@ void some_algorithm(auto& x, auto& y) { using std::swap; swap(x, y); -} +} \ No newline at end of file diff --git a/code_examples/algorithms/swap_ranges_code.h b/code_examples/algorithms/swap_ranges_code.h index e475d3b..2faf516 100644 --- a/code_examples/algorithms/swap_ranges_code.h +++ b/code_examples/algorithms/swap_ranges_code.h @@ -1,3 +1,3 @@ std::vector data{ 1, 2, 3, 4, 5, 6, 7, 8, 9}; std::swap_ranges(data.begin(), data.begin()+3, data.rbegin()); -// data = { 9, 8, 7, 4, 5, 6, 3, 2, 1 } +// data = { 9, 8, 7, 4, 5, 6, 3, 2, 1 } \ No newline at end of file diff --git a/code_examples/algorithms/transform_inclusive_code.h b/code_examples/algorithms/transform_inclusive_code.h index a93ab69..036e6ea 100644 --- a/code_examples/algorithms/transform_inclusive_code.h +++ b/code_examples/algorithms/transform_inclusive_code.h @@ -9,4 +9,4 @@ std::vector out2; std::transform_inclusive_scan(data.begin(), data.end(), std::back_inserter(out2), std::plus<>{}, [](int v) { return std::abs(v); }); -// out2 == {10, 13, 15, 20, 26} +// out2 == {10, 13, 15, 20, 26} \ No newline at end of file diff --git a/code_examples/algorithms/transform_reduce_code.h b/code_examples/algorithms/transform_reduce_code.h index 5ce78b6..aab02cd 100644 --- a/code_examples/algorithms/transform_reduce_code.h +++ b/code_examples/algorithms/transform_reduce_code.h @@ -6,4 +6,4 @@ auto sum_of_squares = std::transform_reduce(data.begin(), data.end(), std::vector coef{1, -1, 1, -1, 1}; auto result = std::transform_reduce(data.begin(), data.end(), coef.begin(), 0); -// result == 1*1 + 2*(-1) + 3*1 + 4*(-1) + 5*1 == 3 +// result == 1*1 + 2*(-1) + 3*1 + 4*(-1) + 5*1 == 3 \ No newline at end of file diff --git a/code_examples/algorithms/uninitialized_constr_code.h b/code_examples/algorithms/uninitialized_constr_code.h index 514edcc..ca0192e 100644 --- a/code_examples/algorithms/uninitialized_constr_code.h +++ b/code_examples/algorithms/uninitialized_constr_code.h @@ -2,15 +2,12 @@ alignas(alignof(std::string)) char buffer[sizeof(std::string)*10]; auto *begin = reinterpret_cast(buffer); auto *it = begin; -std::uninitialized_default_construct_n(it, 3); -it += 3; -std::uninitialized_fill_n(it, 2, "Hello World!"); -it += 2; -std::uninitialized_value_construct_n(it, 3); -it += 3; -std::uninitialized_fill_n(it, 2, "Bye World!"); +it = std::uninitialized_default_construct_n(it, 3); +it = std::uninitialized_fill_n(it, 2, "Hello World!"); +it = std::uninitialized_value_construct_n(it, 3); +it = std::uninitialized_fill_n(it, 2, "Bye World!"); -// {"", "", "", "Hello World!", "Hello World!", "", "", "", "Bye World!", "Bye World!"} - -std::destroy_n(begin, 10); +// {"", "", "", "Hello World!", "Hello World!", "", "", "", +// "Bye World!", "Bye World!"} +std::destroy_n(begin, 10); \ No newline at end of file diff --git a/code_examples/algorithms/uninitialized_copy_code.h b/code_examples/algorithms/uninitialized_copy_code.h index e66cec0..3f7ebee 100644 --- a/code_examples/algorithms/uninitialized_copy_code.h +++ b/code_examples/algorithms/uninitialized_copy_code.h @@ -1,6 +1,7 @@ alignas(alignof(std::string)) char buff1[sizeof(std::string)*5]; alignas(alignof(std::string)) char buff2[sizeof(std::string)*5]; -std::vector data = {"hello", "world", "and", "everyone", "else"}; +std::vector data = { + "hello", "world", "and", "everyone", "else"}; auto *bg1 = reinterpret_cast(buff1); std::uninitialized_copy(data.begin(), data.end(), bg1); diff --git a/code_examples/ranges/borrowed_optin_code.h b/code_examples/ranges/borrowed_optin_code.h index f80215a..122ba93 100644 --- a/code_examples/ranges/borrowed_optin_code.h +++ b/code_examples/ranges/borrowed_optin_code.h @@ -1,2 +1,2 @@ -template -inline constexpr bool std::ranges::enable_borrowed_range> = true; +template inline constexpr bool + std::ranges::enable_borrowed_range> = true; diff --git a/code_examples/ranges/projected_type_code.h b/code_examples/ranges/projected_type_code.h index b74b879..3a4f614 100644 --- a/code_examples/ranges/projected_type_code.h +++ b/code_examples/ranges/projected_type_code.h @@ -7,9 +7,9 @@ std::vector out1; // std::vector out1; would not compile std::ranges::copy_if(data, std::back_inserter(out1), [](B) { return true; }, // predicate accepts B - [](A) { return B{}; }); // projection projects A->B for the predicate only + [](A) { return B{}; }); // projection projects A->B std::vector out2; std::ranges::transform(data, std::back_inserter(out2), [](auto x) { return x; }, // no-op transformation functor - [](A) { return B{}; }); // projection projects A->B for the functor only + [](A) { return B{}; }); // projection projects A->B diff --git a/code_examples/ranges/stateful_code.h b/code_examples/ranges/stateful_code.h new file mode 100644 index 0000000..2c622ba --- /dev/null +++ b/code_examples/ranges/stateful_code.h @@ -0,0 +1,22 @@ +std::list data{1, 2, 3, 4, 5, 6, 7, 8}; +auto view = data | std::views::drop(3); + +for (auto v : view) { + // iterate over: 4, 5, 6, 7, 8 +} + +// Note, if we used std::vector, push could invalidate +// the cached iterator inside of std::views::drop. +data.push_front(99); +for (auto v : view) { + // iterate over: 4, 5, 6, 7, 8 +} + +// Fresh view +for (auto v : data | std::views::drop(3)) { + // iterate over: 3, 4, 5, 6, 7, 8 +} + +const auto view2 = data | std::views::drop(3); +// for (auto v : view2) {} +// Wouldn't compile, std::views::drop requires mutability. \ No newline at end of file diff --git a/code_examples/theory/deduction_algorithm.h b/code_examples/theory/deduction_algorithm.h new file mode 100644 index 0000000..d46d1d2 --- /dev/null +++ b/code_examples/theory/deduction_algorithm.h @@ -0,0 +1,8 @@ +std::vector data{1, 2, 3, 4, 5, 6, 7, 8, 9}; + +auto v = std::accumulate(data.begin(), data.end(), 0); +// 0 is a literal of type int. Internally this means that +// the accumulator (and result) type of the algorithm will be +// int, despite iterating over a container of type unsigned. + +// v == 45, decltype(v) == int \ No newline at end of file diff --git a/code_examples/theory/deduction_concepts.h b/code_examples/theory/deduction_concepts.h new file mode 100644 index 0000000..f3061bf --- /dev/null +++ b/code_examples/theory/deduction_concepts.h @@ -0,0 +1,5 @@ +template concept IsInt = std::same_as; +void function(const IsInt auto&) {} + +function(0); // OK +// function(0u); // will fail to compile, deduced type unsigned \ No newline at end of file diff --git a/code_examples/theory/floating_point.h b/code_examples/theory/floating_point.h new file mode 100644 index 0000000..35e6b99 --- /dev/null +++ b/code_examples/theory/floating_point.h @@ -0,0 +1,9 @@ +auto src = UINT64_MAX - UINT32_MAX; +auto m = (1.0f * src) * 1.0L; +auto n = 1.0f * (src * 1.0L); +// decltype(m) == decltype(n) == long double + +std::cout << std::fixed << m << "\n" << n << "\n" << src << "\n"; +// 18446744073709551616.000000 +// 18446744069414584320.000000 +// 18446744069414584320 \ No newline at end of file diff --git a/code_examples/theory/floating_point_ordering.h b/code_examples/theory/floating_point_ordering.h new file mode 100644 index 0000000..b57653f --- /dev/null +++ b/code_examples/theory/floating_point_ordering.h @@ -0,0 +1,19 @@ +float v = 1.0f; +float next = std::nextafter(v, 2.0f); +// next is the next higher floating pointer number +float diff = (next-v)/2; +// diff is below the resolution of float +// importantly: v + diff == v + +std::vector data1(100, diff); +data1.front() = v; // data1 == { v, ... } +float r1 = std::accumulate(data1.begin(), data1.end(), 0.f); +// r1 == v +// we added diff 99 times, but each time, the value did not change + +std::vector data2(100, diff); +data2.back() = v; // data2 == { ..., v } +float r2 = std::accumulate(data2.begin(), data2.end(), 0.f); +// r2 != v +// we added diff 99 times, but we did that before adding to v +// the sum of 99 diffs is above the resolution threshold \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_different_a.h b/code_examples/theory/integral_conversions_different_a.h new file mode 100644 index 0000000..efedfb3 --- /dev/null +++ b/code_examples/theory/integral_conversions_different_a.h @@ -0,0 +1,5 @@ +int a = -100; +unsigned b = 0; + +auto v = a + b; +// v ~ -100 + (UINT_MAX + 1), decltype(v) == unsigned \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_different_b.h b/code_examples/theory/integral_conversions_different_b.h new file mode 100644 index 0000000..b0cab72 --- /dev/null +++ b/code_examples/theory/integral_conversions_different_b.h @@ -0,0 +1,5 @@ +unsigned a = 100; +long int b = -200; + +auto v = a + b; +// v = -100, decltype(v) == long int \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_different_c.h b/code_examples/theory/integral_conversions_different_c.h new file mode 100644 index 0000000..fdc56b8 --- /dev/null +++ b/code_examples/theory/integral_conversions_different_c.h @@ -0,0 +1,5 @@ +long long a = -100; +unsigned long b = 0; // assuming sizeof(long) == sizeof(long long) + +auto v = a + b; +// v ~ -100 + (ULLONG_MAX + 1), decltype(v) == unsigned long long \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_problems.h b/code_examples/theory/integral_conversions_problems.h new file mode 100644 index 0000000..d301355 --- /dev/null +++ b/code_examples/theory/integral_conversions_problems.h @@ -0,0 +1,9 @@ +int x = -1; +unsigned y = 1; +long z = -1; + +auto t1 = x > y; +// x -> unsigned, t1 == true + +auto t2 = z < y; +// y -> long, t2 == true \ No newline at end of file diff --git a/code_examples/theory/integral_conversions_same.h b/code_examples/theory/integral_conversions_same.h new file mode 100644 index 0000000..a1b1606 --- /dev/null +++ b/code_examples/theory/integral_conversions_same.h @@ -0,0 +1,5 @@ +int a = -100; +long int b = 500; + +auto v = a + b; +// v == 400, decltype(v) == long int \ No newline at end of file diff --git a/code_examples/theory/integral_promotions.h b/code_examples/theory/integral_promotions.h new file mode 100644 index 0000000..66b8844 --- /dev/null +++ b/code_examples/theory/integral_promotions.h @@ -0,0 +1,5 @@ +uint16_t a = 1; +uint16_t b = 2; + +auto v = a - b; +// v == -1, decltype(v) == int \ No newline at end of file diff --git a/code_examples/theory/references.h b/code_examples/theory/references.h new file mode 100644 index 0000000..fb6f317 --- /dev/null +++ b/code_examples/theory/references.h @@ -0,0 +1,15 @@ +void function(const int& v) {} + +long a = 0; +long long b = 0; +// Even when long and long long have the same size +static_assert(sizeof(a) == sizeof(b)); +// The two types are unrelated in the context of references +// The following two statements wouldn't compile: +// long long& c = a; +// long& d = b; + +// OK, but dangerous, implict conversion to int +// int temporary can bind to const int& +function(a); +function(b); \ No newline at end of file diff --git a/code_examples/theory/safe_compare.h b/code_examples/theory/safe_compare.h new file mode 100644 index 0000000..c21a88e --- /dev/null +++ b/code_examples/theory/safe_compare.h @@ -0,0 +1,11 @@ +int x = -1; +unsigned y = 1; +long z = -1; + +auto t1 = x > y; +auto t2 = std::cmp_greater(x,y); +// t1 == true, t2 == false + +auto t3 = z < y; +auto t4 = std::cmp_less(z,y); +// t3 == true, t4 == true \ No newline at end of file diff --git a/code_examples/theory/safe_in_range.h b/code_examples/theory/safe_in_range.h new file mode 100644 index 0000000..1a7326c --- /dev/null +++ b/code_examples/theory/safe_in_range.h @@ -0,0 +1,6 @@ +auto t1 = std::in_range(UINT_MAX); +// t1 == false +auto t2 = std::in_range(0); +// t2 == true +auto t3 = std::in_range(-1); +// t3 == false \ No newline at end of file diff --git a/code_examples/theory/safe_ssize.h b/code_examples/theory/safe_ssize.h new file mode 100644 index 0000000..b6792bb --- /dev/null +++ b/code_examples/theory/safe_ssize.h @@ -0,0 +1,8 @@ +std::vector data{1,2,3,4,5,6,7,8,9}; +// std::ssize returns ptrdiff_t, avoiding mixing +// a signed and unsigned integer in the comparison +for (ptrdiff_t i = 0; i < std::ssize(data); i++) { + std::cout << data[i] << " "; +} +std::cout << "\n"; +// prints: "1 2 3 4 5 6 7 8 9" \ No newline at end of file diff --git a/code_examples/views/elements_code.h b/code_examples/views/elements_code.h index 4c2e0f3..6aea3bf 100644 --- a/code_examples/views/elements_code.h +++ b/code_examples/views/elements_code.h @@ -3,9 +3,11 @@ std::vector> data{ }; std::vector second; -std::ranges::copy(data | std::views::elements<1>, std::back_inserter(second)); +std::ranges::copy(data | std::views::elements<1>, + std::back_inserter(second)); // second == {100, 99, 17} std::vector third; -std::ranges::copy(data | std::views::elements<2>, std::back_inserter(third)); -// third == {"Cat", "Dog", "Car"} +std::ranges::copy(data | std::views::elements<2>, + std::back_inserter(third)); +// third == {"Cat", "Dog", "Car"} \ No newline at end of file diff --git a/code_examples/views/keys_values_code.h b/code_examples/views/keys_values_code.h index 1544036..817d1cb 100644 --- a/code_examples/views/keys_values_code.h +++ b/code_examples/views/keys_values_code.h @@ -10,4 +10,4 @@ std::vector values; std::ranges::copy(std::views::values(map), std::back_inserter(values)); // values == {1.0, 1.5, 2.0, 2.5} -// in unspecified order matching order of keys +// in unspecified order matching order of keys \ No newline at end of file diff --git a/packages/color_blocks.tex b/packages/color_blocks.tex deleted file mode 100644 index 602ca3b..0000000 --- a/packages/color_blocks.tex +++ /dev/null @@ -1,28 +0,0 @@ -\usepackage[many]{tcolorbox} % for COLORED BOXES (tikz and xcolor included) - -\newtcolorbox{box-note}{ - sharpish corners, % better drop shadow - boxrule = 0pt, - toprule = 4.5pt, % top rule weight - enhanced, - breakable, - enlarge top by=5pt, - colback = white, - before skip = 0.2cm, % add extra space before the box - after skip = 0.5cm, % add extra space after the box - drop fuzzy shadow = black!35, - fontupper=\sffamily -} - -\newtcolorbox{box-nobreak}{ - sharpish corners, % better drop shadow - boxrule = 0pt, - toprule = 4.5pt, % top rule weight - enhanced, - enlarge top by=5pt, - colback = white, - before skip = 0.2cm, % add extra space before the box - after skip = 0.5cm, % add extra space after the box - drop fuzzy shadow = black!35, - fontupper=\sffamily -} \ No newline at end of file diff --git a/packages/custom_commands/CC.tex b/packages/custom_commands/CC.tex new file mode 100644 index 0000000..c3887fa --- /dev/null +++ b/packages/custom_commands/CC.tex @@ -0,0 +1,4 @@ +% Manually micro-typed "C++" + +%\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\raisebox{.25ex}{\small ++}\hspace{.1em}} +\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\sbox0{$1$}\sbox2{\small++}\raise\dimexpr(\ht0-\ht2)/2\relax\box2\hspace{.1em}} \ No newline at end of file diff --git a/packages/custom_commands/ExternalLink.tex b/packages/custom_commands/ExternalLink.tex new file mode 100644 index 0000000..f689ce4 --- /dev/null +++ b/packages/custom_commands/ExternalLink.tex @@ -0,0 +1,22 @@ +% Sourced from +% https://tex.stackexchange.com/questions/99316/symbol-for-external-links +\newcommand{\ExternalLink}{% +\tikz[x=1.2ex, y=1.2ex, baseline=-0.05ex]{% + \begin{scope}[x=1ex, y=1ex] + \clip (-0.1,-0.1) + --++ (-0, 1.2) + --++ (0.6, 0) + --++ (0, -0.6) + --++ (0.6, 0) + --++ (0, -1); + \path[draw, + line width = 0.5, + rounded corners=0.5] + (0,0) rectangle (1,1); + \end{scope} + \path[draw, line width = 0.5] (0.5, 0.5) + -- (1, 1); + \path[draw, line width = 0.5] (0.6, 1) + -- (1, 1) -- (1, 0.6); + } +} \ No newline at end of file diff --git a/packages/custom_commands/circled.tex b/packages/custom_commands/circled.tex new file mode 100644 index 0000000..2370d54 --- /dev/null +++ b/packages/custom_commands/circled.tex @@ -0,0 +1,3 @@ +\usepackage{enumitem} +\newcommand*\circled[1]{\tikz[baseline=(char.base)]{ + \node[shape=circle,draw,inner sep=1pt] (char) {#1};}} \ No newline at end of file diff --git a/packages/custom_commands/codebox.tex b/packages/custom_commands/codebox.tex new file mode 100644 index 0000000..15b30d8 --- /dev/null +++ b/packages/custom_commands/codebox.tex @@ -0,0 +1,26 @@ +\usepackage[many]{tcolorbox} % for COLORED BOXES (tikz and xcolor included) + +\newtcolorbox{codebox}[2][]{ + sharpish corners, % better drop shadow + boxrule = 0pt, + toprule = 4.5pt, % top rule weight + enhanced, + enlarge top by=5pt, + colback = white, + before skip = 0.2cm, % add extra space before the box + after skip = 0.5cm, % add extra space after the box + drop fuzzy shadow = black!35, + fontupper=\footnotesize\sffamily, + fonttitle=\footnotesize\sffamily\color{black}, + title=Open in Compiler Explorer #2, + attach boxed title to bottom right={xshift=6pt,yshift=6pt}, + boxed title style={ + enhanced, + arc=0pt, + outer arc=0pt, + boxrule = 0pt, + colback = white, + drop fuzzy shadow = black!35, + }, + #1, +} \ No newline at end of file diff --git a/packages/local.tex b/packages/custom_commands/constraints.tex similarity index 55% rename from packages/local.tex rename to packages/custom_commands/constraints.tex index 76a389e..fa30ee1 100644 --- a/packages/local.tex +++ b/packages/custom_commands/constraints.tex @@ -1,25 +1,3 @@ -\usepackage{sidenotes} % Required -\usepackage{multirow} % Required -\usepackage{diagbox} % Used for boolean algorithms. - -% Command to consistently format side-panel with C++ versions that introduced different variants of the algorithm. -\newcommand{\cppversions}[5]{ -\begin{margintable} -\footnotesize -\begin{tabular}{|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|} -\rowcolor{black!80} \multicolumn{2}{c}{\textcolor{white}{#1}} \\ -introduced & #2 \\ -\hline -constexpr & #3 \\ -\hline -parallel & #4 \\ -\hline -rangified & #5 \\ -\hline -\end{tabular} -\end{margintable} -} - % Command to consistently format the algorithm info table. \newcommand{\constraints}[4]{ \begin{center} diff --git a/packages/code_blocks.tex b/packages/custom_commands/cpp.tex similarity index 76% rename from packages/code_blocks.tex rename to packages/custom_commands/cpp.tex index 972f694..eda6cf9 100644 --- a/packages/code_blocks.tex +++ b/packages/custom_commands/cpp.tex @@ -10,25 +10,13 @@ \if\isOverleaf% \usepackage[newfloat=true]{minted} \else -\usepackage[newfloat=true,outputdir=build]{minted} +\usepackage[newfloat=true,cachedir=mint,outputdir=build]{minted} \fi \usepackage[]{xcolor} \usemintedstyle{xcode} - \renewcommand\theFancyVerbLine{\footnotesize\arabic{FancyVerbLine}} -\newminted{cpp}{ - linenos=true, - firstnumber=1, - tabsize=4, - obeytabs, - xleftmargin=1em, - breaklines, - fontsize=\footnotesize, - numbersep=0.75em -} - \newmintedfile[cppfile]{cpp}{ linenos=true, firstnumber=1, diff --git a/packages/custom_commands/cppversions.tex b/packages/custom_commands/cppversions.tex new file mode 100644 index 0000000..71ac5fd --- /dev/null +++ b/packages/custom_commands/cppversions.tex @@ -0,0 +1,18 @@ +% Command to consistently format side-panel with C++ versions +% that introduced different variants of the algorithm. +\newcommand{\cppversions}[5]{ +\begin{margintable} +\footnotesize +\begin{tabular}{|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|m{\dimexpr.5\marginparwidth-2\tabcolsep-1pt}|} +\rowcolor{black!80} \multicolumn{2}{c}{\textcolor{white}{#1}} \\ +introduced & #2 \\ +\hline +constexpr & #3 \\ +\hline +parallel & #4 \\ +\hline +rangified & #5 \\ +\hline +\end{tabular} +\end{margintable} +} \ No newline at end of file diff --git a/packages/custom_commands/version.tex b/packages/custom_commands/version.tex new file mode 100644 index 0000000..9089338 --- /dev/null +++ b/packages/custom_commands/version.tex @@ -0,0 +1 @@ +\newcommand{\version}{1.0.0} \ No newline at end of file diff --git a/packages/fonts.tex b/packages/fonts.tex index 625447d..bb09222 100644 --- a/packages/fonts.tex +++ b/packages/fonts.tex @@ -32,8 +32,4 @@ Contextuals=AlternateOff ] -\renewcommand{\familydefault}{\sfdefault} - -% Manually adjusted C++ -%\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\raisebox{.25ex}{\small ++}\hspace{.1em}} -\newcommand{\CC}{C\nolinebreak[4]\hspace{-.1em}\sbox0{$1$}\sbox2{\small++}\raise\dimexpr(\ht0-\ht2)/2\relax\box2\hspace{.1em}} \ No newline at end of file +\renewcommand{\familydefault}{\sfdefault} \ No newline at end of file diff --git a/packages/glossary_and_index.tex b/packages/glossary_and_index.tex deleted file mode 100644 index fd31198..0000000 --- a/packages/glossary_and_index.tex +++ /dev/null @@ -1,4 +0,0 @@ -\usepackage{glossaries} -\makeglossaries -\usepackage{imakeidx} -\makeindex \ No newline at end of file diff --git a/packages/hyperref.tex b/packages/hyperref.tex new file mode 100644 index 0000000..4b0aa75 --- /dev/null +++ b/packages/hyperref.tex @@ -0,0 +1,9 @@ +\usepackage{hyperref} +\hypersetup{ + colorlinks=true, + linkcolor=blue, + filecolor=magenta, + urlcolor=cyan, + pdftitle={Standard C++ Algorithms}, + pdfpagemode=FullScreen, + } \ No newline at end of file diff --git a/packages/packages.tex b/packages/packages.tex new file mode 100644 index 0000000..cc5556d --- /dev/null +++ b/packages/packages.tex @@ -0,0 +1,20 @@ +\usepackage{sidenotes} % Required +\usepackage{multirow} % Required +\usepackage{diagbox} % Used for boolean algorithms. +\usepackage{tikz} % Used for External link icon + +\usepackage{imakeidx} +\makeindex + +\input{packages/fonts} +\input{packages/hyperref} + +% Custom commands +\input{packages/custom_commands/ExternalLink} +\input{packages/custom_commands/cppversions} +\input{packages/custom_commands/constraints} +\input{packages/custom_commands/CC} +\input{packages/custom_commands/circled} +\input{packages/custom_commands/version} +\input{packages/custom_commands/codebox} +\input{packages/custom_commands/cpp} \ No newline at end of file From f27fd58c89ee1ca52d70089d1629239a0c85adff Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Wed, 1 Feb 2023 17:59:22 +0100 Subject: [PATCH 33/44] Update README.md Update for v1.0.0 release. --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e4b1a8e..a98c87f 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v0.3.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) +[Latest PDF release (v1.0.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v0.3.0/book_with_cover_v0.3.0.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) ## Changelog +- `1.0.0` Content complete release with Compiler Explorer links. - `0.3.0` New chapter with ADL information and formatting cleanup. - `0.2.1` Fixed page numbering issue, small text changes. - `0.2.0` Added chapter covering C++20 ranges and views. From f136c65f94863140ed349e754fd6400a29640e48 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Thu, 2 Feb 2023 08:43:56 +0000 Subject: [PATCH 34/44] Add missing closing parenthesis. --- chapters/03_algorithms_05_divide.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index 2fc9309..e025d04 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -10,7 +10,7 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \index{\cpp{std::lower_bound}} \index{\cpp{std::upper_bound}} -The \cpp{std::lower_bound} and \cpp{std::upper_bound} algorithms offer boundary search with logarithmic complexity (for random access ranges. +The \cpp{std::lower_bound} and \cpp{std::upper_bound} algorithms offer boundary search with logarithmic complexity (for random access ranges). \cppversions{\texttt{lower\_bound}}{\CC98}{\CC20}{N/A}{\CC20} \cppversions{\texttt{upper\_bound}}{\CC98}{\CC20}{N/A}{\CC20} From 0f7d39812a8a02fe86b9b32e7fffc0ffc31b01f4 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Thu, 2 Feb 2023 11:48:00 +0000 Subject: [PATCH 35/44] Adjust O(n*logn) formatting. --- chapters/03_algorithms_03_sorting.tex | 6 +++--- chapters/03_algorithms_05_divide.tex | 2 +- chapters/03_algorithms_15_heap.tex | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/chapters/03_algorithms_03_sorting.tex b/chapters/03_algorithms_03_sorting.tex index 4351d54..fd25854 100644 --- a/chapters/03_algorithms_03_sorting.tex +++ b/chapters/03_algorithms_03_sorting.tex @@ -79,12 +79,12 @@ \subsection{\texorpdfstring{\cpp{std::lexicographical_compare_three_way}}{\textt \subsection{\texorpdfstring{\cpp{std::sort}}{\texttt{std::sort}}} \index{\cpp{std::sort}} -The \cpp{std::sort} algorithm is the canonical $O(n*logn)$ sort (typically implemented as intro-sort). +The \cpp{std::sort} algorithm is the canonical $O(n\log n)$ sort (typically implemented as intro-sort). \cppversions{\texttt{sort}}{\CC98}{\CC20}{\CC17}{\CC20} \constraints{\cpp{random_access_range}}{\cpp{random_access_range}}{\cpp{operator<}}{\cpp{strict_weak_ordering}} -Due to the $O(n*logn)$ complexity guarantee, \cpp{std::sort} only operates on \cpp{random_access} ranges. Notably, \cpp{std::list} offers a method with an approximate $n*logn$ complexity. +Due to the $O(n\log n)$ complexity guarantee, \cpp{std::sort} only operates on \cpp{random_access} ranges. Notably, \cpp{std::list} offers a method with an approximate $n\log n$ complexity. \begin{codebox}[]{\href{https://compiler-explorer.com/z/vef61TWj9}{\ExternalLink}} \footnotesize Basic example of using \cpp{std::sort} and \cpp{std::list::sort}. @@ -110,7 +110,7 @@ \subsection{\texorpdfstring{\cpp{std::stable_sort}}{\texttt{std::stable\_sort}}} \cppversions{\texttt{stable\_sort}}{\CC98}{N/A}{\CC17}{\CC20} \constraints{\cpp{random_access_range}}{}{\cpp{operator<}}{\cpp{strict_weak_ordering}} -If additional memory is available, stable\_sort remains $O(n*logn)$. However, if it fails to allocate, it will degrade to an $O(n*logn*logn)$ algorithm. +If additional memory is available, stable\_sort remains $O(n\log n)$. However, if it fails to allocate, it will degrade to an $O(n\log n\log n)$ algorithm. \begin{codebox}[]{\href{https://compiler-explorer.com/z/TKx8qP8bK}{\ExternalLink}} \footnotesize Example of re-sorting a range using \cpp{std::stable_sort}, resulting in a guaranteed order of elements. diff --git a/chapters/03_algorithms_05_divide.tex b/chapters/03_algorithms_05_divide.tex index e025d04..98e8c51 100644 --- a/chapters/03_algorithms_05_divide.tex +++ b/chapters/03_algorithms_05_divide.tex @@ -30,7 +30,7 @@ \subsection{\texorpdfstring{\cpp{std::lower_bound}, \cpp{std::upper_bound}}{\tex \cppfile{code_examples/algorithms/lower_bound_code.h} \end{codebox} -While the algorithms will operate on any \cpp{forward_range}, the logarithmic divide and conquer behaviour is only available for \cpp{random_access_range}. Data structures like \cpp{std::set}, \cpp{std::multiset}, \cpp{std::map} and \cpp{std::multimap} offer their $O(logn)$ implementations of lower and upper bound as methods. +While the algorithms will operate on any \cpp{forward_range}, the logarithmic divide and conquer behaviour is only available for \cpp{random_access_range}. Data structures like \cpp{std::set}, \cpp{std::multiset}, \cpp{std::map} and \cpp{std::multimap} offer their $O(\log n)$ implementations of lower and upper bound as methods. \begin{codebox}[]{\href{https://compiler-explorer.com/z/o9Wdvzno9}{\ExternalLink}} \footnotesize Example of using \cpp{lower_bound} and \cpp{upper_bound} methods on a \cpp{std::multiset}. diff --git a/chapters/03_algorithms_15_heap.tex b/chapters/03_algorithms_15_heap.tex index 89526b4..e2db31d 100644 --- a/chapters/03_algorithms_15_heap.tex +++ b/chapters/03_algorithms_15_heap.tex @@ -39,7 +39,7 @@ \subsection{\texorpdfstring{\cpp{std::make_heap}, \cpp{std::push_heap}, \cpp{std \subsection{\texorpdfstring{\cpp{std::sort_heap}}{\texttt{std::sort\_heap}}} \index{\cpp{std::sort_heap}} -The \cpp{std::sort_heap} will reorder the elements in a heap into a sorted order. Note that this is the same as repeatedly calling \cpp{std::pop_heap}; hence the algorithm has $O(n*logn)$ complexity. +The \cpp{std::sort_heap} will reorder the elements in a heap into a sorted order. Note that this is the same as repeatedly calling \cpp{std::pop_heap}; hence the algorithm has $O(n\log n)$ complexity. \cppversions{\texttt{sort\_heap}}{\CC98}{\CC20}{N/A}{\CC20} \constraints{\texttt{random\_access\_range}}{}{\texttt{operator<}}{\texttt{strict\_weak\_ordering}} From 4cb5bfdf5c33576c03b61fd163cb91aff098c4fb Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 18:34:49 +0000 Subject: [PATCH 36/44] Replace non-standard ssize_t. --- chapters/02_introduction.tex | 2 +- chapters/03_algorithms_01_foreach.tex | 2 +- code_examples/algorithms/for_each_n.cpp | 2 +- code_examples/algorithms/for_each_n_code.h | 2 +- code_examples/introduction/categories_code.h | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/chapters/02_introduction.tex b/chapters/02_introduction.tex index 50599c2..9a29019 100644 --- a/chapters/02_introduction.tex +++ b/chapters/02_introduction.tex @@ -82,7 +82,7 @@ \subsection{Iterator categories} \textit{arrays, e.g. std::vector}\index{\cpp{std::vector}} \end{description} -\begin{codebox}[]{\href{https://godbolt.org/z/dcaMEdnoM}{\ExternalLink}} +\begin{codebox}[]{\href{https://godbolt.org/z/aWT1Wajva}{\ExternalLink}} \footnotesize Example demonstrating the difference between a random access iterator provided by \cpp{std::vector} and a bidirectional iterator provided by \cpp{std::list}. \tcblower \cppfile{code_examples/introduction/categories_code.h} diff --git a/chapters/03_algorithms_01_foreach.tex b/chapters/03_algorithms_01_foreach.tex index 0c7e8b3..2ceed90 100644 --- a/chapters/03_algorithms_01_foreach.tex +++ b/chapters/03_algorithms_01_foreach.tex @@ -97,7 +97,7 @@ \subsection{\texorpdfstring{\cpp{std::for_each_n}}{\texttt{std::for\_each\_n}}} \raggedbottom -\begin{codebox}[]{\href{https://compiler-explorer.com/z/1hfje76Yq}{\ExternalLink}} +\begin{codebox}[]{\href{https://compiler-explorer.com/z/o8rEzc6q3}{\ExternalLink}} \footnotesize Example demonstrating multiple uses of \cpp{std::for_each_n}. \tcblower \cppfile{code_examples/algorithms/for_each_n_code.h} diff --git a/code_examples/algorithms/for_each_n.cpp b/code_examples/algorithms/for_each_n.cpp index 7a6e8ad..eab3cce 100644 --- a/code_examples/algorithms/for_each_n.cpp +++ b/code_examples/algorithms/for_each_n.cpp @@ -5,7 +5,7 @@ #include inline constexpr const size_t MAIN_SEATS = 2; -inline constexpr const ssize_t PAGE_SIZE = 2; +inline constexpr const size_t PAGE_SIZE = 2; struct Player { std::string display_name; diff --git a/code_examples/algorithms/for_each_n_code.h b/code_examples/algorithms/for_each_n_code.h index b504025..14922b4 100644 --- a/code_examples/algorithms/for_each_n_code.h +++ b/code_examples/algorithms/for_each_n_code.h @@ -9,7 +9,7 @@ std::for_each_n(std::execution::par_unseq, auto it = final_ranking.begin(); uint32_t page = 0; while (it != final_ranking.end()) { - ssize_t cnt = std::min(PAGE_SIZE, final_ranking.end() - it); + size_t cnt = std::min(PAGE_SIZE, size_t(final_ranking.end() - it)); std::for_each_n(it, cnt, [page](const Player& p) { store_final_score(page, p.display_name, p.score); }); diff --git a/code_examples/introduction/categories_code.h b/code_examples/introduction/categories_code.h index f1cd3cc..33c59e1 100644 --- a/code_examples/introduction/categories_code.h +++ b/code_examples/introduction/categories_code.h @@ -3,7 +3,7 @@ auto it1 = arr.begin(); it1 += 5; // OK, std::vector provides random access iterator ++it1; // OK, all iterators provide advance operation -ssize_t dst1 = it1 - arr.begin(); // OK, random access iterator +ptrdiff_t dst1 = it1 - arr.begin(); // OK, random access iterator // dst1 == 6 std::list lst = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; @@ -13,5 +13,5 @@ std::advance(it2, 5); // OK, linear advance by 5 steps ++it2; // OK, all iterators provide advance operation // it2 - lst.begin(); Would not compile -ssize_t dst2 = std::distance(lst.begin(), it2); // OK, linear calc. +ptrdiff_t dst2 = std::distance(lst.begin(), it2); // OK, linear calc. // dst2 == 6 From 6cce920bc6de220bce3d5850f196f6226d073b73 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:10:07 +0000 Subject: [PATCH 37/44] Custom margin for title and copyright page. --- chapters/00_title_page.tex | 4 +++- packages/packages.tex | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index 402505f..b6a7b15 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -1,3 +1,4 @@ +\newgeometry{margin=5cm} \begin{titlepage} \centering \vspace{2cm} @@ -41,4 +42,5 @@ \endgroup \clearpage -\pagestyle{plain} \ No newline at end of file +\pagestyle{plain} +\restoregeometry \ No newline at end of file diff --git a/packages/packages.tex b/packages/packages.tex index cc5556d..c7b4f71 100644 --- a/packages/packages.tex +++ b/packages/packages.tex @@ -6,6 +6,8 @@ \usepackage{imakeidx} \makeindex +\usepackage{geometry} + \input{packages/fonts} \input{packages/hyperref} From 4096f285a5489de9d3b31a73db69e88dc7accd9b Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:10:37 +0000 Subject: [PATCH 38/44] Update version for new release. --- packages/custom_commands/version.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/custom_commands/version.tex b/packages/custom_commands/version.tex index 9089338..e0ac109 100644 --- a/packages/custom_commands/version.tex +++ b/packages/custom_commands/version.tex @@ -1 +1 @@ -\newcommand{\version}{1.0.0} \ No newline at end of file +\newcommand{\version}{1.0.1} \ No newline at end of file From 406b52c7a0cee2a81a86dd88a899b1491f1380cd Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:14:50 +0000 Subject: [PATCH 39/44] Update changelog. --- chapters/01_preface.tex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chapters/01_preface.tex b/chapters/01_preface.tex index 52d5d3f..c3f4536 100644 --- a/chapters/01_preface.tex +++ b/chapters/01_preface.tex @@ -41,5 +41,6 @@ \section*{Book status} \subsection*{Changelog} \begin{enumerate} - \item[1.0] First complete release + \item[1.0.1] Small (mostly) formatting changes. + \item[1.0.0] First complete release. \end{enumerate} \ No newline at end of file From b13443990832d96f73dcd54a7394217004f2aca7 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Fri, 3 Feb 2023 19:38:13 +0000 Subject: [PATCH 40/44] Revert "Custom margin for title and copyright page." This reverts commit 6cce920bc6de220bce3d5850f196f6226d073b73. --- chapters/00_title_page.tex | 4 +--- packages/packages.tex | 2 -- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/chapters/00_title_page.tex b/chapters/00_title_page.tex index b6a7b15..402505f 100644 --- a/chapters/00_title_page.tex +++ b/chapters/00_title_page.tex @@ -1,4 +1,3 @@ -\newgeometry{margin=5cm} \begin{titlepage} \centering \vspace{2cm} @@ -42,5 +41,4 @@ \endgroup \clearpage -\pagestyle{plain} -\restoregeometry \ No newline at end of file +\pagestyle{plain} \ No newline at end of file diff --git a/packages/packages.tex b/packages/packages.tex index c7b4f71..cc5556d 100644 --- a/packages/packages.tex +++ b/packages/packages.tex @@ -6,8 +6,6 @@ \usepackage{imakeidx} \makeindex -\usepackage{geometry} - \input{packages/fonts} \input{packages/hyperref} From 299a8a57228a04f3bcbeab8d812387c8c0f57870 Mon Sep 17 00:00:00 2001 From: "RNDr. Simon Toth" Date: Sat, 4 Feb 2023 09:43:18 +0100 Subject: [PATCH 41/44] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a98c87f..21cebfc 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,13 @@ This repository contains the LaTeX source and C++ code samples for the book "A Complete Guide to Standard C++ Algorithms". -[Latest PDF release (v1.0.0)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) +[Latest PDF release (v1.0.1)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.1/a_complete_guide_to_standard_cpp_algorithms_v1_0_1.pdf) -[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.0/book_with_cover_v1.0.0.pdf) +[![Book Cover](static/book_cover.png)](https://github.com/HappyCerberus/book-cpp-algorithms/releases/download/v1.0.1/a_complete_guide_to_standard_cpp_algorithms_v1_0_1.pdf) ## Changelog +- `1.0.1` Small (mostly) formatting fixes. - `1.0.0` Content complete release with Compiler Explorer links. - `0.3.0` New chapter with ADL information and formatting cleanup. - `0.2.1` Fixed page numbering issue, small text changes. From fedf78f01606b3c0a4c88e9590b12d6f7c91a922 Mon Sep 17 00:00:00 2001 From: wxinix Date: Wed, 5 Apr 2023 08:48:14 -0400 Subject: [PATCH 42/44] Update READEME with additional info about alternative approach of building from sources using TexStudio. --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 21cebfc..c432bc0 100644 --- a/README.md +++ b/README.md @@ -31,3 +31,16 @@ The resulting PDF will be in the build folder. Most code files have wrapping main files that exercise both the build and also contain `assert` expressions that verify correctness of the code. The `verify.sh` shell script will build all (except for a few that don't compile with GCC 11) examples, and then run each of them to validate all asserts. + +## Alternative approach of building from sources + +If you're building from sources inside a virtual machine and the VSCode and Docker approach doesn't work for you, [TexStudio](https://www.texstudio.org/) provides an alternative solution for Windows users. Follow the steps below: + +- Install [Anaconda](https://www.anaconda.com/) and add `C:\Users\YourUserName\anaconda3\Scripts` to system path. Replace `YourUserName` with your actual user name. +- Install [TexStudio](https://www.texstudio.org/). +- Go to Texstudio menu "Options -> Configure Texstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". +- Go to Texstudio menu "Options -> Configure Texstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". +- For Texstudio menu "Options -> Configure Texstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. +- Finally, go to "Texstudio menu Options -> Configure Texstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` + +These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. From 68fe51a2c2770f5c767594c85c3b2de322f0bd6e Mon Sep 17 00:00:00 2001 From: wxinix Date: Wed, 5 Apr 2023 10:51:23 -0400 Subject: [PATCH 43/44] Update README to better clarify the alternative solution for building from sources using Texstudio inside a virtual machine on Windows. --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c432bc0..e308e47 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,15 @@ The `verify.sh` shell script will build all (except for a few that don't compile ## Alternative approach of building from sources -If you're building from sources inside a virtual machine and the VSCode and Docker approach doesn't work for you, [TexStudio](https://www.texstudio.org/) provides an alternative solution for Windows users. Follow the steps below: +This section was provided by [@wxinix](https://github.com/wxinix). Please direct any questions to him. + +[Texstudio](https://www.texstudio.org/) offers an alternative solution for Windows users who are building from sources within a virtual machine and are unable to use the VSCode and Docker approach. Follow the steps below: - Install [Anaconda](https://www.anaconda.com/) and add `C:\Users\YourUserName\anaconda3\Scripts` to system path. Replace `YourUserName` with your actual user name. -- Install [TexStudio](https://www.texstudio.org/). +- Install [Texstudio](https://www.texstudio.org/). - Go to Texstudio menu "Options -> Configure Texstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". - Go to Texstudio menu "Options -> Configure Texstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". - For Texstudio menu "Options -> Configure Texstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. - Finally, go to "Texstudio menu Options -> Configure Texstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` -These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. +These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. Ask @wxinix if you have any questions. From f81c7c49f4314a88924472b9569e4a981d60f44d Mon Sep 17 00:00:00 2001 From: wxinix Date: Wed, 5 Apr 2023 18:32:02 -0400 Subject: [PATCH 44/44] Update README with typo fixes to better explain the alternative solution for building from sources using TeXstudio in a virtual machine environment. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e308e47..e9d3208 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ The `verify.sh` shell script will build all (except for a few that don't compile This section was provided by [@wxinix](https://github.com/wxinix). Please direct any questions to him. -[Texstudio](https://www.texstudio.org/) offers an alternative solution for Windows users who are building from sources within a virtual machine and are unable to use the VSCode and Docker approach. Follow the steps below: +[TeXstudio](https://www.texstudio.org/) offers an alternative solution for those who are building from sources in a virtual machine environment and are unable to use the VSCode and Docker approach. Follow the steps below (assuming the Guest OS of the virtual machine is Windows): - Install [Anaconda](https://www.anaconda.com/) and add `C:\Users\YourUserName\anaconda3\Scripts` to system path. Replace `YourUserName` with your actual user name. -- Install [Texstudio](https://www.texstudio.org/). -- Go to Texstudio menu "Options -> Configure Texstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". -- Go to Texstudio menu "Options -> Configure Texstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". -- For Texstudio menu "Options -> Configure Texstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. -- Finally, go to "Texstudio menu Options -> Configure Texstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` +- Install [TeXstudio](https://www.texstudio.org/). +- Go to TeXstudio menu "Options -> Configure TeXstudio -> Commands -> XeLaTex", and enter the following command: `xelatex.exe -synctex=1 -interaction=nonstopmode -shell-escape -aux-directory=build -output-directory=build %.tex`. This command sets both the aux and output directories to ".\build". +- Go to TeXstudio menu "Options -> Configure TeXstudio -> Build -> Meta Commands -> Default Compiler", and enter `txs:///xelatex`. This switches the default compiler from `pdflatex` to `xelatex`. Then, enable the checkbox "Show Advanced Options". +- For TeXstudio menu "Options -> Configure TeXstudio -> Build -> Build Options -> PDF File", enter `build`. This tells the pdf previewer to look for the generated pdf file in the ".\build" directory. +- Finally, go to "TeXstudio menu Options -> Configure TeXstudio -> Commands -> Makeindex", and enter the following command: `makeindex.exe build%.idx` -These steps will enable you to build from sources inside a virtual machine using TexStudio on Windows. Ask @wxinix if you have any questions. +These steps will enable you to build from sources inside a virtual machine using TeXstudio. Keep in mind that the VSCode and Docker approach might not work in a virtual machine environment, because it requires extra set-up for [nested virtulization](https://stackoverflow.com/questions/39720254/can-i-run-docker-in-a-virtual-machine). Ask @wxinix if you have any questions.