I don't think you're addressing the concerns which made him choose C. In his case: debugging is complex, compilation is slow, name mangling is unreliable, global state is abound.
He makes no mention of inline assembler, nor does he encourage premature optimization.
He talks instead about a concrete problem he had: the typical performance of operations was not good enough, thus no single optimization would help much. He also mentions that the abstractions encouraged by C++ do not help you build data-oriented applications (e.g. games); using them properly actually hurts performance.
meh - imo, even if C++ is a ridiculously bloated beast, it has some things that are indispensable for game programming that C doesn't. vector math without operator overloading is gross. being able to have generic containers with templates is really useful, even if templates are gross. encapsulating functionality in classes is always useful, and you can get a lot of use out of inheritance without overusing it.
these things are really codebase readability and maintainability concerns, and to me they're orthogonal to "data-oriented programming", things like optimizing for cache locality, SoA instead of AoS, not calling virtual methods in warm or hot paths, throwing away malloc and using purpose-built custom allocators, SIMD/AVXify all the things, etc. etc.
it's a lot harder to handle the insane, byzantine complexity of a game engine in something as pared-down as C.
with that said, i don't like C++ and i definitely don't think it's a beautiful language by any means.
jonathan blow's new language, jai, is very interesting. purpose-built for game programming. check it out if you don't know about it yet. it's looking really promising for all types of high-performance, game-like projects.
> vector math without operator overloading is gross.
Many C compilers can use normal infix operators with SIMD vectors and it will have better performance than C++ operator overloading (especially in debug builds). All you need is a typedef.
typedef float vec4f __attribute__ ((vector_size (16)));
vec4f a = { 1, 2, 3, 4 }, b = { 5, 6, 7, 8 };
vec4f c = a * (a + b);
The typedef is slightly different for Clang, but that's just one line. This code should work with GCC, Clang and the Intel C compiler. MSVC doesn't do this, but I don't write code for MSVC any more because I can compile compatible object files with Clang.
In my experiments, I've noticed that you get the best performance by passing vectors and matrices by value, not by pointer or reference. It also makes the API nice, because you can return matrix values, e.g. `mat4x4f mvp = matrix_product(projection, matrix_product(model, view))`. In some nasty cases you might need to add force_inline attribute, but most of the time the compiler will inline the functions anyway.
In C++ with operator overloading it's easy to do stupid things like in-place addition (e.g. operator+= for a vec4f) or start transposing matrices in place. This will make the compiler emit memory load/store instructions when you'd want to have these values in registers.
> being able to have generic containers with templates
Generic containers are somewhat of an issue with C, but most of the std::containers are not suitable for some game development tasks (this is why projects like eastl exists). I prefer using intrusive containers in C, similar to how the Linux kernel deals with linked lists and red-black trees. Even the C++ standard library (the GNU one) is internally implemented this way, the "generic container" is just a thin type safety shim on top to avoid template bloat.
> In C++ with operator overloading it's easy to do stupid things like in-place addition (e.g. operator+= for a vec4f) or start transposing matrices in place. This will make the compiler emit memory load/store instructions when you'd want to have these values in registers.
That sounds like a failure of inlining. If you're at the point where a single stack spill makes a difference, you won't want to pay the cost of the calling convention spills either. And if you are inlining, then SROA and mem2reg will easily remove those load/store instructions. Modern compiler optimizations make what you describe not a problem anymore.
> Modern compiler optimizations make what you describe not a problem anymore.
Yes, it should get inlined, but I've seen this fail in a recent-ish GCC (4.6 to 4.8 or so). And there's still the issue of debug builds being slower even if the compiler works perfectly in optimized builds.
Operator overloading will only work with SIMD if you write your vector class using intrinsics or SIMD extensions anyway. If you're doing scalar loads and stores, you can't rely on getting SIMD instructions in the output.
> Yes, it should get inlined, but I've seen this fail in a recent-ish GCC (4.6 to 4.8 or so)
Seems like a pretty bad GCC bug then, one that should be fixed upstream.
I really dislike it when code avoids functions because of fear that they won't be inlined (or to try to work around compiler bugs to that effect), because doing this dramatically reduces code maintainability and safety in exchange for very little benefit, given the inline hint keyword and __attribute__((always_inline)).
GCC's inlining has been a bit brittle, especially when dealing with vector arguments, and also depending on the ABI (4x double vectors without AVX, etc). It's much better in GCC 5.x now.
I generally use always_inline for vector arithmetic functions, just to be sure. You never want to have a function call to do just a few SIMD instructions.
Incidentally, a tip: C11 has basic generic/overloading support based on macros. So even if you can't get "a + b * c + d", you can at least get "add(a, mul(b, c), d)". The implementation is ugly, but if you only need it for one thing, it doesn't really matter.
1.) the comment about ASM was simply to illustrate that I doubt many highly competent coders would ever advocate doing a large project completely in C. bad example maybe, but that was my only point.
2.) complaints about debugging are invalid, sorry. we have amazing tools available these days and there's no excuse to suck at debugging.
3.) compilation is slow, he's right about that. #pragma hdrstop can help a bit there
4.) name mangling is a bitch, you can get around it by defining your exports, etc.
5.) everything else is just excuses for bad C++ code. it doesn't matter what the latest C++ style encourages you to do, what matters is writing code that works for your application.
I actually think it's awesome he's going the C route, it's just that he could probably do his project in C++ two times over in the same amount of time and I think most of his reasoning for choosing C is crap.
Didn't mean to come off like a dick so much, I wish him well, and I gotta admit alloca() is awesome
He makes no mention of inline assembler, nor does he encourage premature optimization. He talks instead about a concrete problem he had: the typical performance of operations was not good enough, thus no single optimization would help much. He also mentions that the abstractions encouraged by C++ do not help you build data-oriented applications (e.g. games); using them properly actually hurts performance.