C++ Club - Meeting #128
Episode Date: May 6, 2021Meeting notes: https://glebd.github.io/cppclub/CppClubUK-128-20210506.html...
Transcript
Discussion (0)
Hello, my name is Gleb. I live in London and I run a C++ study group at work.
In our weekly meetings, I talk about C++ news and anything related to C++.
You can find the notes at cppclub.uk.
This podcast and the meeting notes contain public information only. This is podcast episode number three for the meeting number 128
that took place on the 6th of May 2021. Right, so the April mailing is out and
here is what caught my attention. Still expected proposal. This paper progresses to revision 10.
It's an alternative error handling mechanism for when
you cannot or don't want to use exceptions but are okay with checking function return values
like in the old times reddit thread about this proposal has the usual topics we need static
deterministic exceptions another one we don't need another error handling method,
which mentions Bjarne's paper criticizing Herb Sutter's proposal.
The poster says, I think he was right.
Someone says, exceptions introduce invisible control flow.
And so, presumably, we must handle everything manually and explicitly.
Another poster says, Herb Subsceptions is the ideal solution, but the committee has shown
that it is both incapable of and unwilling to dedicate time to more important, larger issues
before C++29, as Bryce Lelbach said. To which Bryce himself replies, I didn't say that. He says, I never said C++ 23 and 26 are expected to be minor
releases. And then Niall Douglas says, well, actually, I did say something like that. And
what he said was, because of the lack of face to face meetings has severely impacted early stage
proposals, you won't see the true effects of COVID on the C++ feature pipeline
until 26 or 29. So obviously people were free to interpret this as they liked. Anyway,
another proposal that caught my attention was this one we've seen before, interaction of
std colony to the standard library. I thought it was getting
renamed to std hive, but maybe that was a separate paper just for renaming it. Colony is a formalization
extension and optimization of what is typically known as a bucket array container. You have
multiple memory blocks of elements and a boolean token for each element, which denotes whether or not that element
is active or erased, commonly known as a skip field. If it's erased, it is skipped over during
iteration. When all elements in a block are erased, the block is removed, so that iteration
does not lose performance by having to skip empty blocks. If an insertion occurs when all the blocks are full, a new memory block is allocated.
Because a skip field is used, no reallocation of elements is necessary upon erasure.
Because the structure uses multiple memory blocks,
insertion to a full container also do not trigger reallocations.
This means that element memory locations stay stable and iterators stay
valid regardless of erasure insertion. Another proposal that caught my attention was static
operator parenthesis or function call. This proposal is to just allow the ability to make
the call operator a static member function instead of requiring it to be a non-static member function. This would enable getting rid of hidden this parameter when calls
to function objects are not inlined. Without knowing any background this does look like a
perhaps a useful addition. The next one is make declaration order layout mandated.
The proposal is to remove the ability of implementations
to reorder class members according to their access control
to follow the established industry practices
and avoid problems with ABI.
The paper says the current rules allow implementations freedom
to reorder members in the layout if they have
different access control.
But this is not really used in practice.
I must admit I didn't even know this possibility existed.
The next one is...
Pre-Processor Embed Directive.
This is a C-compatible way of including binary resources into programs
based on a new preprocessor directive, embed. The author is Jeanne-Hide Minit, who previously was
pushing for a C++ feature called stud embed. And now it turned into a preprocessor directive. Curiously. Regarding std embed, this redditor writes,
I don't know how true that is, could be just speculation. I think the author has decided
that it would be helpful to get some implementation experience with the embed directive because the
progress with std embed has been very slow and the feedback from Luji has not always been as helpful as it could have been.
I think it's a bit of a shame
we are going to see a new preprocessor feature introduced
when we are trying to get rid of the preprocessor altogether.
But if C compatibility is involved, then fair enough.
Corentin Jabot wrote a proposal
called for a fewctuators More,
which is, I think, a reference to a spaghetti western for the few dollars more.
This paper proposes that the dollar sign, the backtick, and the at symbol
could be used as C++ tokens for any proposal that might need them in the future.
It was prompted by the proposal of the dollar sign for reflection
and its subsequent rejection out of the fear to break code or tools.
Curranton writes,
It seems increasingly difficult to find syntaxes for new features
that are both distinct, easily readable and terse.
The paper does not propose any changes to the standard,
but simply tries to convince the committee
that having an option of using the above punctuators
makes sense for any new features of C++.
The author analyzes the current usage of the punctuators
in C++ and other languages
to see if there is a potential conflict.
Here is, for example,
how the at symbol is used in Objective-C
and others.
Apparently, you can use the dollar sign in identifiers
because GCC Clang and MSVC
and even ICC support it as an extension.
I've never seen it used in identifiers.
Must be lucky.
Next is constexpr class.
Andreas Fertig proposes to allow constexpr in the class head,
much like final.
It would declare that all member functions,
including special member functions,
in this class are implicitly constexpr. The author writes, this reduces the noise resulting
from entirely constexpr classes as we have it now. I'm not sure about this. Comparing
to final doesn't seem fair, as final doesn't change functions in the class itself. Whereas if this proposal is accepted,
it will become more difficult to see whether a class member function is constexpr, as you would
have to look at the class declaration first. Maybe in this case a little additional verbosity
is actually good and makes things clearer. In this case, the proposal is to help the author of the proposal in a concrete example,
which he says was his test implementation of making unique putter constexpr.
So as he simply added constexpr to all member functions of unique putter,
he thought that maybe there was another way.
The next one is extend init statement to allow alias declaration.
Jens Maurer noticed an inconsistency in the fact that since C++20 it is valid to include
typedefs in init expression within if, switch and range for, but not alias declarations
which are otherwise preferred over typedefs.
In C++20 you can do this for typedefint T and then T E over V, but you can't use using
T equals int.
And this paper proposes to fix that.
The author says the alternative would be to prohibit type deaths as an innate statement.
Next is a quick aside.
If you remember, we mentioned JF Bastion and I said I heard that he was no longer at Apple.
I found where he was.
Turns out he's at Woven Planet, which is a subsidiary of Toyota working on self-driving cars
they recently acquired
Lyft's self-driving car unit
level 5
so good luck to JF Bastion
in his new role
maybe we'll actually see
self-driving cars at some point
in the future
I hear they will be allowed in London at some point this year.
So that'll be fun.
London streets are probably going to be a major stress test for any software algorithms.
Visual Studio 2019 16.10 preview 3 was released.
That's entirely too many version numbers in one sentence.
The main highlight is the included standard library or STL.
As they still call it is C++20 feature complete.
They have a nice progress chart of STL features to be implemented.
And as you can see see there's nothing to do
for C++ 20. It's interesting how MSVC compiler transformed from something
lagging behind the other compilers to something trailblazing the path. It
didn't get unnoticed by Reddit. They say,
amazing, MSVC has been beating both GCC and Clang for C++17 and C++20. So that's very good news.
I think Visual Studio 2019 will still be updating for the next year or so, because
who knows how long it'll take them to productionize 2022.
Speaking of other compilers, GCC 11.1 released.
The release notes list the following major changes.
Switching the default debugging format to DWOV-5.
Switching the default C++ language version to GNU 17.
I presume this means that it's C++ 17 plus any GCC-specific extensions.
Great progress in the C++ 20 language support,
both on the compiler and library sites,
and experimental C++ 23 support.
So good to see that GCC is also progressing.
Next we have a paper.
Konstantin, who owns one of the coolest domain names
a C++ developer could think of,
const.me,
wrote a document explaining SIMD,
Single Instruction Multiple Data Programming.
He starts with the following motivation. I've noticed many programmers I'm working with aren't familiar with SIMD. I don't
want to stop writing vectorized code. The performance is just too good. Instead, I'm
writing this article hoping to educate people. Constantine explains how to use intrinsic compiler
SIMD functions to achieve maximum performance without relying on compiler to vectorize your code.
A Redditor comments, programming SIMD with intrinsics is like programming in Assembler.
From personal experience, code full of intrinsics may be faster than compiler-optimized version,
but can be a nightmare to support.
Before deciding to use them in production, it is probably wise to benchmark your code
against a proof-of-concept version that uses intrinsics, and decide if the performance
gain is significant enough to warrant potentially increased support efforts, especially if not
all team members are comfortable
with using intrinsics. To help the compiler, you can use data-oriented programming techniques,
arranging your data structures in a way that simplifies parallelization. These techniques
are especially popular in game development. The Reddit thread points to an interesting
compiler from Intel that I didn't know about.
Quote, ISPC is a compiler for a variant of the C programming language with extensions for single-program multiple-data, SPMD as they call it, programming.
Under the SPMD model, the programmer writes a program that generally appears to be a regular
serial program, though the execution model
is actually that a number of program instances execute in parallel on the hardware.
ISPC compiles a C-based SPMD programming language to run on the CMD units of CPUs and GPUs.
It frequently provides a 3x or more speedup on architectures with 4x vector SSE units
and 5x to 6x on architectures with 8x wide AVX vector units, without any of the difficulty of
writing intrinsic code. It uses the remarkable LLVM compiler infrastructure for backend code generation and optimization,
which their own compiler don't use.
They're only now starting to use Clang as the frontend,
but I think ICC has a completely proprietary backend.
On CPU, it supports Windows, Mac, and Linux
with both x86 and x86-64
targets. It currently supports all kinds of instruction sets including
Xeon, Fi, Knight's Corner. On GPU ISPC supports Intel processor graphics
generation 9 and later on Linux platform. I wonder how the generated code performs on AMD CPUs and GPUs.
Next.
How C++ resolves a function call.
Canadian programmer Jeff Preshing posted an article about how C++ resolves a function call,
which has a nice diagram based on the C++ standard.
He explains in detail what happens at each step of the diagram. Name lookup which is
member name lookup, qualified name lookup and unqualified name lookup with its own
set of rules that include argument-dependent lookup, handling of function templates, overload resolution,
and tiebreakers within that. This is a really good explanation that should be bookmarked and
revisited more than once. The Reddit thread has more useful links, including ADL-related articles,
like the description on cppreference.com,
argument-dependent lookup, also known as ADL or Koenig lookup,
which no one calls it like that anymore.
These function names are looked up in the namespaces of their arguments, in addition to the scopes and namespaces considered by the usual unqualified name lookup.
Also a related article by Arthur O'Dwyer,
which starts with,
In the beginning, Bjarne created namespaces.
Yeah, it's a fairly long one.
And an AppSale tip of the week,
number 49, about argument-dependent lookup.
All of those are useful articles.
Next, Arthur O'Dwyer writes, don't blindly prefer in place back to push back.
Even a decade after C++11 was released, writes Arthur, I still sometimes see programmers
assume that in place back is somehow related to move semantics.
In place back was added to the language at the same time as std move, just like lambdas were
added at the same time as std function, but that doesn't make them the same thing. In place back
constructs a container element in place given constructor arguments. You can also move an
element into its place in
the container, but in this case pushback can also be used. Arthur O'Dwyer concludes, I recommend
sticking with pushback for day-to-day use. You should definitely use emplaceback when you need
its particular set of skills. For example, emplaceback is your only option when dealing with a deck of mutex or other
non-movable type.
But pushback is the appropriate default.
One reason is that inplaceback is more work for the compiler.
Pushback is an overload set of two non-template member functions.
Inplaceback is a single variadic template.
So when you call pushback, the compiler must do overload resolution but that's
all. When you call emplaceback the compiler must do template type deduction followed by
overload resolution followed by function template instantiation and code generation. That's a much
larger amount of work for the compiler. In the reddit thread a redditor nicely summarized the article tldr understand what in place bag does before you
use it as usual the thread goes into various interesting places mentioning scott myers
and that sub thread evolves into discussion of his retirement from c++. Where did Scott Myers go, someone asks.
Another one mentions an in-place new anti-pattern.
We'll see it in just a moment.
Speaking of anti-patterns,
the next is a whole website called C++ Anti-Patterns.
This page documents some common mistakes that i see again
and again in bug reports and requests for help on sites like stackoverflow writes the author
the list includes the following reading from an iStream without checking the result
in many cases the problem in the program is that in the input statement there was a failure so the variables
that was supposed to be read contain garbage values and the calculations are garbage in this
example. Otherwise the program has no way to check the assumption that it reads from the file
correctly. The solution is simple always check your I.O. operations. Another one, testing for iStream end of file in a loop.
A common mistake when using iStream is to try and use end of file EOF function call
to detect when there's no more input.
This doesn't work because the end of file bit is only set after you try to read from
a stream that has already reached end of file bit is only set after you try to read from a stream that has already reached end of file. When all the input has been read the loop will run
again, reading into X will fail and then process X is called even though nothing
was read. The solution is to test again to test whether the read succeeds
instead of testing for end of file. The next one, locking and unlocking a std mutex.
Well, this should be fairly obvious.
Use RAII lock guard technique for that.
He says, be careful that you don't forget
to give a scoped lock variable a name.
This will compile, but doesn't do what you expect.
I've seen that in real code, in
production code even. So, this doesn't do anything. The mutex is not locked after that.
So, no synchronization. Next one is inserting into a container of smart pointers with emplaceback called with new x this is the emplaceback new pattern
you cannot just say pushback new x because if your container stores unique pointers to x
there's no implicit conversion from a bare pointer to a unique pointer. And a popular solution is to use
in place back with new x
because that compiles.
But this is not safe.
If the vector is full
and needs to reallocate memory
that could fail
and throw a bad alloc exception,
in that case,
the pointer will be lost
and will never be deleted.
Admittedly,
if you have a bad alloc exception,
you have bigger problems,
but still. The safe solution is to create a temporary unique pointer that takes ownership of the
pointer before the vector might try to reallocate.
Or in C++14 you should just use std make unique, and it's a non-issue.
And again, recommendation, do not prefer a place back just because it allows you to call
it an explicit constructor.
Next is defining less than and other orderings correctly.
The TLDR summary of this is you should probably use std tie when implementing comparison since C++11 you can just implement these operators by using
std tie and the operator so the proper ordering will be preserved next anti-pattern is dynamically
allocating std thread objects this code could use stdunique pointer to stdthread.
But for some reason the people who use this antipattern never seem to use smart pointers
either.
Some pattern there.
So the real problem with the code above is that it creates a stdthread using dynamic
allocation.
This is unnecessary and simply makes the code slower and more error prone.
The standard thread type has value semantics, so there's no need to refer to it indirectly through a pointer.
Note that the thread starts immediately on creation, so if you want to defer it you can use std optional of std thread the next one is using std bind when constructing std thread objects people still use that priced
and finally using std string of empty string to create empty string objects because the default constructor does it for you so no need
unless it's like a default argument or something
right so that's all the anti-patterns i think we're running out of time that will be it for
today and i'll leave you with this tweet by Olof Urvage.
What C++ means? The C part is raw pointers, the plus plus part trying to prevent people
from using raw pointers. I thought that was very good.
So that's it. Thank you very much for coming and I'll talk to you next week hopefully.
See ya.
Bye.