I don't use orthodox C++, but the author of this is also the author of bgfx, which is a very popular graphics api abstraction. It runs on (and has commercial products on) Android, ios, Playstation, Xbox, PC, Mac, Linux, and wasm. While the coding style might be unpopular, it has successful projects.
Ah, this article makes a lot more sense - if you're running across that many different platforms and compilers, then you've been burned many times by modern C++. I don't really agree with the whole "As C-like as possible" approach, but this statement is something I strongly believe in:
> General guideline is: if current year is C++year+5 then it's safe to start selectively using C++year's features.
As a younger programmer, I used to listen to people hating about everything C++ does, offers, or wants to be. Specifically https://yosefk.com/c++fqa/ [1]. Then I tasted the RAII (probably up there with Dynamic Programming for "horribly named but super powerful paradigms") koolaid, and threw my caution to the wind. It's been a pleasant ride since, even if I've been living in a somewhat comfy C++ environment with few third-party integration woes and a reasonable compiler upgrade pace.
Sure, I agree, you should carefully evaluate which language best serves your needs, and what tools in a language you should use or stay away from. But some game dev ranting about which C++ features they consider useless (without even articulating their background or constraints) is worthless imo. Especially if this is the credit they give to their name:
> After over 20 years working with C/C++ I finally got clear idea how header files need to be organized.
[1] Yes, that FAQ parody is now very outdated, seeing as it precedes even C++11. I can believe that it was on-point at the time of writing, but C++ grew up since then.
C++ indeed has many gems like RAII, and many turds. My hope is that one of the last unavoidable turds (mandatory header files) gets replaced by modules but that seems eternally on the horizon.
Frankly I haven't used C++ since LLM copilots dropped, so that would address a lot of the tedium with managing headers in rapid greenfield development.
I have very mixed thoughts about headers, there is something I kind of love about being able to document the public declarations in one slim text file; and put the definitions elsewhere. There are lots of ways to do that, but other languages don't seem to bother. There are automated solutions, to pull these out, but I rarely see, for example, a python file will often just be a series of functions with no way to tell.
But they have a ton of warts, and require duplicate maintenance.
Python does enable this, actually. __init__.py can act as a "header" file that lets you define your porcelain interface separately from internal interfaces.
When I need to refactor a large C codebase the first thing I do is recompile it with C++ - and then fix the missing prototypes and other grit that gets in the way. The cost to do this is minimal but I then have more options (via macros, automatic assignment, etc) to start shoring it up like a retaining wall that's about to fail. At the end of the day, a POD C++ structure with const inline accessors is way easier to reason about and costs nothing once compiled. It probably helps that I started with CFront, so C++ is just C (and C is just assembler with macros.)
Except that in C (at least prior to C23), it should be "int main(void)".
I'm fairly sure that all existing C compilers will quietly accept "int main()", but there's an argument that "int main()" causes the program to have undefined behavior. "int main(void)" is the form documented in the standard.
And the "return 0;" is unnecessary both in C++ and in all versions of C starting with C99. (Some will argue that it's better style to be explicit.)
Meh, the program execution language about main() has included from the beginning an escape hatch: it allows main to be defined “in some other implementation-defined manner.” “int main(void)” is one of four forms documented in the standard (or one of two specific shapes of code; the other two are “or equivalent” and “or in some other implementation-defined manner.”
3 out of 3 people replying haven't been able to differentiate C+ (orthodox C++) from Bjarne Stroustrup's C++. I posted the hello world code to show it's a poor example as it displays similitude between C+ and C.
One of the powers of C++ really is that it just gives you a vast toolset of mostly zero-cost abstractions. You can be the type of person that uses almost all of it. You can be like the author and decide to ignore most of it. You can be like most people and be somewhere in the middle and use some modern stuff and not other stuff, or whatever you're comfortable with.
People see some feature X of C++ that they don't like the design or performance of, but .. just don't use it if you don't like it, it's great. People see this setup as complexity, and in a way it is; they're not sure when to use tool X over tool Y over tool Z, what's "modern" and what's "old", but once you're experienced that's what's great about C++. Program with it however you want, for whatever purpose you want, using whichever piece you want. Ignore the stuff you don't like.
"don't use the STL if it allocates memory" is pretty aggressive change, especially compared to "some templates are okay"
Gimme std::vector/map/unordered_map/list. The performance cost is pretty miniscule to not have to worry about which dependency is better. The debuggabilty of those classes is kinda nasty tho.
> The debuggabilty of those classes is kinda nasty tho.
It’s not too bad as long as you have the gdb pretty-printers installed, and explicitly instantiate each of the member functions for each of the template specializations that you use, and link against a debug libstdc++ with bounds checking enabled, and… okay fair point.
Measured how? Being good enough or not for which of a million uses? Without that these statements may well be true for the author but meaningless for the reader & potential user.
(Avoiding allocations is frequently a reasonable idea when minimizing latency, for example - maybe not for you).
I'm a game developer who writes games that run with rollback netcode even on android web browsers, so yeah, performance critical code. There's only one data structure in any of our games that shows memory operations in the profiler at all, the one that holds the game states on the clients. This is quite a bit of data and is operated on frequently. We use a robin hood flat map for that. For all the other stuff we basically use std::vector and it never shows up in the profiler.
I think it's wild how people insinuate I'm a fool if I literally say it never is a problem for us. I think it creates a "boy who cried wolf" problem if you always harp on stuff that doesn't actually manifest.
In general, I'm with you on memory allocation. I recently rewrote our animation system to not allocate memory at runtime, but to say "never use std::vector because it's performance is bad" is a statement I can't get behind.
Using <algorithm> makes your code faster, simpler, and safer. Using <format> makes your code faster, simpler, and safer.
I’m so sick of people beating up on C++. The C++ community is in a moral panic to explain why it’s not as safe as rust and coming up with a ten step plan to fix all of the things wrong with it, but the reality is that it’s users like this that give it a bad rep. The same program in idiomatic modern C++ is on average slightly faster than C, significantly safer than C, and usually shorter than C. C++ does not need to apologize for its sins. C++ exceptions are not the end of the world. Templates since C++20 with concepts are ergonomically nicer to use than Java generics (and using them generously can make your code safer), and above all else, it’s a universal language that’s worth investing time in. Mastering C++, or at least attempting to in earnest, will make you a better programmer no matter what language you use for work.
/rant from a C developer that misses C++ every day
What I had read somewhere is that some of the features of C++ are slow and will require runtime support with extra functions, but some features of C++ are as fast and efficient as C so is not a problem.
I dislike many of the features of C++ and would do them differently, although some things do help. For example, I think that they should not have allowed to overload = and , operators, and I think that features of GNU C such as zero-length arrays and zero-length structures are helpful even though apparently in C++ a structure cannot really have zero length (but in C it can), but my opinion is I think it should be possible for your own structure or union type to overload any operators that are not already defined, including reading through and writing through a pointer (if you define the structure type as having a specific type that it points to; by default it does not point to anything). For example:
struct X x;
struct X y;
(...)
*x=*y; // This line can be overloaded assignment and dereferencing
x=y; // This line cannot be overloaded assignment
x+y; // This line can be overloaded addition, since addition on structure types is not normally defined
> I think that they should not have allowed to overload = and , operators
Comma operator I would agree is really only used for shenanigans. But operator= is as crucial as the copy/move constructors for ownership, so I'm not sure how you picture this...
> apparently in C++ a structure cannot really have zero length
> I think it should be possible for your own structure or union type to overload any operators that are not already defined
I see that you are trying to solve the problem of "when reading this code, I don't know if these are the built-in operators or custom ones". I would be interested in learning about situations in which this was a problem for you, because I have not made that experience myself. From my perspective, you need to know the types of x and y to know in the first place which operations are possible - default or custom - so this rule does not really buy you much. But again, I don't think I understand your problem well enough.
FWIW, you can define a custom operator-> and operator* (they have to return pointers, or types that themselves have operator->/operator* which are then applied recursively). I am not convinced that mixing pointer-related semantics into e.g. object assignment is a sufficiently elegant solution from a conceptual perspective though.
A structure with no members can be made to have zero length as a member of something else but not as part of its own definition. I.e., the `[[no_unique_address]]` attribute is a property of the member, not of the struct on its own. It's somewhat annoying that you can't flag an empty struct as being really empty, and have no-unique-address-ness applied everywhere it's used automatically; you have to flag every use with the attribute manually.
None of these are realistic, if you have to use third_party software, because that one may require them, and then all bets are off.
The only way to enforce this, if you are in full control of the source code you are in - for example the Game Engine / Runtime may fall into this, but it's impossible, or rather impractical to think you can do for the rest of the Game Tools / Plugins / etc.
So with this comes conundrum - folks working predominantly on the Game Engine side tend to push this "orthodox" style as the one that should be accepted, while the folks working on the "tools" style simply can't accept it, because otherwise we have to reinvent tons of code, or not being able to link to closed-source third_party libs in order to make a tool.
Orthodox, huh? Looks more heterodox than just about anything I've seen. "Scientology C++" would be more descriptive.
Just to shame one out of this bundle of dumb ideas, the motivations behind avoiding exceptions range from "ugh" to "there's extra code injected in there that might be slow or something." This of course kills RAII patterns and a majority of the advantages of using C++.
IMO, the author should develop a new language and name it C+-, preferably using C preprocessor macros instead of a separate parser.
This guideline is basically banning the use of std::string.
Having a string abstraction beyond C null-terminated string is a huge deal both for convenience and for safety. It is super easy to get strings wrong in C.
This is also one of the reasons why printf won't work. You cannot pass a type like std::string to a variable args function.
I can understand banning it. It’s important to understand std::string is more of a string builder implementation, which is rarely what you want, but people treat it as the default way to handle strings in C++.
Too often I have to fix code that makes liberal use of std::string, making copies and allocations everywhere for no apparent reason, when string_view or better const char* would do.
I have to use std::string_view with %.*s as a string formatter every day. It is an incredible pain. Of course the solution is getting rid of printf style formatting, not string_view.
The hardest part of passing `std::string` to `printf` was getting the macro to convince the pre-standard-C++11 compiler that it was `constexpr` enough so that `-Werror=format` would work.
I found it was easiest to sometimes allocate a temporary, if the input wasn't known to be NUL-terminated.
Well, wrap it up in something more ergonomic, but it works. I'm usually putting strings into some sort of I/O API (like a logger) instead of hardcoding copies to stdout everywhere.
The best parts of C++ are generics and RAII, neither of which have anything to do with OOP and both of which Rust wisely stole (well, only the latter is really unique to C++).
Sounds like a legendary person. Do you think he would have used a computer if it was purely mechanical and driven by 1,000 hamsters? (serious question)
https://github.com/bkaradzic/bgfx