C++ Club - Meeting #129

Episode Date: May 18, 2021

Show notes: http://cppclub.uk...

Transcript
Discussion (0)
Starting point is 00:00:00 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++?
Starting point is 00:00:35 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,
Starting point is 00:01:24 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.
Starting point is 00:02:17 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
Starting point is 00:03:02 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.
Starting point is 00:03:43 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.
Starting point is 00:04:05 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
Starting point is 00:04:38 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
Starting point is 00:05:36 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,
Starting point is 00:06:19 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,
Starting point is 00:07:47 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.
Starting point is 00:08:33 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.
Starting point is 00:09:14 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.
Starting point is 00:10:00 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
Starting point is 00:10:40 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
Starting point is 00:11:33 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,
Starting point is 00:12:13 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
Starting point is 00:12:46 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?
Starting point is 00:13:23 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.
Starting point is 00:14:09 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
Starting point is 00:14:55 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.
Starting point is 00:15:51 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.
Starting point is 00:16:22 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.
Starting point is 00:17:06 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
Starting point is 00:17:55 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.
Starting point is 00:18:40 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,
Starting point is 00:19:14 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,
Starting point is 00:19:36 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!

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.