Transcript
Discussion (0)
Hi, my name is Gleb, I live in London and I run a C++ club at work where we discuss
C++ programming news and related articles.
You can find our meeting notes at cppclub.uk.
This podcast and the meeting notes contain public information only.
This is episode 4 for the meeting number 129 that took place on the 13th of May 2021.
The MSVC's infamous Reddit bug.
The title of this Reddit post is
Really, Microsoft? How on earth this is valid C++?
The multiple examples of nonsensical code in this thread
are happily compiled and executed by MSVC.
Some of the snippets are
Class Foo Then Empty Curly Braces executed by MSVC. Some of the snippets are class foo, then empty curly braces,
then we have int main curly braces foo star foo declaring a pointer to the is foo bar equals and then an asterisk foo dot,
then a number of garbage characters,
like someone just went across a keyboard,
then angle brackets with a float in them,
and then parentheses, and then return 0.
MSVC happily compiles that and runs it.
The next example is similar.
There's an empty class foo, and then we have a function test that accepts a pointer to foo and inside it has the argument foo and then dot then garbage letters
then int in angle brackets and then parentheses also happily compiled by MSVC
the final example is int main and then we have curly braces with the return statement which returns, wait for it,
null putter dot underscore empty angle brackets empty parentheses.
Also happily compiled by MSVC.
Obviously not any other compiler.
This comment explains why the compiler is wrong.
Ill-formed code requires diagnostic, which MSVC doesn't generate, making it non-conforming. Stefan T. Loverwaite replied,
Our compiler developer Jonathan Emmet created a bug report titled Fix the infamous Reddit
bug.
According to his changelog, fixing this accept-invalid bug led to finding
and fixing two more bugs. Hopefully that fix will make it to a next update of Visual Studio.
The coolest things in C++ A Redditor asks, what's the coolest thing
you've created with C++?
The thread has many great examples, too many to mention.
Like a street hole detection for audio A6, some indie games,
software for the Human Brain Project for visualizing intracranial plots of hundreds of subjects by cutting a 3D mesh of the brain at real time and generating
inside-cut textures from MRI files.
Many posts mention hobby projects, which shows that people use C++ for their creative endeavors
and not just for work.
Go and scroll through the thread to see more examples.
Related SpaceX.
Stack Overflow blog interviewed Stephen Girding,
Dragon's software development lead.
Quote,
The actual work of software development by vehicle engineers
such as Girding is largely done using C++,
which has been the mainstay of the company's code
since its early days.
C++ compiler fingerprints
A Redditor asks,
Is there any way to tell which compiler was used to create a given C++ object file or executable?
If so, how does this process work?
Turns out, in many cases this is indeed possible,
which has implications for InfoSec
and computer forensics. Some compilers leave a section in the executable that contains
various information about the toolchain used to create it. It is possible to remove that
section if you want, using a strip command. On Windows, it is possible to extract this information from the executable file header,
which uses portable executable PE format. The PE header follows the MZ header, which for Windows
programs displays a message telling the unfortunate DOS user that they can't run this program.
MZ stands for Mark Zbikowski, a Microsoft engineer who designed the original
MS-DOS executable file format. There is a program called PID, which can detect compilers and various
executable packers and cryptos by inspecting the PE header. The Reddit thread also mentions the IDA disassembler which can
detect the compiler using various heuristics. The most interesting paper
mentioned in the thread was extracting compiler prominence from program
binaries using machine learning. Quote, we present a novel technique that
identifies the source compiler of program binaries,
an important element of program provenance.
Program provenance answers fundamental questions of malware analysis and software forensics.
Our models identify the source compiler of binary code with over 90% accuracy,
even in the presence of interleaved code from multiple compilers.
Abusing cor-weight for error handling. As if all other ways of handling errors in C++,
standard or proposed, weren't enough, here is another way. We have coroutines now,
which are amazing. What if we could use them for error reporting instead? The illustration of the idea is presented in the article.
What it looks like is you have a function that returns some expected type,
which either returns an int value or a string error and that function returns an error string on an error or say returns five on success
and then the caller function calls it with a co-await operator so it looks like int result
equals co-await g that function that just described, and then when it returns a result,
the next line of that calling function uses the co-weighted function returns a value,
the caller function just returns it from itself. But since the caller function also returns an
expected object with int result or a string error message. If that called function returns an error message,
then it just transparently propagated
and returned from the caller function as well.
The article mentions the Rust result type
with some example code,
and the author Antoine Morea explains how to implement the coroutine machinery
to make this possible. He provides full source code on Wandbox. The Reddit thread offers some
interesting feedback including a link to error handling benchmarks which has a similar feature
in the list of benchmarks. It also turns out that Facebook's Folly expected type by Eric Kneebler supports Co-await.
Designated initializers in C++20. Rainer Grimm writes, designated initialization is an
extension of aggregate initialization and empowers you to directly initialize the
members of a class type using their names.
He starts by reminding us what kind of class counts as an aggregate in C++20.
The class must have no private or protected non-static data members, no user-declared
or inherited constructors, no virtual private or protected base classes,
and no virtual member functions.
An example for an aggregate would be the following class.
struct.3d, then curly braces,
and then three ints, x, y, and z.
They can be default initialized. I prefer the uniform initialization syntax
using curly braces. Note that Rainer doesn't do that in his example, but I try to make
it a habit for myself. C++11 introduced aggregate initialization using the uniform initialization syntax with
curly braces.
So you can write.3d and then the variable name,.3d, then curly braces, 1, 2, 3.
And that will initialize the appropriate structure member.
C++20 added designated initializers based on the C99 syntax.
You can see the proposal in the show notes.
There are some limitations, though.
The designators cannot be reordered, because that would change the order of initialization
and destruction, which is important for C++.
Nested aggregates cannot be initialized in this way. Regular and
designated initializers cannot be mixed in the same initialization, and arrays cannot be
initialized this way either. The proposal mentions that both GCC and Clang have supported designated
initialization syntax via extensions for some time.
Reiner says that at the moment of writing this article, only MSVC supported C++20 designated
initialization syntax.
And as we saw before, the new designated initialization syntax can be used to emulate the named arguments
feature present in some other languages but not C++. If you have a function foo that takes an aggregate type, you can use
designated initialization at the call site to emulate named parameters by
creating that structure in place. Ticket maps. Anthony Williams writes, quote,
It has been an increasingly common scenario that I've encountered where you have some ID
that's monotonically increasing, such as a subscription or connection index, or user ID,
and you need your C++ program to hold some data that's associated with that ID value.
The program can then pass around that ID and use it to access the associated data at a
later point.
Over time, IDs can become invalidated, so the data associated with that value is removed,
and you end up with a sparse set of currently active IDs. For the described use case, he implemented an algorithm
Sean Parent calls Russian CodeCheck algorithm.
Quote,
In this algorithm, the map is implemented as a vector of pairs of key and optional data.
Because the keys come from a monotonically increasing index,
the vector is always sorted,
and inserts are always at the end.
Entries can be removed by clearing the data,
and if there are too many empty entries, then the vector can be compacted.
Lookups are always fast, because the vector is always sorted.
So a simple binary search will find the right element. I'll have to revisit
Sean Parent's presentation if only to see what is so Russian about that algorithm.
The implementation is available on GitHub under the Boost software license.
Accessing private members of a friend class from friend function, which compiler
is correct?
A Redditor noticed discrepancy between compilers in how they treat friend classes.
It seems that Clang allows reciprocal access to classes' private members, whereas GCC
or MSVC don't.
The question was, which compiler was right?
The example is as follows.
There is a class foo with a member variable x which has a friend class bar that's declared
inside the class foo.
And then we have a class bar which has another friend function declaration which is also
defined inside the class and in
that function which takes one object of type bar and one object of type foo,
the code accesses that private data member of class foo. Now, Clang thinks it's okay.
GCC MSVC show it as an error.
Someone found a post from 2014 by Richard Smith on a Clang developer mailing list.
Quote,
Clang's behavior reflects the current intent of the C++ core working group.
This is core issue 1699, and the current resolution to that is to
treat everything that is lexically within a befriended entity as being befriended.
GCC does not yet implement the resolution to core issue 1699, which is reasonable since
it's not yet even resolved. Looks like this issue hasn't been resolved yet. However, the C++ standard says
friendship is neither inherited nor transitive. As another redditor explains, quote, in the example
original poster provided, the class bar is a friend of class foo, the function foobar is a friend of class bar.
Just because bar is a friend of foo, it doesn't mean foobar is automatically also friend of foo.
In real life, the friend of your friend is not automatically your friend as well.
With inheritance, the friend of your son or daughter is not automatically your friend.
You could also view friend as an explicit statement. Only the thing you explicitly
declare as a friend is actually a friend. No non-declared friends sneak into the
relationship." It looks like MSVC and GCC are correct here and Clang isn't.
Modern C++ class members and initializations the right way.
Pranay Kumar reminds us in his article on Medium how to initialize a class properly
and in a modern way.
Use member initializers in the same order as their declaration. Prefer in-class member initializer over constant initializations
or over default constructor.
Don't cast away const ever.
And use delegating constructors to represent common actions
for all constructors of a class.
As an aside, please don't blog on Medium.
They monetize your content and then make it inaccessible for others.
The sad truth about C++ copy elision.
Scott Walchok writes,
quote,
In this post, I'll walk you through an example where an obvious optimization you might expect
from your compiler doesn't actually happen in practice.
He comes up with an example that shows copy elision happening in one case but not in the other,
where the code is slightly modified, an extra local variable is introduced.
Ready has not one but two threads discussing this post. Predictably, you can see Rust threads, some of them going into details with code examples in Rust. Bjarne Stroustup quotes, quote, works as expected,
end quote, and quote, we need destructive move, end quote.
The best reply award goes to the one at the very bottom of the second discussion.
You could just replace the sad truth about by amazing optimizations of in the title
and showcase how neatly optimized is the first example.
Type-safe pimple implementation without overhead
Maltes Karupke writes
I like the pimple idiom because I like to keep my headers as clean as possible
and other people's headers are dirty
Unfortunately, the pimple idiom never feels like a good solution
because it has runtime
overhead that wouldn't be needed if I didn't care about clean headers so much.
You always need an extra heap allocation and every method performs an extra pointer dereference."
That's a strange reason to use a Pimple idiom. I could understand the reason being decrease
coupling between classes and hide implementation details, but keep your
headers clean is not a reason I would use. The author then presents a solution.
Quote, this code fixes that so that there can be zero runtime overhead.
This uses a well-known hack where you put the necessary storage into your class and
then placement new the forward declared object into the storage.
End quote.
My impression is that this solution eliminates the main pimple advantage which is removing
the need to rebuild client code when implementation changes.
Sure, it's more efficient than ordinary pimple,
but I'm not convinced it's worth it,
unless perhaps you're an embedded systems programmer.
If your clients have to rebuild after implementation changes anyway,
just keep things simple and use the implementation directly.
That's it for today,
and I'll leave you with a motivational tweet by Nick Ciappini,
a chemistry postdoc,
who writes,
quote,
Whenever I mess up an experiment,
I just think of what a pigeon considers a successful nest,
and a picture of a pigeon with some twigs
and an egg. Thanks for listening and I'll talk to you next week. Bye!