Of all the features added to C++ over the past years, I think Designated Initializers is both the best and one of the least talked about. Time to right that wrong.
If you’ve been following my hot takes on C++, you might have noticed that I haven’t been the most enthusiastic person about the recent additions to the language. While some of them were nice addition, I haven’t felt like they had a significant impact on my code unless I had a somewhat niche use case. But for the past months I’ve been using C++20’s designated initializers and it’s been quite the change.
Looking back at 2025 and looking forward to 2026 through the lens of graphics programming.
I had done some work with graphics while working on various titles at Paradox, but I never felt really confident about it like I would have been about C++ or multithreading or the few other topics I’ve talked about in the past. Sure I had done some work with it, figured out what the point of shaders is (the answer is: they shade) and migrated Hearts of Iron IV from DirectX 9 to 11, but it still felt a bit mystical. So I decided to use my spare time between contracts this year to catch up.
Let’s talk about game simulations. Today we dive into the nitty-gritty bits of implementing data driven multi-threading.
In our last episode in this series we presented the concept of task-based parallelism with scheduling driven by data accesses. I recommend going back to it for a quick reminder because today we are gonna talk about implementation. Let’s get coding!
Last time we explored some commonly found alternatives to using enums for error codes such as asserts, contracts and std::expected. Finally today we consider some unthinkable options.
The code examples given in part 1 and part 2 strongly focused on simple (and I’d argue, quite common) form of error handling: run a sequence of operations, and if any of them fails bail out and report an error. We tried a few alternatives but they all came with caveats. Error code returns polluted a lot of the logic with if ( error ) { return error; }, std::expected was a bit more concise but demanded we rewrite the signature of the functions we call to fit the monadic form and finally contracts allowed for cleaner user code but aren’t really made to do custom error handling (if they make it into C++26 at all) or recoverable errors (like a missing file).
I wanted my program to boot up faster so I tried to multithread it with both executors and the more classic TBB.
Back in 2020 I did a remote talk at CppCon about my work on making Stellaris boot up much faster. I later brought similar improvements to Hearts of Iron IV and Europa Universalis IV (although I’m afraid the latter was never pushed to the live branch, you have to opt-in to a beta version). They key to those improvement was, of course, concurrency: put stuff to load in the background if it’s not needed upfront, and use multiple threads to do the work when possible.