C++ Annotated March 2022: Function vs. Type, Serialization in C++, DWCAS, C++ Modules in Visual Studio
We have all of the March news for you in our latest monthly C++ Annotated digest. Read the monthly digest on our blog and subscribe to C++ Annotated emails by filling out this form.
C++ Committee news
February’s virtual plenary was the official feature freeze date for C++. Since then, it has become noticeably quieter around the C++ Standards Committee. During the last couple of months, the Library Evolution Study Group has been working on various fixes to existing language features that have been improved for C++23, such as ranges and formatting. They’ve also been polishing the API of new features already accepted for C++23, in particular mdspan, which is now looking really good. The Language Evolution Study Group is similarly focused on various smaller bug fixes for language features.
We now also definitely know that we will not be getting executors or networking in C++23, two of the four items that were originally prioritized for C++23 according to the “overall plan for C++23” proposal. The three language features mentioned in that plan (reflection, pattern matching and contracts) are also nowhere near ready yet. As a result, the largest core language feature that is actually going to land in C++23 is deducing this. C++23 was never going to be as large a release as C++20 was, but it also seems that the pandemic and the need to move all standardization work from in-person to online meetings significantly hampered progress.
During the last couple of months, I (Timur) was mostly active in the Core study group that finalizes the wording for language features, where my paper on portable assumptions is now in the final stages of wording review. I am firmly expecting that it will be voted into C++23 at the next plenary session. I also participated in the Contracts study group (SG21), which is currently rather stuck on the question of whether or not the expressions inside a contract annotation should be allowed to have side effects, and what exactly would happen if they did. Various opinions exist, and it will take more work before we reach a consensus and can formulate a specification, but hopefully this will happen in time for C++26. The current state of this work is now being tracked in the newly established Contracts Working Paper.
Expect the standard committee work to remain relatively quiet for the next few months. We are now finalizing all outstanding fixes for the July 2022 online plenary session. This is where we will finalize the C++23 wording for the committee draft, which then goes out for review by the individual national bodies.
SFINAE, overload resolution, and a surprising error
A new blog post at C++ stories highlights an interesting aspect of SFINAE. The sample used in the article performs the basic operation of printing a tuple via the stream output operator. The custom operator throws a surprising compiler error when printing of the
'\n' symbol is called. And even more surprisingly, the code doesn’t work for
std::tuple_size_v<TupleT> but works when using
std::tuple_size<TupleT>::value inside the implementation. The author digs deeper into the reasons why
std::tuple_size_v<TupleT> doesn’t work for
TupleT=char. An interesting discovery here is that the immediate context matters.
While the compiler recognizes immediately whether
std::tuple_size<char>::value is valid or not, in the case of
std::tuple_size_v<char> it has to look at the variable template declaration. And this is not part of the immediate context but a side-effect. Errors in such side-effects are treated as hard errors, not SFINAE errors. There are more examples of the same situation in the article, followed by the links to examples in Compiler Explorer. Feel free to play with them and explore this surprising phenomenon. Have you run into any cases like this during your own development? Let us know in the comments!
saturating_int – new function vs. new type?
In his new blog post, Jonathan Müller discusses a trade-off between implementing the desired behavior by writing new functions and following the OOP principles by defining new types capable of such behavior. The basic example used for the experiments is arithmetic with saturation instead of overflowing arithmetics. When dealing with integer addition, should we write a new function that sums up the arguments or implement a new
int type with such characteristics?
The post author describes both solutions and discusses the pros and cons of each. For example, if you solve the problem by writing a new function, then you can’t rely on known operators and have to use nested calls, thus making the code less readable. In this case, generic code is also impossible out-of-the-box. You’ll also run into problems trying to store an additional state or do extra work without updating the whole type.
Enforcing volatility is a good example of when updating the type helps to guarantee a specific behavior. Like you add
volatile to the pointer type to avoid unnecessary and incorrect compiler optimizations.
But creating a new type isn’t ideal either. A new type is essentially different from the original one. You need to constantly cast to change the behavior. And you still experience issues in generic code.
There is no ready-to-use generic answer in the article. The problem is laid out and solutions are discussed. The article even highlights a few approaches to solving the problem inside the language itself. It’s up to you to decide which solution will work better for your specific case.
Metal in C++ with SDL2
You probably have heard about Metal, a graphics API from Apple. While the typical examples demonstrating usage scenarios usually rely on the Apple’s ecosystem (ObjectiveC/Swift and XCode), this new blog post shows you how to create a working sample using C++ and the C++ specific toolset like CMake and Conan.
The idea is to use Metal-cpp, a low-overhead C++ interface for Metal that helps developers add Metal functionality to graphics apps, games, and game engines that are written in C++. The library is added via CMake. And SDL2, which helps create a window and draw in it using Metal, is installed via Conan. The trick is then to use a bit of Objective-C++ to link the metal device to the corresponding view.
In the end, you get a minimal running Metal application. And some tips for compiling metal shaders.
Automatic Serialization in C++ for Game Engines
Serialization is an important functionality of any game. It’s essential when players decide to stop, postpone the game for a while, and then resume later. In practical terms, it means any game object, character, scene, etc. should be stored to the disk and then restored back. The article discusses how to perform serialization on your own for games written in C++.
It discusses the problem of storing pointers and references, which can’t be saved in the way that every other value is saved. The address should be preserved, but since this is impossible, more complicated procedures are regularly used. They often require a two-pass or some complicated indirection method, and the saving of an additional map.
Of course, the root of the problem is that C++ still doesn’t bring reflection to the developers. So you always have to add special code to your classes to get it. The example implementation in the article is based on preprocessor macros and helps avoid adding serialization and deserialization functions manually to each class.
DWCAS in C++
Timur Doumer’s latest article is dedicated to lock-free data structures that rely on double-width compare-and-swap (DWCAS) CPU instruction. The experiment and the research performed by Timur shows that, in the end, if you want to have a
std::atomic that portably uses DWCAS (for example, in your C++ library) instead of silently adding locks into your code, your only option is to implement your own
std::atomic for all relevant target platforms, or to use a third-party library that does it for you.
The blog post contains a simple example, and the resulting instructions generated by the compiler are inspected in the compiler explorer. And the results are really surprising!
With Apple Clang, everything works as expected for Apple Silicon and for Intel targets. It breaks on Linux, however. For Clang, there is a way to fix this by passing a certain compiler flag. But that’s not a solution for the portable library that Timur wants to build. For GCC, things don’t work as expected, even with the flag. It’s no better on Windows. DWCAS instructions are not guaranteed by MSVC. The only explanation Timur could find is that fixing
std::atomic would constitute an ABI break. This is really a sad consequence of ABI stability over performance priority.
Check out more explanations and a detailed example in the original article.
Experimental support for C++ modules in CMake projects with Visual Studio
The Visual Studio team released an announcement introducing C++ modules support with CMake and Visual Studio generator. Modules is a new feature of C++20 which still lacks proper support in tooling. And despite so many blog posts having already been written about the benefits that modules bring to C++ developers, it was still practically impossible to use them. Now there is finally a way!
Visual Studio 2022 17.2 Preview 2 has experimental support for C++ modules in CMake projects. It’s now limited to the Visual Studio (MSBuild) generator only. Which means the default Ninja generator should be changed to Visual Studio for all configurations. The blog post shows you how to do this: modify your project’s CMakePresets.json or edit the CMake settings with Advanced configuration.
The blog post is not a tutorial on how to switch to modules. There are other posts from the Visual Studio team about that. However, it shows how to start with modules by selecting a proper CMake version, setting a Visual Studio generator, and creating a module interface unit file in the Visual Studio UI. The basic sample demonstrates how to export entities and use them in other source files and module interfaces.
CMake 3.23 released
At the end of March, the new version of CMake was released. Among other things, the new version got an update for CMake Presets. And the new
include field is especially useful as it allows files to include other files. A file can be included multiple times, but the include cycles are not allowed, of course.
An interesting detail about this feature from the published documentation: If CMakePresets.json and CMakeUserPresets.json are both present, CMakeUserPresets.json implicitly includes CMakePresets.json, even when no
include field is used.
Another thing to note is that while files directly or indirectly included from CMakePresets.json should be guaranteed to be provided by the project, CMakeUserPresets.json may include files from anywhere.
Qt Creator 7 released
March was the month when Qt Creator 7 was released. The language engine got a significant update:
- Clangd backend is used by default now.
- It uses LLVM 14 for C++ support.
- C++17 is preselected in the new project wizard as a default C++ standard.
CMake support got an update too:
- New Run and Stop CMake buttons to reconfigure the project easily.
- CMake options now distinguish Initial Configuration and Current Configuration in the UI, which makes it easier to understand how the settings were inherited.
- Help links for CMake options were added to the context menu when called on an option.
CLion 2022.1 EAP news
CLion 2022.1 is going to be released soon. The release candidate is already published. This update is geared toward quality improvements and enhancing the existing workflows rather than adding new features. Here are the main directions for the fixes which made it to CLion 2022.1:
- CMake Presets integration was updated. CLion can now generate CMake profiles for configure presets (this was previously possible for build presets only). CMake 3.22 is bundled. CLion also comes with the enhancements for CMake code formatting and folding. And there is one brand-new feature that we began developing a long time ago and hadn’t finalized until now – just in time for the current release. This is CMake Profiling. A built-in visualizer will help you find weak places in your CMake scripts that take significant time.
- The CUDA-GDB debugger can now be used in CLion. Some users have tried it in CLion before, but a few issues were present which prevented it from working correctly. Now they are fixed.
- In code analysis, there are plenty of fixes that make the analysis in CLion more accurate, as well as updates to Clang-Tidy and MISRA settings and a new intention preview.
- Another area where fixes for accuracy were introduced a lot during this release cycle is Inlay Hints, i.e. hints in the editor for parameters and types.
There are many other fixes, which you can find looking through the EAP webpage.