
Modern C++ gamedev: thoughts and misconceptions - ingve
https://vittorioromeo.info/index/blog/gamedev_modern_cpp_thoughts.html
======
PezzaDev
After years of experience writing code for games, I find that the dumbest code
is the best code. It doesn't matter if it's C# or C++, whenever I've used
something like reactive extensions or template metaprogramming, it has been a
terrible mistake every single time. Make your code simple, dumb and verbose
all the time. Avoid using any complex abstractions or any overcomplicated
syntactic sugar and you'll have a codebase that anyone can jump into and
quickly be able to add features without introducing bugs (at least less
likely). This matters more than anything else.

~~~
lazypenguin
Matches my experience as well in the hobby gamedev realm. I can give an
anecdote, I’m involved in a “private server” dev community for an old niche
MMO from 2003. There exists 3 codebases: first the original code written in a
windows-style C++ with heavy use of inheritance, custom collections, Hungarian
notation, etc. The second is a collaborative open source version that is
written in a naive/basic C++ style (e.g. c w/ classes) and the third is a
from-scratch reimplementation in modern C++ with all the bells and whistles,
cmake, heavy use of templates, etc.

Despite the modern c++ version being the highest quality and the original
windows-style version being the most fully featured, the vast majority of
people use and prefer the rinky-dink basic c++ version. Simply for the reason
that you don’t need to be a senior level dev to contribute meaningfully to the
code.

~~~
microtherion
So, naive code attracts naive programmers? I'm not sure this is the ringing
endorsement you take it to be.

I should also add that Hungarian notation is the prototypical example of dumb,
verbose code, that wants to make individual lines easier to understand by
dragging type information into every single variable name.

~~~
Sebb767
> I should also add that Hungarian notation is the prototypical example of
> dumb, verbose code

If used wrongly. Joel Spolsky wrote a whole post on it[0], but the TL;DR is
that you should use the notation to differentiate between variables of the
same type. For example, you might have world coordinates and object
coordinates in a game script. Correctly used Hungarian notation would denote
them, for example, with `wPosX` and `pPosX`. Even though they're both int (or
float), you can easily see that you shouldn't assign one to the other.

Using them to notate types, however, as in `iPosX`, is completely useless. I
fully agree with that.

[0] [https://www.joelonsoftware.com/2005/05/11/making-wrong-
code-...](https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-
wrong/)

~~~
vlovich123
For dynamic languages sure. For strongly typed languages it's better to just
use the type system to prevent those kind of things. C++ doesn't have great
support here (lots of boilerplate needed) & usually people reach for Boost
Units or Boost strong types but it's not that hard
([https://www.fluentcpp.com/2016/12/08/strong-types-for-
strong...](https://www.fluentcpp.com/2016/12/08/strong-types-for-strong-
interfaces/)). Mozilla is also exploring this specifically for coordinate
spaces & whatnot too ([https://research.mozilla.org/2014/06/23/static-
checking-of-u...](https://research.mozilla.org/2014/06/23/static-checking-of-
units-in-servo/)).

------
eska
Reading the twitter thread he mentioned in the introduction gave me a very bad
impression of the author. He started a nonconstructive flamewar, called all
constructive criticism misguided, and thereby shit on half a dozen video game
development VETERANS. The arguments by Omar in particular are worth reading
much more than this article.

Also, all his code examples are bad. No one would build a texture atlas the
way he did for example, and in any practical algorithm one wouldn't be able to
use folds like he did. So suddenly one would have to use something entirely
else like a for loop or std::accumulate().

~~~
1tCKV3QfIo
Bloomberg has a higher hiring bar than most, if not, all game studios. I like
how you fallback on seniority while the video game industry isn't exactly
known for top tier talent.

~~~
bionicbeagle
It takes a special kind of arrogance to assume that you know better than the
people who've been successful in a completely unrelated field.

It reminds me of the arrogance displayed in the Twitter thread, which is a
shame. I think like someone said, experience is valued only by those who have
it.

I've been there too though, I was that arrogant young programmer once thinking
all these old-timers weren't a match for my technical skills. It usually
passes.

------
winterismute
While I do agree with various points in the article, it is kind of funny that
the author works in the financial sector and has, apparently, no experience in
working on big games devloped in long stretches by hundreds of people at the
same time: for example, this article about "C++ and gamedev" shows examples
from his own Quake VR codebase, an insanely cool but clearly one-man project
started and brought forward by he himself alone...

