
Why I Write Games in C (yes, C) - WoodenChair
https://jonathanwhiting.com/writing/blog/games_in_c/
======
zemo
> The stop-the-world garbage collection is a big pain for games, stopping the
> world is something you can't really afford to do.

I love this opinion from games programmers because they never qualify it and
talk about what their latency budgets are and what they do in lieu of a
garbage collector. They just hand wave and say "GC can't work". The reality is
you still have to free resources, so it's not like the garbage collector is
doing work that doesn't need to be done. What latency budgets are you working
with? How often do you do work to free resources? What are the latency
requirements there? Even at 144 fps, that's 7ms per frame. If you have a
garbage collector that runs in 200us, you could run a GC on every single frame
and use less than 3% of your frame budget on the GC pause. I'm -not-
suggesting that running a GC on every frame is a good idea or that it should
be done, but what I find so deeply frustrating is that the argument that GC
can't work in a game engine is never qualified.

edit: wow for once the replies are actually good, very pleased with this
discussion.

~~~
stephc_int13
I know this subject quite well and I will later publish a detailed article.

The real run-time cost of memory management done well in a modern game engine
written without OOP features is extremely low.

We usually use a few very simple specialized memory allocators, you'd probably
be surprised by how simple memory management can be.

The trick is to not use the same allocator when the lifetime is different.

Some resources are allocated once and basically never freed.

Some resources are allocated per level and freed all at once at the end.

Some resources are allocated during a frame and freed all at once when a new
frame starts.

And lastly, a few resources are allocated and freed randomly, and here the
cost of fragmentation is manageable because we're talking about a few small
chunks (like network packets)

~~~
zemo
as an example point, the Go garbage collector clears heaps of 18gb in sub-
millisecond latencies. If I'm understanding the problem at hand (maybe I'm
not!), given an engine running at a target framerate of 144 frames per second,
you're working with a latency budget of about 7ms per frame. Do you always use
all 7ms, or do you sometimes sleep or spin until the next frame to await user
input?

We can also look at it from the other direction: if your engine is adjusting
its framerate dynamically based on the time it takes to process each frame,
and you can do the entire work for a frame in 10ms, does that give you a
target of 100 fps? If you tack on another half millisecond to run a GC pause,
would your target framerate just be 95 fps?

And what do you do when the set of assets to be displayed isn't deterministic?
E.g., an open world game with no loading times, or a game with user-defined
assets?

~~~
stephc_int13
There is no general answer to this question. Frame latency, timing and
synchronization is a difficult subject.

Some games are double or triple buffered.

Rendering is not always running at the same frequency as the game update.

The game update is sometimes fixed, but not always.

I've had very awful experience with GC in the past, on Android, the game code
was full C/C++ with a bit of Java to talk to the system APIs, I had to use the
camera stream. At the time (2010) Android was still a hot mess full of badly
over engineered code.

The Camera API was simply triggering a garbage collect about every 3-4 frames,
it locked the camera stream for 100ms (not nanoseconds, milliseconds!) The
Android port of this game was cancelled because of that, it was simply
impossible to disable the "feature".

~~~
weberc2
You didn't have a bad experience with GC in the past, you had a bad experience
with a single GC implementation, one which was almost certainly optimized for
throughput and not latency and in a language that pushes you toward GC
pressure by default. :)

~~~
stephc_int13
This is an example.

I never worked with Unity myself but I worked with people using Unity as their
game engine, they _all_ had problems with stuttering caused by the GC at some
point.

You can try to search Unity forums about this subject, you'll find hundreds or
thousands of topics.

What really bothers me with GC is that it solves a pain I never felt, and
creates a lot of problems that are usually more difficult to solve.

This is a typical case of the cure being (much) worse than the illness.

~~~
weberc2
What is Unity’s STW time? Is it optimized for latency? If not, you’re using
the wrong collector. The pain point it solves is time spent debugging memory
issues and generally a slower pace of development, but of course if you’re
using a collector optimized for throughout, you’ll have a bad time. Further, a
low-latency GC will pay off more for people who haven’t been using C or C++
for 10 years than those who have.

~~~
gameswithgo
It is very nice to say that, in theory, a GC could work very well for
performance demanding games. But until someone builds that GC, in an
environment where everything else is suitable for games also, it is academic.
We can't actually build a game with a theoretical ecosystem.

~~~
weberc2
Of course, this is a theoretical conversation. My point is that you don’t
refute the claim, “low latency GCs are suitable for game dev” with “I used a
high latency GC and had a bad time”. Nor do you conclude that GCs inherently
introduce latency issues based on a bad experience with a high latency GC.
There is neither theory nor experiment that supports the claim that GCs are
inherently unsuitable for game dev.

------
stephc_int13
The author's opinion is uncommon but not unique.

