Uses of ‘auto’ in Modern C++

The auto keyword has been part of C++ since C++11, and its role has developed somewhat since then. This article intends to cover all the use cases of auto, including syntax that is new in the upcoming C++23 standard. The list of uses covered is:

  1. Type deduction for variable definitions
  2. References to other entities
  3. Deduction of a function’s return type
  4. Generic lambdas and functions
  5. Making a temporary copy of an entity
Continue reading “Uses of ‘auto’ in Modern C++”

Modern C++ flow diagnostic tools

Most programs produce output. (I suppose a corner-case could be a unit test framework that indicates all tests passing by not throwing an exception, whilst itself not producing any output.) A usual way of confirming a program is correct is by examining its output, this is familiar to programmers as part of the edit-compile-run-debug cycle of coding practice. (Note: I’m not including coding practices such as TDD, which impose a slightly different workflow.)

So, hands up, who’s ever done it? Of course, I refer to inserting debugging code such as std::cerr << "here1\n"; into C++ source files, when you want to ensure a certain code path has been visited. This article intends to show a way for you to never have to use such practices again, thereby avoiding the risk of evidence of debugging hacks remaining in your production code.

Continue reading “Modern C++ flow diagnostic tools”

C++ Coroutines Primer (3)

In the previous two articles of this mini-series we introduced co_await, co_yield and co_return, the last of these not taking a parameter. In this article we’ll look at the possibility of co_return actually returning a value, and the minimal boilerplate necessary to allow this functionality to work. Note that this boilerplate does become closer in complexity to Generator.hpp from the first article, so in your own projects you may prefer to start with that source file and adapt it as necessary.

Continue reading “C++ Coroutines Primer (3)”

C++ Coroutines Primer (2)

In the previous article we looked at co_yield producing a result from a coroutine and co_return (without parameter) causing an early “return” from a coroutine. In this article we’re going to look at the third C++ coroutine keyword, being co_await, and introduce the C++ boilerplate code necessary to allow it to function.

While co_await and co_yield are closely related, the semantics and meaning are slightly different: co_await says “suspend the current coroutine function until further notice, then resume at this point when directed by the caller“, while co_yield says “return this value to the caller right away, then resume at this point when another value is requested“. When control flow through a coroutine arrives at co_await a;, where a is an object we’ll call a “coroutine context”, a number of steps take place:

Continue reading “C++ Coroutines Primer (2)”

C++ Coroutines Primer (1)

Coroutines are a major addition to the C++20 language standard, and so should be interesting to anyone wishing to develop their use of Modern C++. Coroutines are different to threads (which have been standardized since C++11) and are considerably more lightweight in their Standard Library implementation, requiring just the <coroutine> header. An imperfect analogy could be that threads are similar to pre-emptive multitasking, while coroutines tend to mirror co-operative multitasking. Coroutines are not a replacement for threads, and the two can usefully co-exist; a coroutine can be paused on one thread and re-started on another, for example. An attractive feature of coroutines is that they are less susceptible to data races, deadlock, and some of the other pitfalls when using threads.

So the down side? While the change to the language itself is only the addition of three new keywords, co_await, co_yield and co_return, a large amount of boilerplate (even by C++ standards) is needed to allow them to do anything useful. There is a three-way interaction between the language keywords used in your coroutine code, the Standard Library implementation, and the boilerplate they require as the “glue” between them. This article intends to cover co_yield, with a focus on getting it to act like Python’s yield keyword when constructing generators.

Continue reading “C++ Coroutines Primer (1)”

C++ Lambdas Primer (4)

So far in this mini-series we’ve looked at capture by reference using [&], however you should know that there is another way of accessing variables from the enclosing scope called capture by copy, and this uses the syntax [=]. Can you guess the output of the program shown below?

#include <iostream>

auto f() {
    int a{ 1 };
    auto l = [=]{ std::cout << "l(): a = " << a << '\n'; };
    a = 2;
    l();
    a = 3;
    l();
    a = 4;
    return l;
}

int main() {
    f()();
}
Continue reading “C++ Lambdas Primer (4)”

C++ Lambdas Primer (3)

Often it is desirable for a lambda function to read or modify state which has to be preserved between calls. Just about the simplest example of a stateful lambda is one that adds up all the numbers it is called with, as shown in this example program:

#include <algorithm>
#include <iostream>

int main() {
    auto data = { 1.0, 2.2, 3.1, 2.1, 0.5, 1.6 };
    double sum{};
    auto sum_and_average = [&](const auto& elem){
        std::cout << elem << " ";
        sum += elem;
    };
    std::for_each(begin(data), end(data), sum_and_average);
    std::cout << "\nSum: " << sum << '\n';
}
Continue reading “C++ Lambdas Primer (3)”

C++ Lambdas Primer (2)

This article intends to introduce “generic” lambdas, and also tries to explain how they are implemented so that a fuller understanding of how they work can be gained. So what is a generic lambda?

Consider the following code, and try to guess the output:

#include <iostream>

int main() {
    auto g = [](auto n){ return n / 2; };

    std::cout << "g(3.0): " << g(3.0) << '\n';
    std::cout << "g(3): " << g(3) << '\n';
}
Continue reading “C++ Lambdas Primer (2)”

C++ Lambdas Primer (1)