~~~
humanrebar
Does the author have no experience developing C++ on large teams or are you
asserting that large team game dev is unique compared to other industries?

~~~
winterismute
> or are you asserting that large team game dev is unique compared to other
> industries?

Well, that would be a good question to ask to the author, since he titled the
article "modern C++ gamedev" and not simply "modern C++" although to me what
is discussed seems to be general enough, and "gamedev" here happens to be just
the type of project the code comes from... To me, it does not look like he
makes any significant contribution on how "modern C++ could serve specifically
games development". IMHO he has detected that people in games react strongly
to content about C++ and he benefits from the additional exposure he gets by
putting "gamedev" in his C++ articles/tweets/anything (which is something I am
not judging btw).

Personally, I do think that when your code is simulating a whole parallel
universe using a not-super-high-level language, you might end up with
challenges regarding readability and flexibility of the raw code that other
softwares might not encounter. Not being really part of that industry I feel I
can't know for sure, and therefore I have applied the same reasoning to him
since he is also not part of that industry, it seems.

~~~
humanrebar
I have experience with large scale high performance C++, albeit not in games
specifically. The recommendations in my experience are very sane, conservative
even. I think at this point the onus is on critics to substantially respond to
the points given.

I don't think questioning credentials is particularly elucidating here. I
don't see any reason the entire thesis of the article is flawed.

As to the throwaway reference to gamedev, the article is in response to shade
thrown by gamedevs, attempting to concede specific concerns and propose
solutions.

~~~
eska
That's the thing though. Gamedevs aren't throwing shade, it's a strawman. Look
at the twitter threas mentioned in the introduction and you'll see a bunch of
civil gamedev industry veterans offering constructive criticism, only to be
shut down with "that's misguided" and no counter argument. It gets old fast
and it's been like this for dozens of years. I'm not surprised that they stop
caring.

~~~
humanrebar
If you are making the point that Twitter arguments are counterproductive, I
agree. I will concede all objections about tone because my life is too short
to get dragged into Twitter tone policing.

I do find the recommendations interesting and would like to keep the
conversation about them. I would especially like to avoid gatekeeping and No
True Scotsman arguments in a thread about code.

~~~
eska
No, I did not make the point that Twitter arguments are counterproductive at
all o the contrary this one looks very productive EXCEPT for the author.

So now you're straw manning me as well, then say I'm gatekeeping and no true-
scotsmanning. I don't appreciate that all and am therefore out.

~~~
humanrebar
\- winterismute questioned qualifications to have a position on writing C++
for game dev. That is gatekeeping and No True Scotsman.

\- At least someone on Twitter called some tweets "retarded", etc., so OP
thought to continue the conversation by ignoring nonsense and restating
concerns in a healthier tone. This post _is_ his attempt to be productive,
apparently.

\- Any perceived slight against you, you have inferred. No offense intended.

\- Perhaps HN discussions are only marginally better than Twitter if people
can't avoid making discussions about fold operators personally.

~~~
winterismute
> winterismute questioned qualifications to have a position on writing C++ for
> game dev. That is gatekeeping and No True Scotsman

Well, not really. Some of the people on twitter (and the author himself)
basically pointed out that "one of the main reason we do not use that coding
style is that, despite the advantages, when you need to work on a codebase
that requires both performant simulation of the planet and rapid
experimentation of mechanics at the same time by hundreds of de-localized
devs, you end up seeing many of its limitations". If you want to handle this
argument, you need to have a clear idea of what are the priorities in such a
scenario, which I doubt the author has. Even more interestingly, the article
does not raise the point "this is how I write modern C++, I think it will
benefit all the industries" but specifically seems to want to argue that this
style helps gamedev particularly despite showing basically no domain-specific
observations, nor anything that convinces me he knows what are the critical
problems in the "triple A game" scenario... I don't think I can call this
gatekeeping if I am not convinced - even by reading only the article - that
the author has a good grasp of the fundamental problems involved in the
development of big games, or not?

~~~
humanrebar
The piece makes the point that the features presented have negligible to no
downsides in the context of gamedev (and other contexts as well), and your
argument doesn't refute that other than by assertion. It instead tries to
disqualify the relevant points based on the credentials of the author.

While making big assumptions about the author's experience in the process.
It's plausible he has experience in complex, performance-sensitive, highly
collaborative C++ as well. The whole argument hinges on partial information
and disbelief, really.