A few examples: [https://handmadehero.org/](https://handmadehero.org/)
[https://ourmachinery.com/post/physical-
design/](https://ourmachinery.com/post/physical-design/)

Simple libs widely used in game dev circles:
[https://github.com/nothings/stb](https://github.com/nothings/stb)

This one is a full game engine with tools made for educational purpose:
[https://www.raylib.com/](https://www.raylib.com/)

I do write games and game engine code and tools in C++ without using any of
the OOP features.

I know quite a few of other professional, experienced and talented developers
doing the same, even full AA studios with up to 50 employees.

Some of them are going back to procedural C after a few years of
disappointment with OOP madness.

I'll publish articles about this later, but for now, all I have to say is that
it just works.

Newbies are often afraid of manual memory management and pointers, but there
are simple ways to avoid shooting yourself in the feet with those.

~~~
cwyers
> there are simple ways to avoid shooting yourself in the feet with those

A cursory look at the CVE list for any C software in the wild indicates that
no, there are not simple ways to avoid shooting yourself in the feet with
manual memory management in C. It's _incredibly hard_ even for "elite"
programmers who have a lot of incentive to avoid these problems. The
counterpoint is that people are probably going to spend less time looking for
ways to maliciously use those problems in your C game than they are in the
Linux kernel, but there is plenty of potential for even seasoned C coders to
engage in bullet-on-foot violence.

~~~
stephc_int13
Yeah, I am not talking about vulnerabilities. I am simply talking about ease
of use and avoidance of leaks and crashes.

Writing super secure code is usually not a requirement for gamedev, and I
completely agree, this is hard and potentially harder in C than, say, in Rust.

~~~
eropple
It's a requirement if you do network I/O whether you realize it or not.

~~~
tomp
If a bug in a game's network IO crashes/takes over the whole OS, the security
vulnerability is in the OS, not in the game.

~~~
eropple
No. Security is a full-stack endeavor. At all levels it is incumbent upon
software professionals to build secure and resilient systems, any time they're
exposed to a network. The OS should backstop your efforts, not replace them.

~~~
tomp
I disagree. As a user, I want to be able to run weird software without having
it impact the rest of my system. Browsers and mobile OSs get this much more
right than desktop OSs.

~~~
eropple
This does not meaningfully follow. What you "want" is orthogonal to the
responsibility of the vendors of software.

The OS should protect where it can. So should software, lest your networked
game nuke, say, the parts of your home directory to which it has permissions
because without those permissions it can't do something it needs to.

This is just defense-in-depth. It's super basic stuff and HN is literally the
only place I see people galaxy-braining about the idea that you have an
obligation to not write shit code.

~~~
yellowapple
I suspect GP's point is that a networked game should be limited in access to
only parts of one's home directory relevant for that game (that is: the game's
own save data or configuration files or what have you). It is absolutely the
job of the operating system to provide that sort of sandboxing/isolation.

~~~
eropple
Sure. That's great. That also breaks down sometimes, too.

Defense in depth. Write good, sane, secure, fail-closed code when you want to
ship that code to other people--or don't write code.

------
einpoklum
> [C++] is high performance, and it offers but features I don't want, and at a
> great complexity cost.

1\. Complexity of implementation is not complexity of use. Example: Take
std::array vs a plain C array. std::array is a few hundreds of lines of code.
But - it's about as inutitive to use; has more convenience features; easier
for the compiler to optimize; and doesn't let you should yourself in the foot
that easily.

2\. If Whiting don't want _any_ of the features of C++ - his software-writing
skills are suspect. Now, few people need or want all of C++'s features - but
C's are definitely insufficient, especially if you consider both the langauge
and some of the standard library (or non-standard common libraries).

3\. The complexity you have to tackle with C++ is lower than the complexity of
non-standardized often-inconsistent idiosyncratic platforms and libraries for
achieving the same things with C (or C + assembly).

So I find Whiting's argument for C over C++ unconvincing. IMHO C++ would be
the least-bad language for writing demanding games in.

~~~
tejohnso
Also, the C++ committee is _very_ serious about not paying for what you don't
use. You can rest assured that there's no unused, hidden C++ bloat that is
chipping away at your frame budget.

This is not meant to be a strawman against the complexity cost argument, it's
just a related feature of C++ that I thought might be worth explicitly
pointing out in case anyone is misinterpreting.

Also, if anyone has a credible dispute against the claim I'd like to hear it.

~~~
Pombal
You are describing 'zero cost abstractions'. Except that they are anything but
zero cost. Game developers and low level programmers shy away from them
because of their impact in compile and build times, debug build performance
and stack trace bloat, increased cognitive load, reduced refactoring ability.
Even std::unique_ptr has runtime costs in release builds that a raw pointer
does not.

In game development performance is a feature and anything that makes
performance and memory usage less deterministic is frowned upon, like a GC.
But iteration times are still paramount and things that make compile, load and
link times longer and make it more difficult to debug are also frowned upon.
Yes, we'd like to have our cake and eat it too :)

~~~
jcelerier
> Even std::unique_ptr has runtime costs in release builds that a raw pointer
> does not.

O RLY ? [https://gcc.godbolt.org/z/QhbUjI](https://gcc.godbolt.org/z/QhbUjI)

~~~
Pombal
Yes, really. It's fine for simple types but for more complex types in more
complex situations, you pay the price.

Unique pointers carry around not just the type but also a default deleter, if
you provide one. That deleter has to be copied around, checked for nullptr
before execution and set to nullptr when ownership changes.

For even more examples of this have a look at this talk when it comes out:
[https://cppcon2019.sched.com/event/Sfq4/there-are-no-zero-
co...](https://cppcon2019.sched.com/event/Sfq4/there-are-no-zero-cost-
abstractions)

~~~
rcxdude
Only if the deleter you define has any state, which is very rare and in that
case you would need to copy that data around anyway...

(for example:
[https://gcc.godbolt.org/z/mU7hub](https://gcc.godbolt.org/z/mU7hub))

------
ChrisSD
So... essentially "I know and like C". Which is great! But as you say, that's
not particularly useful for anybody else.

~~~
the_gipsy
The guy should try Rust and then explain why he prefers either, that would be
interesting.

~~~
z3phyr
I like rust, but for me, it is significantly more difficult to read and write.
Same can be said about C++ with extreme STL code..

I prefer to read and write simple C like syntax. I would love it if I can get
Rust concept of borrowing in C, or even simple operator overloading..

~~~
Johan-bjareholt
Ironic, the most complicated thing for me to understand when I started writing
rust was borrowing and lifetimes. The rest of the common features of Rust
isn't too complicated to understand.

~~~
z3phyr
Understanding the concepts is not the issue with me (as of now). It is the
hard to read syntax (my personal opinion!). It puts a lot strain on my mind.
Same is the case with C++ with extreme use of STL. I have difficulty in
reading that, although with clear mind, I will eventually get the code, it is
not fun.

------
relativeadv
"This is just an opinion but, it's also objective truth, you'll be a superior
human in everyway to lesser mud-blood human's who never managed to release
anything. Everyday they'll continue to toil away in the fields like Russia
serfs, while you can skip past them like a wise and jolly Rasputin. (I've
released exactly zero games independently, so I am too one of the serfs, with
only homemade vodka and sweet dreams of a Bolshevik ruled future to keep me
going.)"

[http://howtomakeanrpg.com/a/not-your-
problem.html](http://howtomakeanrpg.com/a/not-your-problem.html)

~~~
dbtqgoat
That’s the exact tone I got from the first words in.

------
Bekwnn
I have similar frustrations using C++, but don't feel using C or even Rust
would be an improvement, more of "side-grade" due to various tradeoffs those
languages have for game development compared to C++.

I am excited about the development of both Zig and Jai since they both are
trying to fill what I believe is a much needed role of a C-like language with
a few more nice language features like better compile-time code, better
compiler error messages, better error handling that doesn't use exceptions,
and build scripts written natively in the languages, all while sticking to
fast compile times and simplicity.

Games are a decently different domain than others, and there is a lot of valid
complaining about C++. It's a known issue, we just need something that's
clearly better, which so far I think those two languages are. I've chipped in
a few dollars a month to support Zig and have been using it in my free time,
but it still very much feels like the version 0.4.0 that it is.

This wait will probably be a few more painful years.

~~~
lone_haxx0r
The thing I hate about them is the backwards declaration syntax.

var x:i32 = 5; ugh, it makes no sense.

~~~
FreeFull
Usually you wouldn't write the type, so `let x = 5;` In general, the C style
type declarations complicate parsing quite a lot as well.

------
gambiting
>>Nobody does this.

I disagree - I work at a AAA games studio and we're currently building a
little project made entirely in C. Not even a hint of C++. This is probably
going to end up being just a little research thing in the end, but don't think
that no one does this anymore.

------
CodeGlitch
Anyone who is interested in secure, plain, well-written C-code should checkout
the OpenBSD project. I've just been browsing through the code to brush-up on
my C as it's been a few years since I've had to use C in anger, and it's a joy
to read.

~~~
ncmncm
Whenever I look at BSD code, it reminds me of how we did everything in the
'80s. There's an esthetic to stone knives and bearskins (check out "Primitive
Technology" series on YouTube) but performance suffers on modern hardware.

Linux is irredeemably complex, and getting moreso all the time. My code
nowadays bypasses it wherever possible -- O_DIRECT files, memory-mapped files,
kernel-bypass NIC libraries that set up hardware ring buffers mapped into user
address space.

------
Hokusai
Some points:

> All my solo project games I've been making recently have been written in
> 'vanilla' C.

The author is talking about solo projects, not big projects where dozens of
teams may be collaborating. It is good that he makes the point at the
beginning.

> Nobody does this.

This is just not true. There are solo projects developed in Python, Go, even
Commodore 64 assembly. When you do something for fun, you can have as much fun
with it as you want.

> It would be nice to make things for the web, but it feels like a
> terrifyingly fast moving enviroment.

The author should look into Emscripten. It is widely used in the games
industry that aims for the web.

> C++ ... It is also slow to compile compared to C.

Probably an important point. You need very good practices to make C++ compile
fast. Most people don't follow them as adds complexity to the project.

> Haxe feels much more promising than most alternatives.

Haxe is kind of Object-Oriented and like C++. So, I do not see the point after
bashing C++'s OOP so badly. For me, the problem is that it transpiles to
another languages. So, when something fails does it in code that you didn't
write but the Haxe did. This makes debugging harder that it already is.

> Some people just say screw it, I'll write my own language, the language I
> want to use. I admire this, and sometimes I toy with the idea of doing the
> same.

Yep. Something possible for your own fun. Not a good idea when developing
commercially. It makes sense for him to choose one language for commercial
projects and another, or even create his own, in his spare time.

> It can be made to run on just about anything. Usually this is relatively
> easy. It is hard to imagine a time when this won't be the case.

This is the big advantage of C. If doesn't run C, probably it just doesn't run
at all.

> I absolutely DO NOT mean to say "hey, you should use C too"

For solo projects is a giver that each developer should use the best-suited
tools for the task and their abilities and preferences.

I, personally, like C++. I have developed big and small projects in C++ and
for me comes natural to write object oriented code. For big projects I see
advantatges in being more rigid than C. The extra rigidity makes more
difficult that there is unexpected funky stuff happening. So, it is easier to
use for teams that maybe situated in different countries. For a solo project,
you do not need that rigidity as you know how your own code works.

~~~
kllrnohj
> > It can be made to run on just about anything. Usually this is relatively
> easy. It is hard to imagine a time when this won't be the case.

> This is the big advantage of C. If doesn't run C, probably it just doesn't
> run at all.

Is that really an advantage of C over C++? They have identical runtime
requirements and typically share a common compiler (although one of those,
MSVC, barely supports C). Is there actually anything that can run C but which
cannot run C++?

~~~
Hokusai
> Is that really an advantage of C over C++?

You may find that not all C++ features are supported for the least popular
processors or for old ones.

Standard C++ threads, for example, were not supported by GCC for Windows for a
long time. I do not know what is the current status, thou.

------
mumblemumble
I'm currently trying to decide if Nim is my C replacement.

On the one hand, it isn't C++, and it has real type safety, and it interfaces
with existing C libraries very easily.

On the other hand, it is rapidly getting very complicated, and seems almost
eager to become another C++ in terms of sheer volume of language features.

~~~
F-0X
>On the other hand, it is rapidly getting very complicated

This seems to be the case for almost any "C replacement", and it will be (my
prediction, at least) the reason they all fail.

I feel I might be a bit of an outlier in this respect, but I have only ever
enjoyed using languages which are small and simple - C, Go, Scheme. The times
I've tried Rust, it's been nice to have code that cannot segfault, but I find
it such a chore to deal with. Whenever I've read open-source projects in Rust
it's always a readability disaster. Also the compiler emitted warnings because
functions were not snake case, which I found obnoxious.

~~~
zem
you would probably like zig, if you haven't given it a look yet.

~~~
PezzaDev
I feel zig is now experiencing feature creep now too sadly. Async and await
and coroutines are being added to it, because it's the new-ish hotness I
guess? Stopped paying attention to the language after that. I was hoping for a
more streamlined language.

~~~
mumblemumble
FWIW, adding async/await feels excusable to me.

I was strongly resistant to it when it first came out in C# something like 10
years ago. I thought it was language cruft that was better handled with a
library solution, and nothing more.

I was convinced to give it a real try about 4 years ago, and had a serious
change of heart after only a week or so. It _was_ just syntactic sugar, but it
was syntactic sugar that made concurrent code much easier to write. Not just
in terms of making the code more verbose, but in terms of making it easier to
do correctly. Because, at least in the way the C# team designed it, it subtly
steers you towards doing things in smarter and safer ways, and away from the
usual hot mess that is multithreading.

I've heard similarly positive things about goroutines, which seem at first
blush to be about the same thing.

Given that Zig is ostensibly about helping people to write performant code
more safely, that leaves me thinking that there's an argument to be made that
async/await are a good fit for Zig's mission, and not just kitchen sink
features.

------
makecheck
I’m not sure why but programmers like to reject entire languages instead of
just rejecting the features they don’t like. A lot of stuff in C++ is entirely
avoidable.

Protocol-oriented programming is extremely useful for games and I like
Objective-C for that reason. (Objective-C doesn’t meet his mentioned goal of
portability but this is an example where C++ could be used to gain “light”
inheritance without having to opt in to the rest of the mess that C++ can be.)

~~~
kgabis
Compilation times even for simple C++ programs are far too long and it's not
like you can reject this "feature" unless you basically write C code compiled
with C++ compiler. For instance: simple hello world with iostreams takes 280ms
to compile vs 40ms for C equivalent with stdio. And it only gets exponentially
worse with bigger programs.

------
htk
Very interesting point of view. I wish I had projects (like games) that could
benefit to craft meticulously with C. The feel of coding closer to the
hardware is great.

~~~
krapp
I'm not sure C is that much closer to the hardware, nowadays.

~~~
kccqzy
C is closer to the hardware in the sense that with any given C code, I can
pretty much predict what the generated assembly will look like. You cannot say
the same for, say, Python.

~~~
Koshkin
> _pretty much_

Not really... Not only the generated assembly code heavily depends on the
compiler (LLVM, gcc, Intel, MSVC...) and the optimization settings, you cannot
be completely sure what the _machine code_ generated by _the assembler_ even
looks like these days.

~~~
someguydave
and you also won't be able to predict the timings of complex code as various
"uncore" and on-core resource limitations bite

------
thw0rted
> The ideal language would be one I can memorize, and then never have to look
> things up.

If the language is so small I never have to look up anything when using it,
it's also so small I'm going to have to spend ages re-re-re-implementing basic
functionality that I get for free from a higher level language.

------
abetusk
I had never considered a "data-centric" approach to be anti OOP but from the
article:

> I am not an OOP convert. I've spent most of my professional life working
> with classes and objects, but the more time I spend, the less I understand
> why you'd want to combine code and data so rigidly. I want to handle data as
> data and write the code that best fits a particular situation.

I've come around to the idea much more that data should be the privileged
structure and code should be considered transformations of the data. In some
sense, code should be light and interchangeable and let the data is the first
class citizen.

Are OOP and "data-centric" approaches at odds with each other? Does OOP only
have validity when the data is relatively "small" and can be abstracted away
easily?

~~~
inimino
> Are OOP and "data-centric" approaches at odds with each other?

Yes, because OOP Objects are data plus code (methods). If you don't have
methods, but just pass data around, you don't have OOP. If you do have
methods, you meet the "expression problem".

If you have static types and well typed code you get pure data at runtime, and
if you include unions and product or sum types at runtime, you end up with
algebraic datatypes, and if you have RTTI and polymorphism and inheritance
then you get classical OOP.

If you have just data living in memory at runtime, and just transformations of
the data (functions) sitting in your code, then you get FP.

------
ufo
I think this discussion is incomplete without a mention to scripting. (Writing
the core engine in a low-level language like C but implementing some or all
game logic in a higher-level garbage-collected language such as Lua or Python)

------
rootVIII
This line is pretty funny: "C++ is still the most common language for writing
games, and not without reason. I still do almost all of my contract work in
it. I dislike it intensely." hahaha poor guy

------
hesdeadjim
He dismisses C# without even knowing what options he has. The latest stuff
Unity has been doing is heavily biased towards data driven design where almost
everything is a struct. There are very few classes to be found that use any
kind of polymorphism, and it is actively discouraged.

You could easily treat Unity as a cross platform rendering engine and asset
loading system, and then build your own engine on top of it. I see very little
reason to drop down to C or C++ at this point, especially if your goal is to
ship your game to as many people as possible.

~~~
jayd16
As a Unity dev this is really generous to Unity. After all it's extremely
complex in a way the author would hate.

That said, I'd love to see a pure C# engine on .NET core 3 that has access to
Span<T>. C# is set up as a great high level language with low level features
as long as you're willing to do the work you would have done in C or C++.

~~~
hesdeadjim
Any game engine evolves towards serious complexity as time goes on. The
question is, do you want to waste time building systems that a million man
hours have gone into to make them resilient across a thousand different
permutations of hardware? Or do you want to ship your game?

I've built a non-trivial engine active from around 2010 to 2015 that targeted
all iOS and Android devices. Our test fleet of devices was enormous because
we'd run into silly issues that were always device specific. My favorite, one
particular phone with one particular driver version for the GPU did not
correctly implement the ES 2.0 standard for constant defined variables. It
caused our games to crash, and because of the scale of mobile this affected
hundreds of thousands of users.

At this point you could not pay me to build an engine, because for anything
that isn't a toy you end up having to solve these problems yourself.

Treating Unity as a rendering engine and asset loading system is not
immediately "easy", but it's a very far cry from "hard". Dismissing it or
other options simply because of unfamiliarity or a shallow distaste for OOP is
ignorant at best and actively misleading otherwise.

------
Uptrenda
I've been surprised and delighted by C recently myself; Being able to make a
small change to a datatype, variable name, and so on, and then instantly
seeing an IDE highlight errors relating to it is amazing for productivity. The
language is incredibly simple. I mostly use structures to define my own
'types' and design functions to work on pointers to those types. That way you
can control whether you want your data to live on the stack or heap and its
fairly robust. I've rarely if ever had to use function pointers or macros, but
they're there if you need more advanced features. I find just being able to
manipulate memory and buffers as raw bytes with no bullshit wrappers helps you
so much with actual programming (TM.)

As far as data types go, I haven't used much more than linked lists and basic
hash maps so far. For everything else I use fixed-sized arrays. It helps you
precisely control how much memory the program is using without worrying that
some higher-level magic garbage collector will overflow. Despite having that
level of control, I haven't been frustrated by it yet. Yes, the standard
library is very basic, but you can add the types you need very easily. There's
some beautiful C code out there that will give you exactly what you want
without having to include hundreds of obscure futures you'll never need.
Overall, I've been very happy with it, and I look forwards to trying to run my
code on all kinds of weird and wonderful platforms. I see huge competitive
benefits for C code bases, to be honest.

------
billfruit
They didn't dwell on why they chose C over C++, in much detail. In my view the
conveniences and quality of life features provided by c++ makes it a better
choice than C for large programs.

Also there are many kind of games that do not require a real time user
interaction including many turn based games, card games, etc, so the type of
game could be a factor in the selection of the tool. Some time s I guess game
design can incorporate pauses and delays that might be required for technical
reasons.

------
oscargrouch
One of my dream side-projects is to create something inspired by Ken
Thompson's C (the plan9 compiler), with some ideas borrowed for Go, where this
"almost C" language could be written in a valid C just like you can do it with
C++.

things like:

(Composing structs)

    
    
        struct Parent {int number}
        struct Child {Parent; int another_number} // anonymous struct convention
    
        Child c;
        c.number = 10;
        c.another_number = 11;
    

(Object notation, namespacing)

    
    
        int DoThis(Child* this, int input) {}
        Child* c ...
        c->DoThis(10)
    

(Someway to define public/private in structs and methods, like Go do with
convention of Lowercase/Uppercase components/function names)

and Finally

(Someway to define a interface and use vtables when needed just like Go´s
Interface but in a more C compatible way like.. )

    
    
        struct MyInterface {
         int (*Read)(int a, int b);
         int (*Write)(int a, int b);
        }
    

or

    
    
        struct MyInterface {
         int Read(int);
         int Write(char);
        }
    
        struct X {}
    
        int Read(X* this, int n) {}
        int Write(X* this, char c) {}
    
        X x = X{};
    
        x.Read(10);
        x.Write('b');  
        MyInterface* c = &x;
    
        (...)
    

(templates) - C++ way is fine

Thats it. With all this you would have "a better C", with compability with C
codebase and still a language much simpler than C++, yet powerful.

------
tripzilch
Can anyone explain me why this is on the front page? I mean it's basically
someone bashing languages they've not worked with, not understand or in the
case of C++ simply don't like. As in, he doesn't bring anything new or
insightful to the table, just "this is my personal opinion". Which btw is a
fine opinion, as far as personal ones go, not one that I share, but it's a
perfect example of "great let's agree to disagree".

Which IMHO basically amounts to trolling for HN comments if you post it here.
Now people are just going to argue how wrong he is, against and for their
favourite languages and nobody will learn anything new or useful.

Is this guy well-known or something? Did he make any good games in C?

Maybe I'm missing the point of this article (someone please explain), but it
looks like it's worth as a post is only as a discussion starter and an
inflammatory one at that. If there was any time a reason to flag a discussion
thread for being useless, it's this one I think.

------
blunte
We see articles not so different from this concept all the time on HN.
Recently it's about ORM vs SQL.

The core interfaces are really not terrible at all. They do have danger zones,
but their complexities are relatively low. As the author of this article
suggests, it's within the realm of something we can remember. That's a BIG
deal.

I haven't done C in a very long time. But with C, my recollection is that the
biggest issue is to make sure you keep track of your memory allocation and
releasing. Sure this is significant, but it's a direct concern. And if you
abstract by one level your management of memory, it's a lot easier (especially
if you include some testing).

I think it's important to periodically evaluate if our efforts to simplify a
situation have actually alleviated or simplified real issues that were hurting
us.

------
sgt
Just downloaded his game Mussel (it runs on macOS). Amazing fun - highly
recommended. Crazy CRT like effects.

------
mkroman
There's one important question left out: what kind of games does the author
make? I can see how 'vanilla' C can be more than sufficient for basic 2D
games, but as soon as you grow in complexity it can quickly become an
insurmountable task to grok.

~~~
astrophysics
Basic games like...Quake 3?

~~~
gmueckl
Yup. Quake 3 is basic. I remeber that the whole game had a "measly" 150.000
lines of code or so. It doesn't do a ton of things modern games do. The list
of things expected from modern games these days in comparion is far too long
to list here. These things have become elaborate world simulators. And all of
these features add up.

The Unreal Engine is around 4 million lines without dependencies (e.g. PhysX,
a proper audio engine, etc). You could try to do all that in C (good luck
finding a good physics library with a plain C interface, btw.). But you need
to have proper software design throughout the project and you will likely end
up replicating some kind of inheritance or polymorphism scheme somewhere.

~~~
gypsyharlot
Quake 3 has 230.000 lines of code. If Quake 3 is considered basic, then any
studio with less than 100 developers can probably reach for C.

The Unreal Engine is far more than a modern game. You wouldn't call Clang and
editor just because Vi was compiled with it.

~~~
gmueckl
The Unreal Engine is less than a modern game. The actual game is obviously
missing. Also, you need engine specific editing tools to make a 3d game that
looks more advanced than minecraft. It is a mistake to exclude them just
because the don't have to be shipped with the final product.

And Quake 3 was made by a team of roughly 20 people. Id Software was quite
small until they started to work on Rage.

------
seba_dos1
Nobody? I write my games in C as well :P The only other reasonable choice
would be Rust, but never got past "have to write bindings to my framework"
stage with it so far.

Pretty much the only things I miss in C for gamedev are sensible string
manipulation and lambdas/closures. Otherwise it works really well - I don't
think I would be able to make my games so multiplatform (at the moment
GNU/Linux, macOS, Windows, Android, Emscripten, Librem 5, Nokia N900, Pocket
CHIP, Raspberry Pi, Steam Link, Nintendo Switch w/ devkitPro; more to come...)
so easily with any other language.

------
zabzonk
Write C code. Compile with C++ compiler. Catch lots of bugs at compile time.

~~~
xamuel
Or even better, compile with a C compiler with proper flags set (like -Wall).
Catch lots of bugs and also enjoy blazing fast compile time!

~~~
zabzonk
Compilation speed for C++ is a function of the complexity of the code being
compiled (particularly templates), otherwise a C++ compiler will typically
compile C code (if it is compilable) at the same speed as a C compiler.

------
blunte
This is a surprisingly eloquent essay on the state of modern programming
languages (and obviously why the author things that sticking with C is a
better choice... and I'm fairly persuaded.)

------
Elrac
I'd like to voice my disagreement with the author about C having the shortest
compile times. From my own experience and reports all over the Web, it seems
that Go handily beats C at compilation speed.

This is mostly down to a lot of design decisions in Go made with the goal of
speeding up compilation. Elimination of header files, pre-compilation of Go
modules, efficiently parseable syntax and more.

From what I've seen, first-time compilation of Go programs competes well with
C, while incremental compiles are practically instantaneous.

------
xamuel
The author despises OOP, and I agree with him there. I would add that OOP can
be particularly bad for _games_ , because often times the OOP vocabulary
clashes with the game's own vocabulary: the game itself has objects (as in,
things the player can pick up and put in their inventory), the game itself has
classes (as in RPG classes). It might even have factories, depending on the
game.

All support for OOP would vanish overnight if people realized you can do all
the important OOP stuff using C structs.

~~~
eternalny1
> the game itself has objects (as in, things the player can pick up and put in
> their inventory), the game itself has classes (as in RPG classes). It might
> even have factories, depending on the game.

This makes no sense to me.

By that logic, it would be difficult for me to create a scheduling system for
classes being taught in factories with OOP, which it's not.

~~~
xamuel
For a program about classroom-scheduling, it would be nice if you could use
"class" as a structure name. Then you could declare a function as taking a
"class" as an argument. But if "class" is a reserved keyword in the language,
you have to come up with some other name like "_class" or "ClassStruct".

I didn't mean to say it's an earth-shattering obstacle. Just one minor
additional nitpick to add to the gigantic pile of problems caused by OOP.

~~~
prewett
The use of "clazz" by Java programmers always seemed like a good solution to
me.

~~~
inimino
Clazzy. Is there a name for this design pattern when the name you wanted was
already taken by a language keyword?

------
uvtc
> Haxe feels much more promising than most alternatives. If I do web stuff
> again I'll be diving in here. There is some good library support. I am a
> little concerned by its relative youth, will it last?

Haxe has actually been around since 2005 or so. I'm really liking Haxe as a
general-purpose compile-to-just-about-anything language.

------
bitwize
> The strongest thing on my desired, but not required list is simplicity. I
> find looking up language features, and quirky 'clever' api's incredibly
> tiring.

Indeed. Clever apis are a disease endemic to the JavaScript community.
expect(me).to.puke.when(i.see.shit.like.this());

> The ideal language would be one I can memorize, and then never have to look
> things up.

You can't memorize C. The idea that you can "hold C in your head" is a myth,
and propaganda repeated by C pushers. In reality, the gotchas surrounding
undefined behavior are so numerous and subtle that you're much better off
using something else, even if it means accepting the complexity that is C++.
(In reality, what you want is a language such as Rust that has well-defined
behavior.)

> I need a platform that I am confident will be around for a while.

Don't get your hopes up. The closest we've had is POSIX, but POSIX is showing
its age, and in the modern era churn is the rule, rather than the exception.
More than a decade old? Rewrite it! New framework came out? Rewrite it!
Developer fell in love with some flashy new technique? Rewrite it! Not enough
nonbinary PoCs on the dev team? Rewrite it!

~~~
aflag
I don't think C is as bad as you're making it out to be. It's a small
language. There are some quirks, but I don't think it's any worse than c++.
You can even easily become a standard zealot (just look any c forum).

Rewriting software sucks. We usually don't like solving the same problem more
than once.

------
KindOne
Needs (2016) added at the end. (Might be older, could not find anything on
archive.org before Jan 2016)

Previous discussion:
[https://news.ycombinator.com/item?id=10870488](https://news.ycombinator.com/item?id=10870488)

------
jokoon
I still believe that C++ is great because you can "use the good parts".

To me the STL containers are good enough. It's always a balance between how
much time you save, and how much performance you lose. C++ is good enough with
this. The only lacking thing would be an object pool.

The author writes

> but so simple it's not too hard to learn to use it carefully.

I would really love a replacement to C++, or an subset of C, that have the
best of both languages. I can't use C because it becomes hairy very quickly,
since you don't have things like STL containers.

I don't like Rust for the exact reason I quoted the author. The learning curve
of Rust is too step, and its syntax if to distant from C. C is great because
it's easy and fast, but it's also lacking many modern features that makes the
programmer's life easier, without stomping down on performance or
customization.

It's true that C++ is very complex, but I still believes it's very very
usable, and the standard is improving a lot.

~~~
skohan
Yeah I agree that there's still room for a "better C". Just C but with a few
modernizations would go a long way.

I think Rust has a different lane: Rust is for when safety is absolutely
important. C is for when you want very little abstraction between you and the
hardware, and you just want it to do what you tell it.

------
big_chungus
I get that this is not a popular opinion these days, but I like C. Quite a
bit, actually. I can get stuff done reasonably quickly, don't have to futz
around with multiple inheritance (C++) or where to call out to C anyway
(python). There is so much more tooling behind C than Rust, or Go, or Zig
(though I've been looking at Zig as a replacement). C isn't as bad as everyone
makes it out to be, for personal stuff. I'd be a little more careful in group
projects, but there's a reason it's still around. You _do_ have to be careful
and do a good design, but you should be doing a design anyway.

Some day, C will be deposed. Something better will show up and gain traction.
On that day, I will happily switch to the new better thing; but for now, C
meets my requirements pretty well.

~~~
non-entity
I didn't even realize C was so unpopular till recently. I kinda figured
industry wise, C++ was much more common, but there seems to be a lot of stuff
still written in C (i.e. many widely used kernels). I can understand was
people would be apprehensive to C, but C++ just seems extremely bloated and I
just haven't been able to get into it.

~~~
krapp
I can see the value in C, especially for API wrappers and such, but it's
entirely possible to just use a minimal set of C++ and avoid bloat. Simple
templates, a vector math library with operator overloads, function overloads
and std::vector alone are most of what you'll probably want to use from C++.
Maybe std::unordered_map as long as you preallocate space for it. It doesn't
have to be hairy.

------
thrower123
I thought it was interesting that he highlighted Haxe as being a young
language. I suppose it is compared to C, but I remember reading some tutorials
about Haxe game development almost a decade and a half ago.

------
jacobush
"when it comes to compilation I can't think of anything faster"

FreePascal! :-D

------
detaro
[2016] it seems

------
e9
Have you tried D? It’s popular in game development world and very pleasant to
work with. [https://dlang.org/](https://dlang.org/)

~~~
zerr
D doesn't seem to be popular in any field, unfortunately.

~~~
qznc
Not popular, but it was definitely used:
[http://dconf.org/2016/talks/watson.html](http://dconf.org/2016/talks/watson.html)

> Can D be used to make games? Yes. Has it been used in a major game release?
> It has now. Remedy Entertainment have successfully shipped the first AAA
> game to use D code. And it’s in a fairly critical subsystem too. This talk
> will cover the usage of D in Quantum Break, problems encountered and solved,
> and where we want to take our usage of D in the future.

------
Mountain_Skies
Wasn't Rollercoaster Tycoon also written in C?

~~~
corysama
It is considered one of the most complex hit games written primarily in
assembly language.
[http://www.chrissawyergames.com/faq3.htm](http://www.chrissawyergames.com/faq3.htm)

------
ltbarcly3
>>I am not an OOP convert. I've spent most of my professional life working
with classes and objects, but the more time I spend, the less I understand why
you'd want to combine code and data so rigidly.

He doesn't want to combine code and data rigidly, so he uses a language
without any concept of generics.

I think he doth protest too much, he can write code however he feels like, and
Im not going to deny his personal opinions, but the justifications and reasons
for those opinions are all just uniformly nonsense.

------
melucasleite
I read your post as poetry. Where can I play your games? You gave me this "The
beguiners guide" (a game about games) feeling.

------
0x0aff374668
Makes some really great points, similar to why C++ is not favored for
embedded. C++ compilers are just too unpredictable and when trying to meet
code limits in 128K, 32K or even 2K of flash, C++ is a nightmare. All of the
major embedded SDKs (STM, Microchip, SiLabs, TI, NXP, I could go on) are
written in C... and C only. MBED is C++, but I've never encountered or heard
of an OEM or integrator using MBED in a real product.

~~~
busrf
Lots of folks use C++ on microcontrollers (including me!). I've definitely
been working closer to the 32 bit processor, >= 128K flash world though.

Certain things are very nice about C++ as compared to C, especially C++11 on:

* type safety and expressiveness (aided by judicious use of templates)

* The constexpr keyword for real compile-time constants (again, type safety)

* RAII (often aided by statically allocated pools of memory from which one can then allocate objects from at runtime)

What kind of issues with code size have you had (and using which compiler)?

~~~
0x0aff374668
Which SDKs do you use that are C++?

I use Keil, IAR, Renesas's compiler, XM8/16/32 from Microchip, and GCC.

Which do you use?

One of the main features of C++, its object-oriented stuff, is built on top of
constructors and destructors -- aka dynamic memory allocation. This sort of
thing is not desirable on deeply embedded systems due to resource constraints
and the very real spectre of heap fragmentation -- embedded stuff tends to
rely on static memory allocation done at compile time or system startup.

C++ isn't quite as portable as C -- Not just the compilers which never quite
supported the same combinations of features -- even if you just stuck with the
ISO-ratified features -- but the language runtime, standard [template]
library, and headers are far larger with various implementations including
plenty of mis-features and quirks. All of this meant that it was quite common
for C++ code to not even compile with a different toolchain -- especially when
templates were used -- and even if it did compile, it might not actually
function correctly.

If you restricted yourself to avoid the problematic areas of C++ (eg not using
templates or dynamic allocations) you'll end up with a language that is
basically C. So why not just use C? :)

~~~
busrf
GCC, and Keil.

As you said, there are no vendor SDKs out there (that I know of, anyways) that
are C++. But it's not terribly hard to integrate them in to a C++ codebase /
roll your own abstractions because you're dissatisfied with the ones the
vendor gives you (;

Different compilers supporting different subsets of the standard is definitely
an issue, and it can be a problem if you're trying to use the same code with
several different toolchains.

GCC in particular however has been really good about keeping up with newer
versions of the C++ standard. I like and use many of the things on this list
(excepting the standard library features, which of course you usually can't
use off the shelf because dynamic memory allocation):
[https://github.com/AnthonyCalandra/modern-cpp-
features](https://github.com/AnthonyCalandra/modern-cpp-features).

In particular, features such as constexpr lambda, if constexpr, and type
traits are nice ways to do some compile-time computation while being type safe
- they're a lot more pleasant to work with than C preprocessor macros.

And of course, templates. They're a nice way to express certain concepts and
reduce duplication without making any impact on your code size. But only
judiciously, otherwise you will wreck your compile times (;

------
avodonosov
How old is this article? It says Haxe is new, but Haxe is a relatively old
tool, first released in 2006.

------
surfsvammel
Isn’t Rust an alternative? I don’t know Rust well enough, but wouldn’t that
fit the bill?

~~~
pornel
It is if you're writing a game from scratch, without need to deeply integrate
with something like Unreal Engine.

I used to write games in plain C, and nowadays I'd definitely use Rust for
them.

There is a small Rust games community and a couple of engines/frameworks:
[https://lib.rs/game-engines](https://lib.rs/game-engines)

One thing with Rust is that it pretty much _requires_ use of entity-component-
system. It's the best practice for real-world games anyway, but people who
write their first game are surprised they can't just "wing it" with some ad-
hoc OOP.

------
CameronBarre
If it works for you and acts as a means to your end, you've won the game!

------
noncoml
Two things that I miss in C are smart pointers and closures.

------
anta40
I wonder why the author didn't mention Pascal. Hmm...

------
ncmncm
If you are coding C++ and it is not super, super FUN, you are Doing It Wrong.

Writing C code is also writing C++ code, but badly.

------
krzat
I hate that C is still a viable choice, it's basically a living fossil.

------
tutfbhuf
Rust!?

------
anta40
What about Pascal, then?

------
mruts
C is one of my favorite languages but you know what be almost perfect for the
author’s needs? OCaml! Almost as fast as C, no mandatory OOP, great
performance, C FFI, nice FP, totally portable and native executables.

Unfortunately OCaml is single-threaded, has a stop the world GC (though if
it’s good enough for Jane Street it’s probably good enough for you), and
sometimes has unfortunate syntax (though ReasonML fixes a lot of this). Also
OCaml doesn’t have the greatest type system in the world. No higher kinder
types and instead we get the parameterized “functor” module system. Which
honestly, kind of sucks. A lot of OCaml is an artifact from the past, if it
could be remade today it would really be something great. Even so, OCaml is a
great language and doesn’t suffer from a lot of the dogma that Haskell
succumbs to.

I’ve always thought of and used OCaml as the functional programming equivalent
of C. No unnecessary bullshit, just straight coding.

~~~
TurboHaskal
While OCaml boasts very low GC latency, I believe F# would be a better choice
for writing games. It is an even more straight to the point OCaml; a Windows
first class citizen; has a concurrent GC and I've heard C# is pretty popular
for writing games due to Unity, so one might be able to leverage that (I don't
know).

A non CLOS heavy SBCL codebase would also be excellent.

------
motles
ctrl+f rust <cr>

------
maxharris
No mention of Rust?

~~~
maxharris
So I dug some more, and it turns out that while he _does_ have something to
say about Rust, I'm not sure I'm convinced by his conclusion about safety:

"Safety. Yes, Rust is more safe. I don’t really care. In light of all of these
problems, I’ll take my segfaults and buffer overflows. I especially refuse to
“rewrite it in Rust” - because no matter what, rewriting an entire program
from scratch is always going to introduce more bugs than maintaining the C
program ever would. I don’t care what language you rewrite it in."

From [https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-
repl...](https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-
replacement.html)

(I think it's a bit mean to downvote me for not knowing that he addressed that
point in a separate page of his. He didn't link to this Rust review at all!)

~~~
detaro
how do you know the article by Drew DeVault represents the opinion of the
author of the article above? Did he link it somewhere?

~~~
maxharris
D'oh! Different person! whoops! So that _really_ underscores my point, then.
The "Why I Write Games in C" guy doesn't mention Rust _anywhere_!

------
crimsonalucard
He didn't talk about Rust. I feel rust bridges the gap, it's just library
support still sucks.

~~~
newnewpdro
> and ideally I'd like to have the option of developing for consoles. So it's
> important that my programming language is portable, and that it has good
> portable library support.

Every console will have a C toolchain out of the box, we can't say the same
for rust.

~~~
crimsonalucard
Hopefully rust will eventually be the new standard.

------
crystalsforme
Didn't even consider Rust...

~~~
gypsyharlot
[https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-
repl...](https://drewdevault.com/2019/03/25/Rust-is-not-a-good-C-
replacement.html)

~~~
Narishma
I don't think that's the same person.

------
Avamander
Yes, I do too love the remote code executions. We really should look at safer
alternatives when making networked games.

------
blacklight86
It's quite curious that he didn't mention Rust as a possible alternative.
Still fast compilation, still no forced OOP bloatware added, plus static
memory management that prevents the issue with garbage collection that happens
in Go.

------
unnouinceput
Quote: "I want to produce less bugs, so I want strict typing, strong warning
messages and static code analysis".

Yeah, at strict typing you lost me buddy. Let's just go with somebody else
reply, as in you like C and that's why you do your hobbies in. Nothing wrong
with that in the end.

~~~
jchw
Implying that typing does not prevent bugs is the strangest programming meme
I’ve ever heard, and its proponents push it so hard that I wonder if there’s
somewhere I can sign up to be paid for it. We rarely are able to directly
compare the cost/benefit of strict typings vs loose typings, but with JS vs TS
you get a pretty direct comparison, and it is absolutely unsurprising that TS
is eating the JS world; it does prevent bugs, it does improve productivity.
Tests do too when done correctly, but both together are better than either
individually.

When you go further, you can see that the core innovation of Rust is basically
adding ownership to the type system and therefore making typing even stronger.
Strong typing is a win for less bugs. Full stop.

~~~
kabes
I'm sure typing prevents some bugs. But if I'm looking at the bugs we find in
the JS projects I work on, only a very few would have been avoided with
typing. The vast majority of bugs are about unsound business logic, wrongly
understood requirements, etc.

~~~
mruts
That’s because the thousands of errors were manually fixed before committing.

I’ve programmed professionally in both Haskell (strongly statically typed) and
Racket (dynamically typed). The number of bugs I encountered at runtime with
Haskell was very close to 0 per 1000 lines of code. For Racket on the other
hand, I would get maybe 5-15 per 1000 lines. Most of these were easy to catch
and never made it into production, so it really only wasted my time instead of
making the app unstable. But there would be a few very stupid bugs that would
have been easily caught with static typing on the unhappy/rare path that
wouldn’t be found until weeks or months later.

Nowadays, I try to avoid all jobs that use languages with dynamic typing on
production software simply for the reason that they make me anxious and upset.
Maybe everyone else is just so much better than me that they never produce
typing relating bugs, but I kinda doubt it.

------
adamnemecek
> "C is dangerous, but it is reliable. A very sharp knife that can cut fingers
> as well as veg, but so simple it's not too hard to learn to use it
> carefully."

Cliche analogy.

------
ulkesh
After I got past the horrible misspellings and atrocious grammar, I could
appreciate what the author is saying. The author is privileged to be able to
concentrate on and enjoy a particular language. That is great, for him.

Most of the rest of us have to learn and use a wide swath of languages,
features, and technologies to stay a leg up in this world. We seldom have the
choice to pick what we work on. Sure, we can change jobs, but inevitably it
requires conforming to someone else’s opinion.

The author makes me want to go back and pick C up again. But like so many
times in the past, I will probably find it needlessly painful and go right
back to the more modern languages which already solved so much for me. I don’t
mind standing on the shoulders of past C developers. I, for one, also do not
mind learning the details of any particular modern language because, if
anything, it makes me more marketable.

