PaulDavisThe1st
8 hours ago
You can take
for (auto const & ess : esses) {
...
}
from my cold dead hands.Also, you can fight me if you want to take
dynamic_cast<Derived> (base_ptr)
and force me to implement my own typing system every time I need to upcast.Basically, stick with C and leave C++ programmers alone. I haven't seen a less useful article about C++ in a long time, and as an HN reader, that's really saying something.
rfgplk
8 hours ago
One thing I've noticed about a lot of these "strict C" developers is that quite often they actually refuse to learn C++. One of the most common complaints of C developers regarding C++ is "it does things behind the scenes/performs magic", often with regards to operator overloading. When they refuse to actually look at the implementation (y'know you can check if an operator has been overloaded) AND they refuse to acknowledge that a huge chunk of "pure C" does HEAPS of magic behind the scenes (that the developer has no idea about) unless they've actually studied the spec in detail. Malloc and memory allocation methods are at least 10k+ lines of code for instance.
fasterik
2 hours ago
I don't think "refusing to learn C++" is the right way to frame it. I want to use the language features that are actually useful to me, without being forced into a specific programming style. I can't speak for every "orthodox C++" programmer, but for me that means using exclusively plain-old-data structs, non-member functions, and "dumb" pointers. I have no issue with learning to use a C++ feature when it's directly useful to a problem I'm trying to solve.
As one example, I recently found templated lambdas useful in making animations.
uecker
3 hours ago
I was programming in C++ before switching to C and I would say that C++ adds a huge amount of mental load compared to C. I think one can understand how much of a relieve it is to not worry about everything C++ does only after not using C++ for a quite a while.
lelanthran
2 hours ago
All the foot guns in C are still in C++, and C++ adds a significant multiple more.
ReactiveJelly
38 minutes ago
The regular table saw is still in the new workshop, and the new workshop adds a SawStop and another regular table saw.
lelele
7 hours ago
> y'know you can check if an operator has been overloaded
And there lies the problem with C++: to be sure, you have to check. C++ code can't be taken at face value -- the most innocuous-looking code could be a ticking bomb.
WalterBright
an hour ago
My favorite was the regex engine that was implemented using C++ operator overloading. The author was very proud of it, but you could not tell what code was regex code and what code was math code.
I went to some lengths with D to discourage such abusive operating overloading practice.
pjmlp
7 hours ago
Just like any C function without looking into the translation unit, don't say you blindly believe on the function name.
lelele
4 hours ago
Not really. C++ is on another level altogether: the code could be calling implicit conversion operators, the compiler could have instantiated some template code in an unforeseen way, and so on.
Years ago, I was really proficient in C++, but after a year of programming in C#, I realized that not once had the behavior of my code caught me off guard. In the following years, I only ran into quirky behavior a couple of times. I could finally program without the constant mental overhead of watching out for C++ pitfalls.
pjmlp
4 hours ago
I suppose you're aware C# also has implicit conversion operators, operator overloading, reflection, aspect oriented programming, compiler plugins, interceptors.
Seems strange to talk down C++ while praising C#, which incidentally has been getting features to increase its use where Microsoft previously might have used C++ instead.
You catch pitfalls in any language the same way, using static analysis, which C authors introduced right in 1979, acknowledging the issues with language, which they decided to outsource to another tool, instead of improving the language.
A long tradition in computing.
WalterBright
30 minutes ago
In D, we've introduced editions so that obsolete, problematic, or redundant features can be removed.
lelele
4 hours ago
Yes, C# is becoming more and more complex, but IMO C++ is still in a class of its own. Just compare how many different, sometimes competing ways there are to initialize variables in C++, each with its own subtleties.
I guess we'll have to agree to disagree here. And of course, even if C++'s user base seems to be shrinking, it still works well for some categories of programmers.
pjmlp
2 hours ago
Even if it is shrinking, people leaving C++ aren't racing to go back to C, rather to languages with similar expressive type systems.
Swift, C#, Kotlin, Scala, Rust, Typescript, Python...
rfgplk
7 hours ago
But isn't this a problem with all code? Looking at a Rust function signature how can you be sure that it does what it says it does? Or python?
dwattttt
an hour ago
To the extent that one doesn't try to suppress compiler warnings & errors / actively break the language, you get what the compiler confirms is true about a function signature.
For Python, it's very little (nothing?). For Rust, you get more than most; lifetimes tell you whether it holds onto a pointer you give it.
lelele
4 hours ago
See my reply to a similar objection: https://news.ycombinator.com/item?id=48520416
LoganDark
7 hours ago
> C++ code can't be taken at face value -- the most innocuous-looking code could be a ticking bomb.
You can't take C code at face value either. The name of a method or type doesn't tell you what it does. It could longjmp for all you know.
lelele
4 hours ago
See my reply to a similar objection: https://news.ycombinator.com/item?id=48520416
01100011
7 hours ago
A lot of us are too busy solving problems. Learning about the latest language features, which we often won't be able to use anyway due to the trouble of moving a large dev environment to a newer standard, feels like academic masturbation.
C++ folks are very much into their language, and can't seem to understand that most folks don't want to dedicate significant amounts of mental resources purely to language details.
bluGill
6 hours ago
Moving to new C++ is a non event, change the compiler / flags and done. Using the new features requires some learning but not a big deal since you can figure out what you need from a summary and learn what is useful for your problem.
The problems of the code I'm writing far exceeds the complexity of the language. Your complaint about complexity fall flat to me, unless you are working on a trivial program you need to deal with things far more complex than any language.
pjmlp
7 hours ago
Like implementing the compilers used by C devs.
uecker
3 hours ago
GCC was implemented in C and there are plenty of other C compilers written in C. GCC has been converted to C++ at some point, but large parts are still essentially C and I do not think the change to C++ was actually helpful (but others may disagree). In any case, the idea that one needs C++ to have C compilers is certainly simply wrong.
WalterBright
7 minutes ago
My original C compiler was implemented in C and then bootstrapped. Zortech C++ was implemented in Zortech C, and then bootstrapped.
D was implemented in "C with Classes" and then translated to D and bootstrapped. There isn't a line of C code in it.
Over time, we gradually replace the C-isms and C++isms and anachronisms in it to native D-isms.
delta_p_delta_x
3 hours ago
Clang/LLVM with its more permissive licence sees _much_ wider use and is written purely in C++. PlayStation, automotive, platforms and runtimes like Chromium/V8, Android, etc. are all built with Clang.
lelanthran
2 hours ago
Clang usage is a fraction of GCC usage.
Regards, an embedded dev.
uecker
2 hours ago
How is this relevant? (also wider use for C I would doubt)
pjmlp
2 hours ago
Naturally GCC was originally written in C, given its age, and the original GNU coding standards document.
With time, the GCC developers acknowledged the benefits of using C++ over C, and migrated the code.
GCC requires a C++ compiler to bootstrap since around 2012, and GNU coding standards has been updated to several languages beyond C, time to go up with times.
uecker
2 hours ago
I think the fallacy of this argument is obvious.
nuc1e0n
2 hours ago
c memory allocation functions can be implemented in 1000 lines or so. I've done it myself. Maybe more are needed to handle strange operating systems or architectures, as glib does.
jstimpfle
7 hours ago
Trust me, I know more C++ than most or all of my peers (working two jobs simultaneously), and I know a million ways that C++ features suck. Also standard library and containers. If you want I'll point out the ways in which std::deque, and even std::map, std::unordered_map, even std::vector (!) suck. IMO, just don't do it.
stinos
4 hours ago
and even std::map, std::unordered_map, even std::vector (!) suck
It's really hard to take your comment serious because of generalization like this. Maybe they're not usable for your particular usecase but that doesn't mean they suck. Just like there's a 'million' ways that C++ sucks in your book, there's a reason there's millions of lines of code out there where these containers are valid usecases and hence work without issues whatsoever nor a need to replace them with something else.
jcranmer
3 hours ago
std::map and std::unordered_map are just unbelievably shitty implementations. The former is a red-black tree, which in my entire programming career I have needed to reach for like... twice? It's just not the right container for almost any problem you have, yet it's the one that gets the short, sweet name. The latter is a bucket-based hashmap, which is about the worst kind of hashmap that can be built. On top of that, their APIs are also really annoying to use compared to, say, Python or Rust's implementation. At least C++20 finally added a simple contains method, but something like setdefault is just a chore to get implemented.
WalterBright
a minute ago
Pretty much the only collections I use are:
1. arrays
2. linked lists
3. hash tables
4. simple binary trees
jstimpfle
4 hours ago
They're not useable for anything serious, i.e. high throughput, low frequency, massively concurrent work. In other words, most of the things for which you shouldn't better have chosen a different language in the first place.
They're also unusable by the way because of ergonomic and software architecture factors, such as bad modularity, terrible compile times, unreadable error messages, unreadable symbol names...
Yes that is overgeneralizing a little bit but it's largely true.
The problem is typically not the containers themselves but all the other bad decisions that they push you to make in order to work around their "small issues".
The huge problem is that these containers can get you started quickly, i.e. leetcode type stuff and single threaded stuff, but at some point you'll realize your architecture ended up completely in the wrong place because of that.
If you haven't been thinking deeply about memory management and concurrency, you won't be able to understand, no offense meant. I've just fixed another subsystem that was completely overwhelmed, seeing 8x bandwidth gains already on a small testsystem, but the factor is basically unbounded when moving to bigger systems, when it's about contended vs uncontended.
delta_p_delta_x
3 hours ago
> anything serious, i.e. high throughput, low frequency, massively concurrent work
Why is only 'high throughput, low frequency, massively concurrent work' considered 'serious'?
Pannoniae
an hour ago
They're just clearly inferior in pretty much any situation.
The map stuff the other posters summed up well but even std::vector is dogshit with pretty much all implementations having inlined grow code in push_back, a not too great API and missed optimisations e.g. no trivial relocation when growing the vector / moving it and no useful APIs such as "grow but don't initialise"...
jstimpfle
an hour ago
To be fair grow-but-don't-initialize is a pretty fundamental part of the API, the reserve() method.
But already the basic premise that you should push back without thinking is wrong. You will suffer reallocations and invalisations when you least expected them, and frankly you have to architect around that fact which is a terrible restriction. You can work around by pre reserving but at that point it's just a basic fixed heap allocated array but worse because the type gives you a weird look all the time, "I'll realloc as soon as you don't pay attention, harhar"!
jstimpfle
2 hours ago
You are free to make your own definition, what are your suggestions?
(Obviously I meant to say low latency, not low frequency)
miroljub
an hour ago
Because if you don't need any of these, any slop implementation will do.
rfgplk
7 hours ago
The standard library implements really do suck (in some cases), but this should be separated from C++ (the language). Even the standard splits the language grammar from the standard library cleanly.
wavemode
6 hours ago
You can't really separate the two, firstly because some parts of the standard library interact directly with the language's syntax (e.g. <initializer_list>), and secondly because the language standard dictates things about the behavior of the standard library that limit implementation options.
For example, the standard says that adding elements to an <unordered_map> is not allowed to invalidate references to keys or elements within the map. That makes it impossible for any standards-compliant C++ implementation to use a high-performance implementation in which keys and elements are stored contiguously in a flat array.
nly
5 hours ago
Your map example only concerns the standard library, not the language.
wavemode
4 hours ago
Its behavior is dictated by the language.
The context of this thread is that someone stated that the C++ standard library sucks, and someone replied to them saying that it's just some implementations that suck, but that's separate from the language. The point I'm trying to make, in response, is that it is about the language. It's not just "some" implementations - there is no implementation of the C++ standard library that doesn't have these inefficiencies, because the language's own standard requires them.
(This is tangential but - this is why I often say that C++ is not actually the most complex language in the world, it's just over-specified. If you took almost any popular programming language and wrote a document dictating the behavior of every single feature and library to the same level of detail, you would end up with a document similar in length or even longer than the C++ standard.)
jstimpfle
2 hours ago
In my reading, they didn't say it's due to bad implementations, though. They were trying to separate the standard into two parts, the one about the language syntax and semantics, and the one about the standard library. And I think this is a fair separation actually. But that doesn't make the core language any better ;-)
gpderetta
6 hours ago
Which sucks... unless you really need reference stability.
jstimpfle
4 hours ago
std::vector has left the chat.
EPWN3D
5 hours ago
C programmers aren't complaining about the "magic" being tens of thousands of lines of code. They're complaining about the magic including bizarre side effects that brazenly violate the principle of least astonishment.
In C++, you can overload the comma operator to do shit. I've seen it done. There's no reason to do it, and no reasonable person would ever expect it in a code base they're unfamiliar with. To find bug in that ultimately roots back to that implementation, you have to go eliminate every other whack-job possibility before it even occurs to you that maybe the weirdo who wrote this code chose to overload the comma operator.
I'm not going to argue with anyone who wants to use C++ in their own projects, you do you. But let's be real about what C programmers are complaining about. It's not line count. It's syntactic obfuscation. I don't just level this criticism at C++ either. Basically every major new language has its own byzantine syntactic constructs to some degree.
flohofwoe
5 hours ago
> Malloc and memory allocation methods are at least 10k+ lines of code for instance.
Only the really big ones, e.g. here is Emscripten's allocator that focuses on small binary size and is implemented in about 1.5 kloc (ignoring comments and whitespace it's actually under 1 kloc), and that allocator is perfectly fine for most use cases (especially C code bases which typically don't have a high allocation frequency):
https://github.com/emscripten-core/emscripten/blob/main/syst...
...and Seb Aaltonen's offset allocator (used for allocating GPU buffers in his Vulkan API wrapper) has under 500 lines of code:
https://github.com/sebbbi/OffsetAllocator/blob/main/offsetAl...
Right tool for the job etc... big general-purpose allocators like jemalloc or mimalloc are usually a bandaid to somewhat salvage a failed memory management strategy.
IncreasePosts
7 hours ago
If we accept the maximum that "any sufficiently advanced technology is indistinguishable from magic", then c++ is indeed magic. It's so advanced that one of the worlds foremost experts in the language(herb sutter) has determined that the language is too complex and we need a whole new language(confront) which is simpler and can be converted to c++.
rfgplk
7 hours ago
C++ is actually obscenely complex, I don't deny that. Just mastering object lifetime rules is crazy difficult due to all the edge cases, but it comes with the territory.
tialaramex
6 hours ago
> it comes with the territory
In the sense that C++ is complicated because it's C++, which is complicated? That's just a tautology. If you mean "the territory" in some other sense there's no reason to believe this.
flohofwoe
6 hours ago
> for (auto const & ess : esses) {
This is allowed by Orthodox C++
> dynamic_cast<Derived> (base_ptr)
This isn't because it requires RTTI, but dynamic_cast is also a typical code smell.
Orthodox C++ isn't generally against new C++ features, it only advices to wait about 5 years (or at least one C++ version) for stabilization and to apply some common sense before adopting them.
The notes about not using RTTI, exceptions and stdlib features that allocate under the hood are all justified by painful experience with those things in the context of game development.
In general, the restrictions outlined in the post make a lot of sense when considering that Branimir (of BGFX fame: https://github.com/bkaradzic/bgfx) is coming out of the game dev hemisphere, and from that PoV none of the restrictions are controversial - on the contrary, it would be highly controversial to suggest going all in on Modern C++ features ;)
PaulDavisThe1st
4 hours ago
> This is allowed by Orthodox C++
I can see no rationale for this whatsoever. It is nothing but syntactic sugar.
> Branmir (of BGFX fame
Appeals to authority don't really work for me.
I've been writing a cross-platform DAW (0) for 25+ years, in C++, and what a game dev has to say about the language in their own work might be of passing interest but not much more.
Being aware of the pitfalls of particular features of a language is an important task for anyone programming in that language. But that doesn't mean that the language is fundamentally broken or that programmers cannot make their own choices about which features to use.
(0) on at least the same level of complexity as a modern game
forrestthewoods
2 hours ago
Your level of vitriol and anger at someone expressing an opinion is really weird.
Literally everyone who uses C++ decides which features to use/embrace and which to avoid. Someone sharing their particular preference is pretty normal and fine.
> Appeals to authority don't really work for me. I've been writing a cross-platform DAW (0) for 25+ years, in C++
I love how you reject appeal to authority and then try to establish yourself as an authority. That’s cute.
PaulDavisThe1st
42 minutes ago
Which wording was vitriolic and angry?
I wasn't seeking to establish my own authority in any way: "X is brilliant, we should listen to them" being countered by "there are lots of people with similar levels of experience with this thing who have many different opinions (I happen to be one of them)" isn't an appeal to authority. But sure, I could have left out the ("I happen to be one of them") part without changing my point much.
TFA is not about someone sharing their preferences. It's a direct call to not use many features, and claiming that to do otherwise is a mistake. Here's an example of sharing preferences:
"I've often tried to use C++'s variadic function templates, but I've found that just using initializer lists tends to be simpler and more readable".
Here's an example of how TFA would put that:
"do NOT use variadic function templates"
forrestthewoods
20 minutes ago
> Which wording was vitriolic and angry?
“I haven't seen a less useful article about C++ in a long time, and as an HN reader, that's really saying something.”
The tone of all your comments reads as oddly intense imho. Perhaps not your intent. Opinions may vary.
> TFA is not about someone sharing their preferences. It's a direct call to not use many features, and claiming that to do otherwise is a mistake
Yeah that’s fine. The whole piece is an opinion piece on what someone thinks is a good approach to C++ development. There is no value in hedging every single bullet point with a bunch of flourish imho.
In game dev C++ circles nothing in this list is particularly controversial. It’s just writing down what many/most devs already did.
badsectoracula
3 hours ago
> for (auto const & ess : esses) {
The problem with this is that whoever is reading the code as-is does not know what type "ess" is. Sometimes you get the definition somewhere nearby, in which case it is probably fine - assuming it is close enough that it'll be included in a diff - but more often than not you don't know.
Yes, an IDE can probably tell you (probably, depends on the IDE and assuming everyone uses one) but even that requires some extra action like moving the mouse over the definition and hoping it'll give you something. However this wont show up in diffs, PRs, code reviews, etc.
IMO `auto` is one of those C++ features that really needs discipline to use - and when in doubt, i'd rather ban its use (except where you cannot do otherwise) than rely on everyone doing the right thing.
PaulDavisThe1st
an hour ago
It doesn't matter what the type is, that's the whole point!
Moreover, what's even more beautiful? You can change the type of things in the container "esses", and the code doesn't need to change.
If you have the experience, this construct tells you everything you need to know: it's an iteration over a container, visiting every element in order, without copying it, and without modifying it.
You don't need to know any more.
wk_end
2 hours ago
How important is that, the ability to be certain of the type of some iterated value from a container from one line in total isolation? The odds are very good that it's clear from the context, which is why the compiler can infer it easily enough too. And then the consequence of not inferring correctly would be...the code doesn't compile?
Of course some discipline is required - as with just about everything in programming, especially in C++ - but developers in just about every other statically-typed language lean on type inference (including far more extensive type inference) and don't wring their hands about it. It's hard not to see this as a case of Blub - if you learned about typing with `Foo foo = new Foo()`, anything different might seem scary.
...anyway, in this case the real win probably is the range-based for loop, rather than the auto. `for (const Foo& foo : foos)` isn't so bad, but `for (std::vector<Foo>::const_iterator it = foos.begin(); it != foos.end(); ++it)` is pretty rough.
badsectoracula
2 hours ago
> How important is that, the ability to be certain of the type of some iterated value from a container from one line in total isolation?
As important as the code to be readable.
> The odds are very good that it's clear from the context
As i wrote, if the actual type can be seen somewhere nearby (close enough to be included in diffs) then that's fine - it is already in the context. Though that is not usually the case and i personally had to work with code with which i was not familiar and which used `auto` all over the place and had to go hunting for the actual container declaration to see what it is (Visual Studio was not helpful in its tooltips).
So my actual experience is that i'd rather see the actual type.
However...
> anyway, in this case the real win probably is the range-based for loop
...yes, the range-based for loop is often the better choice when it comes to readability. And when compared with the iterators, it is pretty much always more readable than them :-P. `for (const Foo& foo : foos)` is basically what i'd prefer to see. It is the use of `auto` i pointed out.
cyber_kinetist
7 hours ago
LLVM uses a hand-rolled version of RTTI for the best performance (the parent constructor accepts its runtime type as an enum), and it seemed that the maintenance costs for it aren't that high. (See https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html)
Or if you're even lazier, you can easily make a more automatic RTTI system with some templates / macros that works much faster than dynamic_cast! (See https://github.com/royvandam/rtti)
my-next-account
7 hours ago
Implementing STL iterators are a bloody PITA
flohofwoe
6 hours ago
You don't need iterators for the range-based for loop, just a .begin() and .end() method which returns a raw pointer is enough.
E.g.:
https://github.com/floooh/oryol/blob/eb08cffe1b1cb6b05ed14ec...
(don't use that project though because it's been archived, I've switched back to plain old C in the meantime)