Kudos to that author for the great, eye catching title and the in depth detail!
Kudos to that author for the great, eye catching title and the in depth detail!
So horrifyingly true.
What's your favorite "you forgot X"? You should definitely make an updated v2 because every link I've opened from the bottom half has been absolutely bonkers.
Three dimensional analog literals drawn using ASCII? What the flying hell was the standards committee thinking.
else while
is perfectly fine, not only in c++.Well, I do appreciate your work, and the information is certainly helpful.
But it's a bit like a urologist explaining what it will be like when you pass a kidney stone.
And then find out that the C++ standards committee is working on a new kidney-stone shape that's backwards compatible, but adds more jagged spikes.
1: https://en.wikipedia.org/wiki/I_Have_No_Mouth,_and_I_Must_Sc...
CW though, it is pretty grim. Very early example of the "AI takes over the world, decides humans are redundant" trope though. (Maybe the first?)
You have "C with classes" that coexist with the "modern" way, full of smart pointers and functional programming. It is popular in embedded systems, video games, servers, and GUIs (mostly Qt). And if you look at the code, it is as if it was a different language, because the requirements are all very different. Embedded system need low level hardware access, video games are all about performance, servers want safety, and GUIs want flexibility.
There are less awful alternative to C++. For example C on one end of the spectrum and Rust on the other end. But none of them cover every C++ use case.
Well, it does unstructured imperative, structured imperative, and OOP imperative!
Except if you count template programming, because that one is pure functional, but only runs at compile time.
- There’s at least an ugly library to do it in C++
- There might be support baked directly into the language
- Or you could do it in Lisp, but that would be too easy
Here's an article on the topic: https://cryptoservices.github.io/fde/2018/11/30/undefined-be...
Most compilers will do something like "treat the bits like they represent a float, even though they mean something else when they're treated as an int."
But the language spec says the compiler is allowed to send an email to Bjarne Stroustrup with your home address so he can come over and personally set your computer on fire.
More seriously, the typical response to undefined behavior is for the compiler to optimize out whatever code may trigger it. It is great for performance and one of the reasons C and C++ often top the charts, but it may lead to weird bugs that depend on the optimization level.
For the union case, the compiler could completely remove the code that reads the float: because it is UB, it is something you shouldn't do, so it considers that you will never do it, so it can just remove the code. In the end your conversion function may do nothing at all (very optimized!). In practice, because it is so common, even though it is UB, it will usually do what you want it to do.
Literal lol... this is not an argument in favor of C++.
Actually, strike that: I'm not sure if it's true or not (though I suspect it is), but it doesn't actually matter. What I'm really getting at here is that there is nothing in Rust that behaves so confusingly or ambiguously as what's described in this article. If you're writing Rust, you'll never have to remember these sorts of rules and how they are going to be applied to your code.
I do agree that reading someone else's Rust can be a challenge, if they're using Rust's type system to its fullest, and you're (quite reasonably and understandably) not up to speed on the entirety of it. And that is a problem, agreed; though, at least, fortunately it's not a problem of ambiguity, but more of a learning-curve issue.
But I have never been writing Rust and have been confused about what the code I'm writing might do, never had to consult and puzzle out some obscure documentation in order to ensure that the code I was writing was going to do what I expected it to do. C++ falls so incredibly flat in this department, and that's why I avoid using it like the plague.
The fact that you can do almost anything IS pretty cool, but without having at least one C++ wizard at hand it can drive you nuts.
If I was starting a new work project with a lot of junior team members, or if I was doing a web project, or a very simple script, fine I’ll use a different language. There can definitely be good reasons not to use C++. But I’m at the point in my expertise that I will default to C++ otherwise. I’m most productive where I am most familiar.
With all due respect to your expertise, the whole idea of a footgun is that it tends to go off accidentally. The more footguns a language contains, the more likely you are of accidentally firing one.
Is a footgun a gun that only aims at feet? Because that seems like a dumb thing to invent in the first place. Or is it a gun that happens a to be aiming at feet? That seems like something that could only exist by user error.
I think “enough rope to hang yourself” is a more accurate description of almost every programming languages, since rope is at least intended to be useful (although it is a bit more morbid of an analogy).
This is a footgun - the way to avoid the holster firing is to simply not wear sunglasses, or keep something in your left pocket, and then it would never fire. But the problem is that both of those things are extremely common (for good reason). It’s a poorly thought out feature because it has severe consequences (potentially shooting your foot) for extremely common situations (wearing sunglasses and using your left pocket).
It is possible that the entire field of programming is full of ridiculous people. But it seems more likely that C++ is like a gun with no safety, or something along those lines.
An obvious example is omitting NULL pointer checks. Passing a NULL pointer to certain system library functions is UB even if it would ordinarily be expected to be reasonable, e.g. memset(NULL, 0, 0), so some compilers will see that you passed a pointer to memset, and passing a NULL pointer to memset is UB, therefore it can omit a subsequent NULL pointer check guarding a call to something else when the something else isn't going to behave reasonably given a NULL pointer.
This is an insane footgun, but it also allows the compiler to omit a runtime NULL pointer check, which makes the program faster, so people who care most about performance lobby to keep it.
They do! Here's a reference. https://en.wikipedia.org/wiki/Slamfire
Another common way to shoot yourself in the foot is a gun which will go off if you drop it. An example of a gun where early models were especially susceptible is the Lanchester: https://en.wikipedia.org/wiki/Lanchester_submachine_gun
It's an idiom. It's not supposed to be entirely logically consistent. It means what it means because people have decided that it means what it means. Your objections aren't really relevant.
"Footgun" is I think a fairly recent addition to the English lexicon, but it's based on the centuries-old "to shoot yourself in the foot". It seems silly to argue with centuries of English idiomatic usage; no one, by definition, is going to win an argument against that.
It is any trap in something technical that is likely to cause problems from perceived normal use.
Compare to related terms: "Pit of failure", "Turing tarpit", and "Pit of success".
> 1972 - Dennis Ritchie invents a powerful gun that shoots both forward and backward simultaneously. Not satisfied with the number of deaths and permanent maimings from that invention he invents C and Unix.
Some of us learn to lean to the side right before pulling the trigger...
http://james-iry.blogspot.com/2009/05/brief-incomplete-and-m...
I think you're being a bit too literal. It's not an analogy at all, and this has nothing to do with firearms best practices. If we were to define a footgun as "a gun that is only capable of shooting you in the foot" (or perhaps more broadly usefully, "a gun that in theory can be useful, but it is nearly impossibly difficult to make it do anything other than shoot you in the foot"), then the entire point of using the term is to describe something that has no useful, logical purpose, and is unsafe to use, even as designed.
Being "given enough rope to hang yourself" is indeed another good idiom to use for things like this, but the implication is different, I think: when you're given enough rope to hang yourself, the outcome is still very much in your hands. You can intentionally or unintentionally use that rope to hang yourself, or you can be reasonably expected to use that rope in another way that would turn out to be safe or useful.
"Footgun", by contrast, is used to describe something that has no (or negligible) safe uses. Maybe the original intent behind the design of what's being described that way was to have safe uses, but ultimately those safe uses never really panned out, or were so dwarfed by the unsafe uses that the safe use isn't worth the thing existing in the first place. But, unfortunately, there are some people -- maybe only 0.01% of people -- who are able use it safely, and critically depend on that safe use, so we can't completely destroy all these footguns and save everyone else from the error of their ways. And unfortunately most everyone else sees these 0.01% of uses, and believes they are so useful, so efficient, so brilliant, they want to try it too... but in their hubris they end up shooting themselves in the foot, like most others before them.
If you are fortunate enough for your domain to have good IO libraries then there is a chance you can do everything the "Modern" way avoid a lot of the headache and avoid most of the footguns entirely. That maturity and omni-pattern availability is potent, but all that power does come with the possibility of mistakes or surprises.
Compare to newer languages where we don't know what might break or might need to do something the language omits as part of its paradigm. I have done a ton of Ruby projects and about half the time we need more performance so I need to bust out C to rewrite a hotspot in a performance sensitive way. Or sometimes you just really want a loop not a functional stream enumerator like is the default in Ruby. For a new language, Theo tried the 1 billion row challenge in Gleam and the underlying file IO was so slow the language implementers had to step in.
This is an engineering and a business choice. There are reasons to avoid C++ and footguns, like any risk, are among them. These aren't risks without mitigation, but that mitigation has a cost. Just like newer languages have reasons not to use them. A team needs to pick the tools with risks and other cons they can handle and the pros that help them solve their problem.
The problem is that your definition of risk may not be the same as others', and so there isn't always agreement on what is ok and not ok to do. And regardless, humans are notoriously bad at risk assessment.
> This is really well explored space in C++. Just using value semantics, shared_ptr, and RAII instead of naked news and reckless mallocs would improve several "old" codebase I have worked in. Maybe people shouldn't be reaching for const_cast so often, and similar.
Right, and all that is exactly the point: all of that stuff is in wide use out there, and I suspect not just in "old" code bases. So there's still not consensus on what's safe to use and what's too risky.
And regardless, I have enough to think about when I'm building something. Remembering the rules of what language features and APIs I should and shouldn't use is added noise that I don't want. Having to make quick risk assessments about particular constructs is not something I want to be doing. I'd rather just write in a safer language, and the compiler will error out if I do something that would otherwise be too risky. And as a bonus, other people are making those risk assessments up-front for me, people in a much better position than I am to do so in the first place, people who understand the consequences and trade offs better than I do.
I really like this value proposition: "if the compiler successfully compiles the code, there will be no buffer overruns or use-after-free bugs in it" (certainly there's the possibility of compiler bugs, but that's the only vector for failures here). For C++, at best, we can only say, "if you use only a particular subset of the language and standard library that the compiler will not define or enforce for you (find a third party definition or define it yourself, and then be very very careful when coding that you adhere to it, without anyone or anything checking your work), then you probably won't have any buffer overruns or use-after-free bugs." To me, that's almost worse than useless; even if I find a C++-subset definition that I think is reasonable, I'm still not really protected, because I still have to perfectly adhere to it. And even if I do, I'm still at the mercy of that subset definition being "correct".
Choosing no to decide you still have made a choice.
This is “I use the language I always use because I always use it”, and not actually a praise or C++ specifically.
In my 21 years coding professionally, I will settle on C++ or Ruby for most problems depending on context. Ruby for quick, dirty, and "now!", while I use C++ for Long lasting, performance, strongly typed, and compatible things. Those aren't perfect categories and there are reasons to pick other tools, but Choosing C++ after a long career does mean something more than "I am comfortable with this".
Does it?
But if you’re going to behave like a child I’ll skip the rest. Enjoy!
(I'm sure it is.)
"Is the style guide they use for internal projects the same as this style guide that they have published externally?"
"could you clarify which ones you're talking about?"
"Is the internal style guide you described the same as this one I found in google's account on github?"
"oh, I see what you mean"
will you send your second, or shall we simply pistols-at-dawn?
Consider that the style guide that AirBnB uses for internal projects is not the same as the style guide they publish externally, and you can sympathize with why the distinction matters :P.
Whereas the respondent said this:
> Google has a “style guide” for internal c++ code
This is a style guide for definitely-internal c++ code, with the internality of the style guide itself unspecified. I'm not sure what the effect of the scare quotes around "style guide" is meant to be, just that it doesn't move the internal part next to the style guide part.
Putting the whole thing in quotes, rather than just "internal", questions whether the guide you found is the guide referred to, rather than the internal nature of the style guide itself, which the quoted sentence takes no position on.
This has been your daily dose of Hacker News style guides for discussing style guides.
The fact that you put "internal" in quotes suggested to me a mild level of sarcasm or disbelief, i.e, I read your message as "You mean this style guide, published on the internet, for all to see? Clearly that's not 'internal'!"
Either way, to me, "internal style guide" (regardless of any quotes placed around any word) means "style guide that is internal" (that is, the style guide itself is private or unpublished).
But the person you were replying to called it a "style guide for internal c++ code": that word ordering makes it clear that "internal" is describing "c++ projects", and that the internal/external (or unpublished/published or private/public) status of the style guide itself is not being talked about at all.
(As an aside, if the commenter upthread had instead said "internal style guide for c++ code", that could have also meant the same thing, but would have been ambiguous, as it wouldn't have been clear if "internal" was describing "style guide" or "c++ code", or both, even. But "style guide for internal c++ code" is about as unambiguous as you can get.)
what noun cluster would you use for the style guide that google uses internally? Their internal style guide is perfectly accurate. If they publish it externally, does make it no longer their internal style guide? Nope. Would it make somebody exploring the topic wish to add the clarification "oh, their internal and external style guides are one and the same"? Yes.
None of that conflicts with what I wrote, but it does conflict with what you wrote.
Uhm, no. I think you've got that completely backwards. Agree entirely with what they said, not at all with your interpretation. I think you're flat-out wrong here.
and btw, the Hesperus/Phosphorus reference I made many commments ago refers to a famous example of these exact questions in the Philosophy of Language. Read up on it and you may begin to know as much about this as I did before we started.
Like I said, I never wrote c++ there so I’m quite likely misremembering details, but IIRC the published style guide linked to upthread is more or less a snapshot of the previously-unpublished style guide used internally for internal code. It may have some omissions and it’s quite likely out of date.
I’m just amused that this discussion about semantics reminds me so much of the gif linked from https://news.ycombinator.com/item?id=40882247
Almost every part of C++ creeps into almost every other part, and C was already complex enough... and let's just ignore that C++ is not completely compatible with C.
I think I found it via a stackexchange answer about how the "Wiring" language for Arduino sketches differs from regular c++. In Wiring, it's mostly things like no rtti, no virtual methods not resolvable at compile time, no exceptions, unsafe-math, permissive casting, pre-build system concatenates all .ino files into one .cpp file, very limited libraries, and some default includes.
From a skim: <chrono>, <filesystem>, <thread> (along with other concurrency libraries like <mutex> and <future>) are the main ones.
As far as language features that we ban, it's notable that we ban exceptions. Rvalue references are generally discouraged outside of well-trod paths (e.g. move constructors). We ban runtime type information (dynamic_cast, typeid, etc). There are some newer features in C++20 that we haven't accepted yet (modules, ranges, coroutines), due to lack of tooling, concerns about performance, and more.
Sometimes these bans get reversed (there was a long-standing ban on mutable reference parameters that was overturned after many years of discussion).
One of the key points we try to emphasize is that readability is primarily about reading other people's code, and so we provide well-trod paths that try to avoid pitfalls and generally surprising behavior (e.g. this article's focus on initialization? https://abseil.io/tips/88). There is value in moving closer to Python's "There should be one-- and preferably only one --obvious way to do it."
We assert that one of the goals of the style guide (the second section, after Background) is that rules should pull their own weight; we explicitly cite this as a reason why we don't ban goto. I imagine this is also why there isn't an explicit ban on alternative operator representations (e.g. writing `if (foo and bar) <% baz(); %>`).
I don't think I agree that every rule pulls its own weight -- I like to cite that I copy-pasted the internal version of the style guide into a Google doc earlier this year to see how long it was... and it clocked in at over 100 pages. But maybe that's an indicator of how complex C++ is, where we have to make the tradeoff between being concise, being precise, and providing sufficient context.
Not disagreeing that C++ is awful in a lot of ways and super difficult though. But I still weirdly like it, personally. I find it a fun challenge/puzzle to work with.
And if the basis of c++ is c with classes, you’re horribly incorrect. C is a small language, easy to understand and hard to master.
Rust is fucking miserable to understand, and as such, hard to master.
Why are you being pedantic?
> Why are you being pedantic?
I'm not. I'm making a real point: Rust is much simpler and easier than C++, so the spirit of Bjarne's quote, which is that for a language to become popular it necessarily has to have as many drawbacks as C++, is wrong.
I mean, a standard (and stupid IMO) interview question is rate your C++ expertise from 1-10, and if you answer more than about 5-6 you get bounced for lying or not recognizing your limitations, while they gleefully point out Stroustrup wouldn't answer 9-10.
Bolted on, very terrible OO that should never be touched
Some of the most batshit insane ideas of encapsulation anyone has ever seen
Some of the most batshit insane return rules anyone has ever seen
Encouraged inconsistency from the language in the form of functions like “Len” that are added because sometimes someone feels it reads better?
Encouraged unwinding exceptions as regular flow control (lol. Yikes)
It is nearly universally agreed that Python has significant code management issues as code bases scale
This is all ignoring debates of fundamental typing issues. Personally, I hate what Python does, but some people seem to prefer it.
Let us not pretend Python doesn’t have some language problems on top of its tooling problems.
That's the worst part. But you have to agree it's the best for throwing small sized glue code around.
> Sure, Python is slow, packaging/versioning is painful
Those are complaints. That is evidence that people complain about Python. You just did it yourself.
But maybe your complaints about C++ are an order of magnitude more plentiful than for Python. And maybe quite a few of your C++ complaints are about much worse things. But that's not the point: they are all complaints.
And that's the problem with the Stroustrup quote: he's implicitly saying that all complaints are created equal, and there's no difference between having 10 complaints or 10,000 complaints (where 3 of the first are major, and 5,000 of the second are major).
It's used, as the GP points out, to shut down legitimate complaints. "Oh, you don't like $REALLY_BIG_HORRIBLE_ISSUE with my language? Psh, whatever, people complain about all languages, I dare you to find another language that you won't find something to complain about." Not the point! Is $REALLY_BIG_HORRIBLE_ISSUE a problem or not? If not, actually explain and justify, with specific arguments, why you don't think it's a problem. And if you do agree it's a problem, stop deflecting attention, admit that's it's a problem, and try to find a solution!
In any case, I think the primary goal of any programming language is to get out of your way and let you tackle more interesting problems related to the problem domain that led you to start writing a program in the first place.
You're basically saying the language gets in the way of solving your problem :)