------
sharpneli
There was a good comparison about those floating around:

[https://imgur.com/a/u1N4Fpy](https://imgur.com/a/u1N4Fpy)

For me the code on the right is far more readable and easier to understand.

~~~
hhmc
The code on the right feels in _mildly_ bad faith to me.

    
    
      - consts are dropped
      - variable definitions are merged onto multiple lines
      - usefully named constants like `nPixels`, `nBytes` are elided
      - the `idx` lambda is inlined
    

The net effect, for an initial skim, is that the code on the right looks
terser and simpler. But in reality many of the "short cuts" hurt the long term
quality of the code.

~~~
xfs
I love C++17 fold expression, but...

Local consts are not particularly useful, especially for things like ints.
Compilers know it's const, and a reader doesn't need to worry about it in a
local scope.

width, height usually come in pairs, so putting them on the same line is
usual.

nPixels and nBytes are not used more than once so they are not useful
abstractions, and the pattern of `width * height * bytes_per_pixel` is so
common that there is no ambiguity about what it does.

The idx lambda is probably a distracting abstraction which requires the reader
to think of it in a different context than the immediate what pixel goes to
where. Again, the pixel moving pattern in the right is so common it's familiar
to most people working in similar areas, and in terms of error-prone both are
not better than the other.

~~~
danielscrubs
When I read that it's const I can in my mind "drop it". It's there, I know
where the value was set and don't have to skim any longer for updates. It
isn't for the compiler, its for me, the next guy who has to read your code.

Same thing with doing two separate things in the loop. Not only can it stop
the the compiler from optimisations from time to time (the c++ compiler is
very clever... but many times it gives up), I've had speed increases in
breaking it up into several loops, but I also have to search and in my mind
break things up. Very easy to do when I'm writing the code (and I'd probably
do the same because of laziness), but quite annoying when I read someone
else's code that does it.

~~~
xfs
If you as a reader need const to make sure a local variable is not changed
this is usually a symptom of this function being too long to see at a glance.
And quite often as code changes I do need to make some local variable non-
const, and it quickly becomes annoying to fiddle back and forth enforcing
const-ness of every local variable.

The argument about optimization is almost certainly premature optimization.
Most of the time how you write a loop doesn't matter. You only find out what
matters via profiling and refactor accordingly.

~~~
gintery
Most people hate const at first (myself included) but once you get used to it,
it really does reduces mental load. Not having to glance around is precisely
the point, its not a big thing but it does help.

~~~
xfs
I stopped using it for locals after it increased my mental load. The
maintenance cost of const for all local variables is huge during refactor like
you're fighting it just to get things done. And in most cases where the type
of a variable matters I do have to glance around like when I need to remove a
variable or change its type or refactor its dependent variables so const
doesn't really for those cases.

~~~
gintery
If you have to assign to a const variable during a small refactor, then maybe
it shouldn't have been const in the first place? I'm struggling to imagine
examples where this is a real problem

------
s9w
I agree with the author on many things - parameter packs however.. I've looked
at them several times. Even if you get a grasp on their syntax, I've almost
never been in a situation where they could be used. As he correctly observes
it's a compile time thing.

> In my particular scenario, all the texture file paths are hardcoded

That feels like a very unique case. Usually data is given in a vector or
something and then you're out of luck anyways. Plus the syntax is very
unintuitive (think about your colleagues), debuggability is zero.

Also the argument about constness feels a bit contrived. Sure with this you
can write const and feel good, but the other version is hardly a bad nonconst.
You can wrap things in functions or whatnot. The function seems to be a bit
long anyways.

~~~
leni536
Variadic templates are essential for simple, argument forwarding template
functions. std::vector::emplace_back is a very simple example, this kind of
use comes up now and then during work.