Lambdas have been an important part of Modern C++ for over a decade, having been added to the language with the release of C++11. Whilst very useful in their own right, proficient use of lambdas touches upon many different areas of C++: functions, reference semantics, generics, operator overloading, RVO… the list goes on. It has been said that having a complete knowledge of C++ lambdas leads to an understanding of the language as a whole; this mini-series aims to uncover some of their secrets and hopefully serves as a practical introduction.

Lambda functions (commonly abbreviated to just “lambdas”) are functions treated as objects, meaning they can be (re-)assigned to variables and returned by (or passed to) other functions. Really, the concept is no more complex than that!

Continue reading “C++ Lambdas Primer (1)”

Ranges in Modern C++ Programming

When template syntax (generics) first appeared in C++ some people were skeptical about whether it was either useful or practical, while others managed to determine that a Turing-complete compile-time language had been inadvertently created. It must be difficult to imagine C++ without std::vector<T>, and similar; the application which surely sold templates was the STL (Standard Template Library), which became part of the first ISO C++ Standard (C++98) when such support in contemporary programming languages was limited.

Ranges are an attempt to simplify the composition of several actions on a range of input values, usually of the same type (such as int). The Ranges TS added a part of the third-party library of the same name to the Standard, this becoming part of C++20. However the freely available “range-v3” library contains much functionality not (yet) found in the Standard, and being header-only means there is little reason to not use it. (Two of the four examples in this article compile with C++20 supporting compilers, one more needs C++23, while the final one needs range-v3 for views::generate().)

Continue reading “Ranges in Modern C++ Programming”

What future for C++?

For the first new blog post of 2022 I thought I’d compare Modern C++ with some of its contemporary languages in terms of syntax and library support. No language exists in a vacuum, and (almost) all languages borrow ideas from each other; the other four languages I picked were (in no particular order): Rust, D, Swift and Kotlin.

All four of these are very much C family languages (curly braces and semi-colons) targeted to different domains. Rust is pitched as a systems programming language and a competitor to C in terms of performance (and is currently being reviewed as a possible second implementation language for the Linux kernel). D is a competitor to C++, being similarly a development of plain C into a true object-oriented language, albeit begun decades after C++ was born (and with a design decision for D2 to abandon backwards compatibility of syntax). Swift is a recent managed (commercial) language from Apple pitched as the successor to Objective-C, which compiles via SIL (Swift Intermediate Language) and the LLVM infrastructure to object code. Finally, Kotlin is a language which targets the JVM meaning it is ideally suited to developing Android apps, this language and eco-system being fully supported by Android Studio.

Continue reading “What future for C++?”

Writing a pseudocode compiler (6) – Compiler driver frontend

With the scanner and parser classes complete, we need to create a main program which uses them both. The parser needs to know about the scanner (the parser class has a member variable which is a pointer to the scanner object), so the scanner needs to be created first. A C++ flex scanner reads by default from the standard input, and whilst member functions for switching streams are available, it was decided that using member function std::istream::rdbuf() to link a disk-file to std::cin was a better solution, and similarly for std::cout where class Tree‘s member output object is hard-wired:

Continue reading “Writing a pseudocode compiler (6) – Compiler driver frontend”

Writing a pseudocode compiler (5) – Rules, statements and expressions

In this part we’re going to look at how the bison grammar rules, specified in the file “grammar.y”, can be made to match against the stream of tokens which the parser requests (this process being called syntax directed translation). Ultimately we want to output correctly generated code by through walking the abstract syntax tree, created in memory by the parser program we wrote. We’ll also take a look at how the global symbol table plays a part in early type checking, as the syntax tree is being constructed.

Continue reading “Writing a pseudocode compiler (5) – Rules, statements and expressions”

Writing a pseudocode compiler (4) – Generating a parser

Using a “parser generator” such as bison means that the framework for creating this key part of the front-end is more rigidly imposed that if it were written from scratch. On the other hand, it also means that some of the hard work is already done for us. If the compiler back-end has to output assembly language or object code, the temptation would be to rewrite the compiler in its input language. (In fact this action is known as “bootstrapping”, and many professionals would only take a programming language (and its compiler) seriously, if such an action is feasible.) However it may be desirable to keep the original “bootstrap” compiler up-to-date as the language is developed further.

The bison program “grammar.y”, written for this project is quite large, at just under 700 lines, so documenting every part in detail is beyond the scope of this mini-series. However, we’ll pick out a few key parts to describe and the remainder should be able to be understood from examining this file.

Continue reading “Writing a pseudocode compiler (4) – Generating a parser”

Writing a pseudocode compiler (3) – Generating a scanner

This article takes a look at the (only) part of the compiler which directly processes the source text (or “source code”), this being the “scanner”. Some amount of theory lies behind the pattern-matching actions of this part of a compiler, however unless you have a need to implement from scratch (something which can be true for commercial-grade compilers) you can safely follow best practices by employing a “scanner generator”.

The purpose of a (hand-written or semi-auto-generated) scanner is to convert (or reduce) textual patterns into a stream of numerical tokens. It really is as simple as that. (Well, almost!) A working knowledge of regular expressions (“regex”) is really a prerequisite, although patterns for common usages, such as floating-point representation of numbers, can be found and utilized without the need to produce them off the top of your head. The textual patterns which are matched by the regex(es) have a special name: lexemes.

Continue reading “Writing a pseudocode compiler (3) – Generating a scanner”