[1]
[https://en.cppreference.com/w/cpp/container/vector/emplace_b...](https://en.cppreference.com/w/cpp/container/vector/emplace_back)

edit: This also shows that you don't have to be able to write variadic
templates to reap the benefits of it. You can enjoy the benefits while using a
library that uses it.

~~~
Koshkin
The key to understanding techniques based on templates is to realize that C++
templates are _just another programming language_ that (a) is functional, (b)
is interpreted at compile time, and (c) where values are C++ types.

------
unchar1
I find the twitter thread to be specially sad, since I see a lot of people
genuinely try to explain that compile times/familiar syntax are more important
to them, but the author seems adamant that they just don't know what modern
tech is and they should learn the "right" approach.

------
leni536
I feel that most of the problems with debuggability is tooling (still). No, I
do not want to debug the standard library most of the time, please let me
optimize that, while I keep my part unoptimized.

Hopefully modules will allow mixed optimization levels for template headers.

Metaprogramming also lacks a good debugging story, but I would be happy to be
proven wrong.

In my opinion these are not language problems, but tooling problems.

~~~
Jare
There are designs and usage choices that make the tooling problem orders of
magnitude harder. You still have to use the tools you have, today, while the
tools of tomorrow arrive. The debate is about what and why (some of) those
choices are taken or rejected by different people.

------
nobbis
His personal blog post has eight obtrusive ads plus a Donate button (to an
engineer in finance in London.)

I get the sense the author's deliberately stirring controversy.

~~~
hellofunk
His Twitter profile pic isn't helping either.

~~~
dkersten
Maybe he changed it, but.. what is wrong with his twitter profile pic? It
looks pretty ordinary to me. Am I missing something?

~~~
hellofunk
Sorry, meant the banner pic on his profile page, at top.

------
tomnj
I’m often disappointed by how disrespectful people can be in programming
discussions. It’s one thing to discuss tradeoffs but ridiculing peoples’
choices or speaking in absolutes is not helpful. There’s no One True Way to
program.

~~~
eska
I always wondered whether it's programming in particular that attracts a lot
of people that think in only black and white, or whether it's the same in
other industries as well.

~~~
pfranz
I know this kind of turns things up to 11, but about 5 years ago there was a
book published called, "Engineers of Jihad." A bunch of news articles were
written when it was published. I have zero idea how reputable it is, or if its
been later debunked (I never read the book itself or heard any followup). My
recollection is that one assertion offered was that many engineers are
predisposed because engineering has logical, straightforward order and
hierarchy while most of life is a lot more messy.

Separately, I think programming itself is solitary and predisposed to people
who don't focus on building social skills (myself included). I think online
(pseudonymous) communication can introduce all sorts of problems.

------
ducaale
The author of this article has made the Dive into C++11/14 series, which is
about using modern C++ for Game development.

[1]
[https://www.youtube.com/playlist?list=PLTEcWGdSiQenl4YRPvSqW...](https://www.youtube.com/playlist?list=PLTEcWGdSiQenl4YRPvSqW7UPC6SiGNN7e)

------
saagarjha
Really the “problem” here is that C++’s functional constructs will never
allocate memory, giving them somewhat strange signatures that don’t really
conduce themselves well to typical functional concepts. At compile time there
is no such goal and as such some of these constructs can only be found there.

~~~
MauranKilom
According to
[https://en.cppreference.com/w/cpp/algorithm/stable_sort](https://en.cppreference.com/w/cpp/algorithm/stable_sort):

> This function attempts to allocate a temporary buffer equal in size to the
> sequence to be sorted. If the allocation fails, the less efficient algorithm
> is chosen.

Or is this not what you consider a "functional construct"?

~~~
elbows
It's more of an issue with things like std::transform or std::copy_if.
Ergonomically, it's nice if these kinds of functions either allocate and
return a new container, or return an iterator that yields the elements of the
result.

But the C++ versions of these take the result location as an argument. It
makes chaining them together a hassle because you have to create all the
intermediate containers explicitly.

I think there are good reasons for the STL to work this way, but it can make
programming in a functional style pretty inconvenient compared to a lot of
other languages.

~~~
leni536
Ranges will help with some of the composability issues of standard library
algorithms.

[1]
[https://en.cppreference.com/w/cpp/ranges](https://en.cppreference.com/w/cpp/ranges)

------
lewis1028282
That site is awful on mobile and filled with ads. Makes it impossible to read.

------
pacificat0r
He got upset when ppl with 15+ years of experience, working at dice or
directors of tech at Activision pointed out that modern c++ is over
complicated and they've been in that phase of liking abstractions but moved
on. Funny he blanked the names of those dudes and some other milder replies.

Then he wrote a blog post on it.

------
DethNinja
I would personally use for loop without size_t. It is short and simple and
works as intended, anyone can understand it.

~~~
HelloNurse
Do you mean with decltype(std::declval<const Image&>().width) as the article
suggests? Or with an ad-hoc typedef, e.g. Image::size_type? Or with a fixed
type that _should_ work correctly? This use of auto is particularly valuable.

~~~
DethNinja
Honestly I would just use auto. I favor heavy use of auto actually, if you got
a modern IDE auto is a really great feature.

------
jdmoreira
I stopped reading at "I would like this to happen at runtime".

I got burnt so many times by runtime craziness already that the only thing I
want happening at runtime is trivial code running from top to bottom.

But if you enjoy runtime hacks, more power to you! You must be a much better
developer than me.

------
drummer
The mentioned cppcon presentation by Dan Saks is awesome. Highly recommend
watching even if you are not interested in C or C++ :
[https://youtu.be/D7Sd8A6_fYU](https://youtu.be/D7Sd8A6_fYU)

------
theincredulousk
I don’t think this really covers the whole topic, but yeah the Twitter thread
is toxic in the exact way most threads are tbh...

I’ve been using C++ for about 15 years now, and also tbh, and dislike just
about everything beyond 11-14. The cognitive load reading code has just
skyrocketed. The simple basis of the issue was when the meaning of existing
syntax elements being “overloaded”. &, &&, [] in my mind. It is not
complicated or profound reasoning - simple as now you need to decipher more
context to understand what is being expressed. I’ve been materially
disappointed to spend 5-10 minutes reading a small bit of unfamiliar code only
to learn that what it ultimately expressed was truly trivial. It just makes
you feel like c++ has become like the toxic parts of academics/math where
everything is expressed in the most complex way possible to establish
superiority over others.

The comments about debug ability are true. STL sources/template stacks are
absolutely terrible to work with. Luckily with the STL though, what 99%+ of
the time, the bug is in your usage, so it doesn’t matter much. To be fair
though that is a trade off - I feel like I’m on vacation debugging c# or
Python, but that is (almost) an intrinsic benefit of interpreted languages,
with the well-known associated costs. Just templates... the cognitive load of
having to understand (simply read) and debug code from an abstract definition
introduces significant pain in terms of debug ability and general usability.

“C++ is not the STL” - true but c’mon - whatever is built-in mostly defines
the stuff that you can be sure is portable, and rely on as a standard
practice/resource in large projects. Very few sane people want 5 different
implementations of a vector or string from god knows where with god knows what
bugs. In the industry for c++, there is serious need for the _option_ to
“reinvent the wheel” of standard libs, but it shouldn’t be that this is
necessary to achieve baseline performance or usability in common use cases.

I am of the same opinion as at least one other below - simplicity of
implementation and readability of language are king in the “real” world.
What’s better than the beauty of a meta-programmed, absolute masterpiece of
modern C++ and zero-cost abstractions? A program that I can understand in 10
minutes two years after it was written, or better yet that can be understood
and debugged by 90% of skill levels instead of 10%.

This conversation could, and does, go on without end. My net-net conclusion so
far is that modern C++ has done more harm to itself than good because it is
trying to be too much. When the creator of the language can’t even keep up
with it enough to call himself an expert, it’s a pretty obvious red flag that
things have gone off the rails.

~~~
smallstepforman
Kind of agree, and the root cause may be backwards compatibility so _another_
construct is invented which is slightly different.

The other day I explored std::promise and std::future, only to realise that
all it is under the hood is a semaphore (which starts with count of zero) and
a pointer. The promise.set() updates the pointed memory and releases the
semaphore, while future.get() waits for the semaphore and reads the memory. My
workaround was just as many lines of code with abstractions that are no more
complex. So whats the point of promise/futures? Async adds spawning a thread
to the mix, yet another unneeded abstraction. Coroutine adds more. Just be
done with this and add proper Actor model and call it a day.

------
Hitton
I don't understand why does he show loop-based version using 2 for loops
instead of using just one. I usually work in higher-level programming
languages, but can't believe that cache hits or something else makes it faster
than finding sum and maximum both while going through all images only once.

And having just one loop makes it simpler too.

~~~
nnoitra
Choosing C++ over C means his programming abilities aren't very good.

~~~
dkersten
The fact that you seem to think C is blanket superior to C++ means that your
programming abilities aren't very good. There are many reasons to choose C++
over C, especially if those two are your only options.

------
Epskampie
The author mentions that you shouldn’t use std::, but offers offers no
alternative other than “write your own version that is shorter and better”.

